Coverage for common/constants.py: 87%

159 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-08-27 10:34 -0500

1""" 

2crate_anon/common/constants.py 

3 

4=============================================================================== 

5 

6 Copyright (C) 2015, University of Cambridge, Department of Psychiatry. 

7 Created by Rudolf Cardinal (rnc1001@cam.ac.uk). 

8 

9 This file is part of CRATE. 

10 

11 CRATE is free software: you can redistribute it and/or modify 

12 it under the terms of the GNU General Public License as published by 

13 the Free Software Foundation, either version 3 of the License, or 

14 (at your option) any later version. 

15 

16 CRATE is distributed in the hope that it will be useful, 

17 but WITHOUT ANY WARRANTY; without even the implied warranty of 

18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 

19 GNU General Public License for more details. 

20 

21 You should have received a copy of the GNU General Public License 

22 along with CRATE. If not, see <https://www.gnu.org/licenses/>. 

23 

24=============================================================================== 

25 

26**Constants used throughout CRATE.** 

27 

28""" 

29 

30import os 

31 

32 

33# ============================================================================= 

34# Plain constants 

35# ============================================================================= 

36 

37CRATE_DOCS_URL = "https://crateanon.readthedocs.io/" 

38 

39DEMO_NLP_INPUT_TERMINATOR = "STOP" 

40DEMO_NLP_OUTPUT_TERMINATOR = "END_OF_NLP_OUTPUT_RECORD" 

41 

42EXIT_FAILURE = 1 

43EXIT_SUCCESS = 0 

44 

45JSON_INDENT = 4 

46 

47JSON_SEPARATORS_COMPACT = (",", ":") 

48# ... see https://docs.python.org/3/library/json.html 

49 

50LOWER_CASE_STRINGS_MEANING_TRUE = ["true", "1", "t", "y", "yes"] 

51 

52# Is this program running on readthedocs.org? 

53ON_READTHEDOCS = os.environ.get("READTHEDOCS") == "True" 

54 

55NoneType = type(None) # for isinstance, sometimes 

56 

57 

58# ============================================================================= 

59# Constant creation 

60# ============================================================================= 

61 

62 

63def mebibytes(mb: int) -> int: 

64 """ 

65 Takes a parameter in mebibytes (1024^2 bytes) and returns the number of 

66 bytes. 

67 """ 

68 return mb * 1024 * 1024 

69 

70 

71# ============================================================================= 

72# Directories within CRATE 

73# ============================================================================= 

74 

75 

76class CratePath: 

77 """ 

78 Directories within the CRATE Python package. 

79 """ 

80 

81 CRATE_ANON_DIR = os.path.abspath( 

82 os.path.join( 

83 os.path.dirname( 

84 os.path.abspath(__file__) 

85 ), # this directory, common 

86 os.pardir, # parent, crate_anon 

87 ) 

88 ) 

89 CRATEWEB_DIR = os.path.join(CRATE_ANON_DIR, "crateweb") 

90 STATIC_SRC_DIR = os.path.join(CRATEWEB_DIR, "static") 

91 NLP_MANAGER_DIR = os.path.join(CRATE_ANON_DIR, "nlp_manager") 

92 JAVA_CLASSES_DIR = os.path.join(NLP_MANAGER_DIR, "compiled_nlp_classes") 

93 NLPRP_DIR = os.path.join(CRATE_ANON_DIR, "nlprp") 

94 

95 

96# ============================================================================= 

97# DockerConstants 

98# ============================================================================= 

99 

100 

101class DockerConstants: 

102 """ 

103 Constants for the Docker environment. 

104 """ 

105 

106 # Directories 

107 DOCKER_CRATE_ROOT_DIR = "/crate" 

108 CONFIG_DIR = os.path.join(DOCKER_CRATE_ROOT_DIR, "cfg") 

109 FILES_DIR = os.path.join(DOCKER_CRATE_ROOT_DIR, "files") 

110 STATIC_DIR = os.path.join(DOCKER_CRATE_ROOT_DIR, "static") 

111 TMP_DIR = os.path.join(DOCKER_CRATE_ROOT_DIR, "tmp") 

112 VENV_DIR = os.path.join(DOCKER_CRATE_ROOT_DIR, "venv") 

113 

114 HOST = "0.0.0.0" 

115 # ... not "localhost" or "127.0.0.1"; see 

116 # https://nickjanetakis.com/blog/docker-tip-54-fixing-connection-reset-by-peer-or-similar-errors # noqa: E501 

117 

118 

119# ============================================================================= 

120# Environment variables 

121# ============================================================================= 

122 

123 

124class EnvVar: 

125 """ 

126 Environment variable names. 

127 """ 

128 

129 CRATE_GATE_PLUGIN_FILE = "CRATE_GATE_PLUGIN_FILE" 

130 # ... environment variable whose presence shows that we are generating 

131 # docs. 

132 GATE_HOME = "GATE_HOME" 

133 GENERATING_CRATE_DOCS = "GENERATING_CRATE_DOCS" 

134 JAVA_HOME = "JAVA_HOME" 

135 KCL_KCONNECT_DIR = "KCL_KCONNECT_DIR" 

136 KCL_LEWY_BODY_DIAGNOSIS_DIR = "KCL_LEWY_BODY_DIAGNOSIS_DIR" 

137 KCL_PHARMACOTHERAPY_DIR = "KCL_PHARMACOTHERAPY_DIR" 

138 MEDEX_HOME = "MEDEX_HOME" 

139 PATH = "PATH" 

140 RUNNING_TESTS = "RUNNING_TESTS" 

141 RUN_WITHOUT_CONFIG = "CRATE_RUN_WITHOUT_LOCAL_SETTINGS" 

142 

143 

144# ============================================================================= 

145# CRATE top-level commands 

146# ============================================================================= 

147 

148 

149class CrateCommand: 

150 """ 

151 Top-level commands within CRATE, recorded here to ensure consistency. 

152 However, see also crate/installer/installer.py, which duplicates some 

153 (because the full Python environment is not then available). 

154 """ 

155 

156 # Preprocessing 

157 AUTOIMPORTDB = "crate_autoimport_db" 

158 FETCH_WORDLISTS = "crate_fetch_wordlists" 

159 POSTCODES = "crate_postcodes" 

160 PREPROCESS_PCMIS = "crate_preprocess_pcmis" 

161 PREPROCESS_RIO = "crate_preprocess_rio" 

162 PREPROCESS_SYSTMONE = "crate_preprocess_systmone" 

163 

164 # Linkage 

165 BULK_HASH = "crate_bulk_hash" 

166 FUZZY_ID_MATCH = "crate_fuzzy_id_match" 

167 

168 # Anonymisation 

169 ANON_CHECK_TEXT_EXTRACTOR = "crate_anon_check_text_extractor" 

170 ANON_DEMO_CONFIG = "crate_anon_demo_config" 

171 ANON_DRAFT_DD = "crate_anon_draft_dd" 

172 ANON_SHOW_COUNTS = "crate_anon_show_counts" 

173 ANON_SUMMARIZE_DD = "crate_anon_summarize_dd" 

174 ANONYMISE = "crate_anonymise" 

175 ANONYMISE_MULTIPROCESS = "crate_anonymise_multiprocess" 

176 MAKE_DEMO_DATABASE = "crate_make_demo_database" 

177 RESEARCHER_REPORT = "crate_researcher_report" 

178 SUBSET_DB = "crate_subset_db" 

179 TEST_ANONYMISATION = "crate_test_anonymisation" 

180 TEST_EXTRACT_TEXT = "crate_test_extract_text" 

181 

182 # NLP 

183 NLP = "crate_nlp" 

184 NLP_BUILD_GATE_JAVA_INTERFACE = "crate_nlp_build_gate_java_interface" 

185 NLP_BUILD_MEDEX_ITSELF = "crate_nlp_build_medex_itself" 

186 NLP_BUILD_MEDEX_JAVA_INTERFACE = "crate_nlp_build_medex_java_interface" 

187 NLP_MULTIPROCESS = "crate_nlp_multiprocess" 

188 NLP_PREPARE_YMLS_FOR_BIOYODIE = "crate_nlp_prepare_ymls_for_bioyodie" 

189 NLP_WRITE_GATE_AUTO_INSTALL_XML = "crate_nlp_write_gate_auto_install_xml" 

190 RUN_CRATE_NLP_DEMO = "crate_run_crate_nlp_demo" 

191 RUN_GATE_ANNIE_DEMO = "crate_run_gate_annie_demo" 

192 RUN_GATE_KCL_KCONNECT_DEMO = "crate_run_gate_kcl_kconnect_demo" 

193 RUN_GATE_KCL_LEWY_DEMO = "crate_run_gate_kcl_lewy_demo" 

194 RUN_GATE_KCL_PHARMACOTHERAPY_DEMO = ( 

195 "crate_run_gate_kcl_pharmacotherapy_demo" 

196 ) 

197 SHOW_CRATE_GATE_PIPELINE_OPTIONS = "crate_show_crate_gate_pipeline_options" 

198 SHOW_CRATE_MEDEX_PIPELINE_OPTIONS = ( 

199 "crate_show_crate_medex_pipeline_options" 

200 ) 

201 

202 # Web site 

203 CELERY_STATUS = "crate_celery_status" 

204 DJANGO_MANAGE = "crate_django_manage" 

205 EMAIL_RDBM = "crate_email_rdbm" 

206 GENERATE_NEW_DJANGO_SECRET_KEY = "crate_generate_new_django_secret_key" 

207 LAUNCH_CELERY = "crate_launch_celery" 

208 LAUNCH_FLOWER = "crate_launch_flower" 

209 PRINT_DEMO_CRATEWEB_CONFIG = "crate_print_demo_crateweb_config" 

210 WINDOWS_SERVICE = "crate_windows_service" 

211 LAUNCH_CHERRYPY_SERVER = "crate_launch_cherrypy_server" 

212 LAUNCH_DJANGO_SERVER = "crate_launch_django_server" 

213 

214 # NLP web server 

215 NLP_WEBSERVER_GENERATE_ENCRYPTION_KEY = ( 

216 "crate_nlp_webserver_generate_encryption_key" 

217 ) 

218 NLP_WEBSERVER_INITIALIZE_DB = "crate_nlp_webserver_initialize_db" 

219 NLP_WEBSERVER_LAUNCH_CELERY = "crate_nlp_webserver_launch_celery" 

220 NLP_WEBSERVER_LAUNCH_FLOWER = "crate_nlp_webserver_launch_flower" 

221 NLP_WEBSERVER_LAUNCH_GUNICORN = "crate_nlp_webserver_launch_gunicorn" 

222 NLP_WEBSERVER_MANAGE_USERS = "crate_nlp_webserver_manage_users" 

223 NLP_WEBSERVER_PRINT_DEMO = "crate_nlp_webserver_print_demo" 

224 NLP_WEBSERVER_PSERVE = "crate_nlp_webserver_pserve" 

225 

226 # Testing 

227 TEST_DATABASE_CONNECTION = "crate_test_database_connection" 

228 

229 

230# ============================================================================= 

231# HelpUrl 

232# ============================================================================= 

233 

234 

235class HelpUrl: 

236 """ 

237 Makes help URLs, for an approximation to context-sensitive help within the 

238 web site. 

239 

240 Note that in Django's template syntax, 

241 

242 .. code-block:: none 

243 

244 {{ HelpUrl.main }} 

245 

246 gets translated to 

247 

248 .. code-block:: python 

249 

250 HelpUrl.main() 

251 

252 i.e. further brackets are unnecessary (and an error). See: 

253 

254 - https://docs.djangoproject.com/en/2.2/topics/templates/#variables 

255 

256 ... "If a variable resolves to a callable, the template system will call it 

257 with no arguments and use its result instead of the callable." 

258 

259 """ 

260 

261 @staticmethod 

262 def make_url( 

263 location: str, language: str = "en", version: str = "latest" 

264 ) -> str: 

265 """ 

266 Make a CRATE help URL. 

267 

268 Args: 

269 location: location within docs 

270 language: language (default ``en``) 

271 version: version (default ``latest``) 

272 """ 

273 return f"{CRATE_DOCS_URL}{language}/{version}/{location}" 

274 

275 @classmethod 

276 def main(cls) -> str: 

277 return cls.make_url("") 

278 

279 @classmethod 

280 def website(cls) -> str: 

281 return cls.make_url("website_using/index.html") 

282 

283 @classmethod 

284 def find_text_anywhere(cls) -> str: 

285 return cls.make_url( 

286 "website_using/clinician_privileged.html#clinician-privileged-find-text-anywhere" # noqa: E501 

287 ) 

288 

289 @classmethod 

290 def clinician_lookup_rid(cls) -> str: 

291 return cls.make_url( 

292 "website_using/clinician_privileged.html#look-up-research-id-from-patient-id" # noqa: E501 

293 ) 

294 

295 @classmethod 

296 def clinician_submit_contact_request(cls) -> str: 

297 return cls.make_url( 

298 "website_using/clinician_privileged.html#submit-patient-contact-request" # noqa: E501 

299 ) 

300 

301 @classmethod 

302 def querybuilder(cls) -> str: 

303 return cls.make_url( 

304 "website_using/research_queries.html#query-builder" 

305 ) 

306 

307 @classmethod 

308 def sql(cls) -> str: 

309 return cls.make_url( 

310 "website_using/research_queries.html#research-query-sql" 

311 ) 

312 

313 @classmethod 

314 def highlighting(cls) -> str: 

315 return cls.make_url( 

316 "website_using/research_queries.html#highlighting-text-in-results" 

317 ) 

318 

319 @classmethod 

320 def results(cls) -> str: 

321 return cls.make_url( 

322 "website_using/research_queries.html#results-table-view" 

323 ) 

324 

325 @classmethod 

326 def patient_explorer(cls) -> str: 

327 return cls.make_url("website_using/patient_explorer.html") 

328 

329 @classmethod 

330 def sqlhelper_find_text_anywhere(cls) -> str: 

331 return cls.make_url( 

332 "website_using/sql_helpers.html#find-text-anywhere" 

333 ) 

334 

335 @classmethod 

336 def sqlhelper_find_drugs_anywhere(cls) -> str: 

337 return cls.make_url( 

338 "website_using/sql_helpers.html#find-drugs-of-a-given-type-anywhere" # noqa: E501 

339 ) 

340 

341 @classmethod 

342 def sitewide_queries(cls) -> str: 

343 return cls.make_url("website_using/site_queries.html") 

344 

345 @classmethod 

346 def research_db_structure(cls) -> str: 

347 return cls.make_url("website_using/database_structure.html") 

348 

349 @classmethod 

350 def submit_contact_request(cls) -> str: 

351 return cls.make_url( 

352 "website_using/contact_patients.html#submit-a-contact-request" 

353 ) 

354 

355 @classmethod 

356 def rdbm(cls) -> str: 

357 return cls.make_url("website_using/rdbm_admin.html") 

358 

359 @classmethod 

360 def developer(cls) -> str: 

361 return cls.make_url("website_using/developer_admin.html") 

362 

363 @classmethod 

364 def user_settings(cls) -> str: 

365 return cls.make_url( 

366 "website_using/clinician_researcher_overview.html#your-settings" 

367 ) 

368 

369 @classmethod 

370 def about_crate(cls) -> str: 

371 return cls.make_url( 

372 "website_using/clinician_researcher_overview.html#about-crate" 

373 ) 

374 

375 @classmethod 

376 def archive(cls) -> str: 

377 return cls.make_url("website_using/archive.html") 

378 

379 

380# ============================================================================= 

381# More plain constants 

382# ============================================================================= 

383 

384# Will we run without a config file? 

385RUNNING_WITHOUT_CONFIG = ON_READTHEDOCS or ( 

386 EnvVar.RUN_WITHOUT_CONFIG in os.environ 

387 and os.environ[EnvVar.RUN_WITHOUT_CONFIG].lower() 

388 in LOWER_CASE_STRINGS_MEANING_TRUE 

389)