endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.9__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of endoreg-db might be problematic. Click here for more details.

Files changed (503) hide show
  1. endoreg_db/authz/auth.py +74 -0
  2. endoreg_db/authz/backends.py +168 -0
  3. endoreg_db/authz/management/commands/list_routes.py +18 -0
  4. endoreg_db/authz/middleware.py +83 -0
  5. endoreg_db/authz/permissions.py +127 -0
  6. endoreg_db/authz/policy.py +218 -0
  7. endoreg_db/authz/views_auth.py +66 -0
  8. endoreg_db/config/env.py +13 -8
  9. endoreg_db/data/__init__.py +2 -11
  10. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +3 -3
  11. endoreg_db/data/event_classification/data.yaml +4 -0
  12. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  13. endoreg_db/data/examination/examinations/data.yaml +114 -14
  14. endoreg_db/data/examination/time-type/data.yaml +0 -3
  15. endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
  16. endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
  17. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
  18. endoreg_db/data/finding/00_generic.yaml +35 -0
  19. endoreg_db/data/finding/00_generic_complication.yaml +9 -0
  20. endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
  21. endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
  22. endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
  23. endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
  24. endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
  25. endoreg_db/data/finding_classification/00_generic.yaml +44 -0
  26. endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
  27. endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
  28. endoreg_db/data/finding_classification/02_colonoscopy_baseline.yaml +83 -0
  29. endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
  30. endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
  31. endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
  32. endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
  33. endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
  34. endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
  35. endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
  36. endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
  37. endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
  38. endoreg_db/data/finding_classification_choice/{colonoscopy_not_complete_reason.yaml → 02_colonoscopy_generic.yaml} +1 -1
  39. endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
  40. endoreg_db/data/finding_classification_choice/{colonoscopy_location.yaml → 02_colonoscopy_location.yaml} +23 -4
  41. endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
  42. endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
  43. endoreg_db/data/finding_classification_choice/{colon_lesion_paris.yaml → 02_colonoscopy_polyp_morphology.yaml} +26 -8
  44. endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
  45. endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
  46. endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
  47. endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
  48. endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
  49. endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
  50. endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
  51. endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
  52. endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
  53. endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
  54. endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
  55. endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
  56. endoreg_db/data/finding_type/data.yaml +8 -12
  57. endoreg_db/data/requirement/01_patient_data.yaml +93 -0
  58. endoreg_db/data/requirement/old/colon_polyp_intervention.yaml +49 -0
  59. endoreg_db/data/requirement/old/coloreg_colon_polyp.yaml +49 -0
  60. endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
  61. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +29 -12
  62. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  63. endoreg_db/data/requirement_set/{endoscopy_bleeding_risk.yaml → 02_endoscopy_bleeding_risk.yaml} +0 -6
  64. endoreg_db/data/requirement_set/90_coloreg.yaml +190 -0
  65. endoreg_db/data/requirement_set/_old_ +109 -0
  66. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  67. endoreg_db/data/setup_config.yaml +4 -4
  68. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  69. endoreg_db/exceptions.py +4 -2
  70. endoreg_db/forms/examination_form.py +1 -1
  71. endoreg_db/helpers/data_loader.py +125 -53
  72. endoreg_db/helpers/default_objects.py +116 -81
  73. endoreg_db/import_files/__init__.py +27 -0
  74. endoreg_db/import_files/context/__init__.py +7 -0
  75. endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
  76. endoreg_db/import_files/context/ensure_center.py +17 -0
  77. endoreg_db/import_files/context/file_lock.py +66 -0
  78. endoreg_db/import_files/context/import_context.py +43 -0
  79. endoreg_db/import_files/context/validate_directories.py +56 -0
  80. endoreg_db/import_files/file_storage/__init__.py +15 -0
  81. endoreg_db/import_files/file_storage/create_report_file.py +76 -0
  82. endoreg_db/import_files/file_storage/create_video_file.py +75 -0
  83. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
  84. endoreg_db/import_files/file_storage/state_management.py +400 -0
  85. endoreg_db/import_files/file_storage/storage.py +36 -0
  86. endoreg_db/import_files/import_service.md +26 -0
  87. endoreg_db/import_files/processing/__init__.py +11 -0
  88. endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
  89. endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
  90. endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
  91. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
  92. endoreg_db/import_files/pseudonymization/fake.py +52 -0
  93. endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
  94. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
  95. endoreg_db/import_files/report_import_service.py +141 -0
  96. endoreg_db/import_files/video_import_service.py +150 -0
  97. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  98. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  99. endoreg_db/management/commands/import_report.py +130 -65
  100. endoreg_db/management/commands/import_video.py +9 -10
  101. endoreg_db/management/commands/import_video_with_classification.py +2 -2
  102. endoreg_db/management/commands/list_routes.py +18 -0
  103. endoreg_db/management/commands/load_ai_model_data.py +5 -5
  104. endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
  105. endoreg_db/management/commands/load_base_db_data.py +5 -134
  106. endoreg_db/management/commands/load_center_data.py +12 -12
  107. endoreg_db/management/commands/load_contraindication_data.py +14 -16
  108. endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
  109. endoreg_db/management/commands/load_disease_classification_data.py +15 -18
  110. endoreg_db/management/commands/load_disease_data.py +25 -28
  111. endoreg_db/management/commands/load_endoscope_data.py +20 -27
  112. endoreg_db/management/commands/load_event_data.py +14 -16
  113. endoreg_db/management/commands/load_examination_data.py +31 -44
  114. endoreg_db/management/commands/load_examination_indication_data.py +20 -21
  115. endoreg_db/management/commands/load_finding_data.py +52 -80
  116. endoreg_db/management/commands/load_information_source.py +21 -23
  117. endoreg_db/management/commands/load_lab_value_data.py +17 -26
  118. endoreg_db/management/commands/load_medication_data.py +13 -12
  119. endoreg_db/management/commands/load_organ_data.py +15 -19
  120. endoreg_db/management/commands/load_pdf_type_data.py +19 -18
  121. endoreg_db/management/commands/load_profession_data.py +14 -17
  122. endoreg_db/management/commands/load_qualification_data.py +20 -23
  123. endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
  124. endoreg_db/management/commands/load_requirement_data.py +62 -39
  125. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  126. endoreg_db/management/commands/load_risk_data.py +7 -6
  127. endoreg_db/management/commands/load_shift_data.py +20 -23
  128. endoreg_db/management/commands/load_tag_data.py +8 -11
  129. endoreg_db/management/commands/load_unit_data.py +17 -19
  130. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  131. endoreg_db/management/commands/start_filewatcher.py +46 -37
  132. endoreg_db/management/commands/storage_management.py +271 -203
  133. endoreg_db/management/commands/validate_video_files.py +1 -5
  134. endoreg_db/migrations/0001_initial.py +297 -250
  135. endoreg_db/models/__init__.py +78 -123
  136. endoreg_db/models/administration/__init__.py +21 -42
  137. endoreg_db/models/administration/ai/active_model.py +2 -2
  138. endoreg_db/models/administration/ai/ai_model.py +7 -6
  139. endoreg_db/models/administration/case/__init__.py +1 -15
  140. endoreg_db/models/administration/case/case.py +3 -3
  141. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  142. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  143. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  144. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  145. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  146. endoreg_db/models/administration/center/center.py +33 -19
  147. endoreg_db/models/administration/center/center_product.py +12 -9
  148. endoreg_db/models/administration/center/center_resource.py +25 -19
  149. endoreg_db/models/administration/center/center_shift.py +21 -17
  150. endoreg_db/models/administration/center/center_waste.py +16 -8
  151. endoreg_db/models/administration/person/__init__.py +2 -0
  152. endoreg_db/models/administration/person/employee/employee.py +10 -5
  153. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  154. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  155. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  156. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  157. endoreg_db/models/administration/person/patient/patient.py +129 -100
  158. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  159. endoreg_db/models/administration/person/person.py +4 -0
  160. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  161. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  162. endoreg_db/models/administration/product/product.py +20 -15
  163. endoreg_db/models/administration/product/product_material.py +17 -18
  164. endoreg_db/models/administration/product/product_weight.py +12 -8
  165. endoreg_db/models/administration/product/reference_product.py +23 -55
  166. endoreg_db/models/administration/qualification/qualification.py +7 -3
  167. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  168. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  169. endoreg_db/models/administration/shift/shift.py +16 -12
  170. endoreg_db/models/administration/shift/shift_type.py +23 -31
  171. endoreg_db/models/label/__init__.py +8 -9
  172. endoreg_db/models/label/annotation/image_classification.py +10 -9
  173. endoreg_db/models/label/annotation/video_segmentation_annotation.py +23 -28
  174. endoreg_db/models/label/label.py +15 -15
  175. endoreg_db/models/label/label_set.py +19 -6
  176. endoreg_db/models/label/label_type.py +1 -1
  177. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  178. endoreg_db/models/label/label_video_segment/label_video_segment.py +98 -102
  179. endoreg_db/models/label/video_segmentation_label.py +4 -0
  180. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  181. endoreg_db/models/media/frame/frame.py +22 -22
  182. endoreg_db/models/media/pdf/raw_pdf.py +194 -194
  183. endoreg_db/models/media/pdf/report_file.py +25 -29
  184. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +55 -47
  185. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  186. endoreg_db/models/media/processing_history/__init__.py +5 -0
  187. endoreg_db/models/media/processing_history/processing_history.py +96 -0
  188. endoreg_db/models/media/video/__init__.py +1 -0
  189. endoreg_db/models/media/video/create_from_file.py +139 -77
  190. endoreg_db/models/media/video/pipe_2.py +8 -9
  191. endoreg_db/models/media/video/video_file.py +174 -112
  192. endoreg_db/models/media/video/video_file_ai.py +288 -74
  193. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  194. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  195. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  196. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  197. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  198. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  199. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  200. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  201. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  202. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  203. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  204. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  205. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  206. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  207. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  208. endoreg_db/models/media/video/video_file_io.py +113 -61
  209. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  210. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  211. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  212. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  213. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  214. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  215. endoreg_db/models/media/video/video_file_segments.py +24 -17
  216. endoreg_db/models/media/video/video_metadata.py +19 -35
  217. endoreg_db/models/media/video/video_processing.py +96 -95
  218. endoreg_db/models/medical/contraindication/README.md +1 -0
  219. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  220. endoreg_db/models/medical/disease.py +22 -16
  221. endoreg_db/models/medical/event.py +31 -18
  222. endoreg_db/models/medical/examination/__init__.py +13 -6
  223. endoreg_db/models/medical/examination/examination.py +39 -20
  224. endoreg_db/models/medical/examination/examination_indication.py +30 -95
  225. endoreg_db/models/medical/examination/examination_time.py +23 -8
  226. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  227. endoreg_db/models/medical/examination/examination_type.py +3 -4
  228. endoreg_db/models/medical/finding/finding.py +32 -40
  229. endoreg_db/models/medical/finding/finding_classification.py +42 -72
  230. endoreg_db/models/medical/finding/finding_intervention.py +25 -22
  231. endoreg_db/models/medical/finding/finding_type.py +13 -12
  232. endoreg_db/models/medical/hardware/endoscope.py +26 -26
  233. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  234. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  235. endoreg_db/models/medical/medication/medication.py +22 -10
  236. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  237. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  238. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  239. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  240. endoreg_db/models/medical/organ/__init__.py +15 -12
  241. endoreg_db/models/medical/patient/medication_examples.py +6 -6
  242. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  243. endoreg_db/models/medical/patient/patient_event.py +19 -22
  244. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  245. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  246. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  247. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  248. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  249. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  250. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  251. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  252. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  253. endoreg_db/models/medical/risk/risk.py +7 -6
  254. endoreg_db/models/medical/risk/risk_type.py +8 -5
  255. endoreg_db/models/metadata/model_meta.py +60 -29
  256. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  257. endoreg_db/models/metadata/pdf_meta.py +31 -24
  258. endoreg_db/models/metadata/sensitive_meta.py +105 -85
  259. endoreg_db/models/metadata/sensitive_meta_logic.py +198 -103
  260. endoreg_db/models/metadata/video_meta.py +51 -31
  261. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  262. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  263. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  264. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  265. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  266. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  267. endoreg_db/models/other/emission/emission_factor.py +18 -8
  268. endoreg_db/models/other/gender.py +10 -5
  269. endoreg_db/models/other/information_source.py +50 -29
  270. endoreg_db/models/other/material.py +9 -5
  271. endoreg_db/models/other/resource.py +6 -4
  272. endoreg_db/models/other/tag.py +10 -5
  273. endoreg_db/models/other/transport_route.py +13 -8
  274. endoreg_db/models/other/unit.py +10 -6
  275. endoreg_db/models/other/waste.py +6 -5
  276. endoreg_db/models/report/report.py +6 -0
  277. endoreg_db/models/requirement/requirement.py +329 -361
  278. endoreg_db/models/requirement/requirement_error.py +85 -0
  279. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  280. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  281. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  282. endoreg_db/models/requirement/requirement_operator.py +103 -112
  283. endoreg_db/models/requirement/requirement_set.py +74 -57
  284. endoreg_db/models/state/__init__.py +4 -4
  285. endoreg_db/models/state/abstract.py +2 -2
  286. endoreg_db/models/state/anonymization.py +12 -0
  287. endoreg_db/models/state/audit_ledger.py +49 -51
  288. endoreg_db/models/state/label_video_segment.py +9 -0
  289. endoreg_db/models/state/raw_pdf.py +101 -68
  290. endoreg_db/models/state/sensitive_meta.py +6 -2
  291. endoreg_db/models/state/video.py +110 -90
  292. endoreg_db/models/upload_job.py +35 -34
  293. endoreg_db/models/utils.py +28 -25
  294. endoreg_db/queries/__init__.py +3 -1
  295. endoreg_db/root_urls.py +21 -2
  296. endoreg_db/schemas/examination_evaluation.py +1 -1
  297. endoreg_db/serializers/__init__.py +2 -10
  298. endoreg_db/serializers/anonymization.py +18 -10
  299. endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
  300. endoreg_db/serializers/meta/__init__.py +1 -6
  301. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  302. endoreg_db/serializers/misc/file_overview.py +11 -99
  303. endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
  304. endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
  305. endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
  306. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  307. endoreg_db/serializers/video/segmentation.py +2 -1
  308. endoreg_db/serializers/video/video_file_list.py +65 -34
  309. endoreg_db/serializers/video/video_processing_history.py +20 -5
  310. endoreg_db/services/__old/pdf_import.py +1487 -0
  311. endoreg_db/services/__old/video_import.py +1306 -0
  312. endoreg_db/services/anonymization.py +128 -89
  313. endoreg_db/services/lookup_service.py +65 -52
  314. endoreg_db/services/lookup_store.py +2 -2
  315. endoreg_db/services/pdf_import.py +0 -1382
  316. endoreg_db/services/report_import.py +10 -0
  317. endoreg_db/services/video_import.py +6 -1255
  318. endoreg_db/tasks/upload_tasks.py +79 -70
  319. endoreg_db/tasks/video_ingest.py +8 -4
  320. endoreg_db/urls/__init__.py +5 -32
  321. endoreg_db/urls/ai.py +32 -0
  322. endoreg_db/urls/media.py +121 -83
  323. endoreg_db/urls/root_urls.py +29 -0
  324. endoreg_db/utils/__init__.py +15 -5
  325. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  326. endoreg_db/utils/case_generator/__init__.py +3 -0
  327. endoreg_db/utils/dataloader.py +142 -40
  328. endoreg_db/utils/defaults/set_default_center.py +32 -0
  329. endoreg_db/utils/names.py +22 -16
  330. endoreg_db/utils/paths.py +110 -46
  331. endoreg_db/utils/permissions.py +2 -1
  332. endoreg_db/utils/pipelines/Readme.md +1 -1
  333. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  334. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +655 -0
  335. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
  336. endoreg_db/utils/setup_config.py +8 -5
  337. endoreg_db/utils/storage.py +115 -0
  338. endoreg_db/utils/validate_endo_roi.py +8 -2
  339. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  340. endoreg_db/views/__init__.py +85 -183
  341. endoreg_db/views/ai/__init__.py +8 -0
  342. endoreg_db/views/ai/label.py +155 -0
  343. endoreg_db/views/anonymization/media_management.py +202 -166
  344. endoreg_db/views/anonymization/overview.py +99 -67
  345. endoreg_db/views/anonymization/validate.py +182 -44
  346. endoreg_db/views/media/__init__.py +7 -20
  347. endoreg_db/views/media/pdf_media.py +197 -174
  348. endoreg_db/views/media/sensitive_metadata.py +193 -138
  349. endoreg_db/views/media/video_media.py +89 -82
  350. endoreg_db/views/meta/__init__.py +0 -8
  351. endoreg_db/views/misc/__init__.py +1 -7
  352. endoreg_db/views/misc/upload_views.py +94 -93
  353. endoreg_db/views/patient/patient.py +5 -4
  354. endoreg_db/views/report/__init__.py +5 -7
  355. endoreg_db/views/{pdf → report}/reimport.py +22 -22
  356. endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +46 -39
  357. endoreg_db/views/requirement/evaluate.py +188 -187
  358. endoreg_db/views/requirement/lookup.py +17 -3
  359. endoreg_db/views/requirement/lookup_store.py +22 -90
  360. endoreg_db/views/requirement/requirement_utils.py +89 -0
  361. endoreg_db/views/video/__init__.py +23 -24
  362. endoreg_db/views/video/correction.py +201 -172
  363. endoreg_db/views/video/reimport.py +1 -1
  364. endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +77 -40
  365. endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
  366. endoreg_db/views/video/video_stream.py +7 -8
  367. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +7 -3
  368. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +391 -413
  369. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
  370. endoreg_db/data/finding/anatomy_colon.yaml +0 -128
  371. endoreg_db/data/finding/colonoscopy.yaml +0 -40
  372. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
  373. endoreg_db/data/finding/complication.yaml +0 -16
  374. endoreg_db/data/finding/data.yaml +0 -105
  375. endoreg_db/data/finding/examination_setting.yaml +0 -16
  376. endoreg_db/data/finding/medication_related.yaml +0 -18
  377. endoreg_db/data/finding/outcome.yaml +0 -12
  378. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +0 -95
  379. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
  380. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
  381. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  382. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  383. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -68
  384. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  385. endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -80
  386. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
  387. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
  388. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
  389. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
  390. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
  391. endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
  392. endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
  393. endoreg_db/data/finding_classification/histology_colo.yaml +0 -51
  394. endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
  395. endoreg_db/data/finding_classification/medication_related.yaml +0 -23
  396. endoreg_db/data/finding_classification/visualized.yaml +0 -33
  397. endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  398. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  399. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  400. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  401. endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  402. endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  403. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  404. endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  405. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
  406. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  407. endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
  408. endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
  409. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  410. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
  411. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
  412. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  413. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  414. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
  415. endoreg_db/data/requirement/age.yaml +0 -26
  416. endoreg_db/data/requirement/gender.yaml +0 -25
  417. endoreg_db/management/commands/init_default_ai_model.py +0 -112
  418. endoreg_db/management/commands/reset_celery_schedule.py +0 -9
  419. endoreg_db/management/commands/validate_video.py +0 -204
  420. endoreg_db/migrations/0002_add_video_correction_models.py +0 -52
  421. endoreg_db/migrations/0003_add_center_display_name.py +0 -30
  422. endoreg_db/models/administration/permissions/__init__.py +0 -44
  423. endoreg_db/models/rule/__init__.py +0 -13
  424. endoreg_db/models/rule/rule.py +0 -27
  425. endoreg_db/models/rule/rule_applicator.py +0 -224
  426. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  427. endoreg_db/models/rule/rule_type.py +0 -20
  428. endoreg_db/models/rule/ruleset.py +0 -17
  429. endoreg_db/renames.yml +0 -8
  430. endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
  431. endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
  432. endoreg_db/serializers/_old/video.py +0 -71
  433. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
  434. endoreg_db/serializers/meta/report_meta.py +0 -53
  435. endoreg_db/serializers/report/__init__.py +0 -9
  436. endoreg_db/serializers/report/mixins.py +0 -45
  437. endoreg_db/serializers/report/report.py +0 -105
  438. endoreg_db/serializers/report/report_list.py +0 -22
  439. endoreg_db/serializers/report/secure_file_url.py +0 -26
  440. endoreg_db/serializers/video/video_metadata.py +0 -105
  441. endoreg_db/services/requirements_object.py +0 -147
  442. endoreg_db/services/storage_aware_video_processor.py +0 -344
  443. endoreg_db/urls/files.py +0 -6
  444. endoreg_db/urls/label_video_segment_validate.py +0 -33
  445. endoreg_db/urls/label_video_segments.py +0 -46
  446. endoreg_db/urls/report.py +0 -48
  447. endoreg_db/urls/video.py +0 -61
  448. endoreg_db/utils/case_generator/case_generator.py +0 -159
  449. endoreg_db/utils/case_generator/utils.py +0 -30
  450. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +0 -368
  451. endoreg_db/views/label/__init__.py +0 -5
  452. endoreg_db/views/label/label.py +0 -15
  453. endoreg_db/views/label_video_segment/__init__.py +0 -16
  454. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
  455. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
  456. endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
  457. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
  458. endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
  459. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
  460. endoreg_db/views/label_video_segment/validate.py +0 -226
  461. endoreg_db/views/media/segments.py +0 -71
  462. endoreg_db/views/meta/available_files_list.py +0 -146
  463. endoreg_db/views/meta/report_meta.py +0 -53
  464. endoreg_db/views/meta/sensitive_meta_detail.py +0 -148
  465. endoreg_db/views/misc/secure_file_serving_view.py +0 -80
  466. endoreg_db/views/misc/secure_file_url_view.py +0 -84
  467. endoreg_db/views/misc/secure_url_validate.py +0 -79
  468. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
  469. endoreg_db/views/patient_finding_location/__init__.py +0 -5
  470. endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
  471. endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
  472. endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
  473. endoreg_db/views/pdf/__init__.py +0 -8
  474. endoreg_db/views/report/report_list.py +0 -112
  475. endoreg_db/views/report/report_with_secure_url.py +0 -28
  476. endoreg_db/views/report/start_examination.py +0 -7
  477. endoreg_db/views/video/segmentation.py +0 -274
  478. endoreg_db/views/video/task_status.py +0 -49
  479. endoreg_db/views/video/timeline.py +0 -46
  480. endoreg_db/views/video/video_analyze.py +0 -52
  481. endoreg_db/views.py +0 -0
  482. /endoreg_db/data/requirement/{colonoscopy_baseline_austria.yaml → old/colonoscopy_baseline_austria.yaml} +0 -0
  483. /endoreg_db/data/requirement/{disease_cardiovascular.yaml → old/disease_cardiovascular.yaml} +0 -0
  484. /endoreg_db/data/requirement/{disease_classification_choice_cardiovascular.yaml → old/disease_classification_choice_cardiovascular.yaml} +0 -0
  485. /endoreg_db/data/requirement/{disease_hepatology.yaml → old/disease_hepatology.yaml} +0 -0
  486. /endoreg_db/data/requirement/{disease_misc.yaml → old/disease_misc.yaml} +0 -0
  487. /endoreg_db/data/requirement/{disease_renal.yaml → old/disease_renal.yaml} +0 -0
  488. /endoreg_db/data/requirement/{endoscopy_bleeding_risk.yaml → old/endoscopy_bleeding_risk.yaml} +0 -0
  489. /endoreg_db/data/requirement/{event_cardiology.yaml → old/event_cardiology.yaml} +0 -0
  490. /endoreg_db/data/requirement/{event_requirements.yaml → old/event_requirements.yaml} +0 -0
  491. /endoreg_db/data/requirement/{finding_colon_polyp.yaml → old/finding_colon_polyp.yaml} +0 -0
  492. /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
  493. /endoreg_db/data/requirement/{lab_value.yaml → old/lab_value.yaml} +0 -0
  494. /endoreg_db/data/requirement/{medication.yaml → old/medication.yaml} +0 -0
  495. /endoreg_db/data/requirement_operator/{age.yaml → _old/age.yaml} +0 -0
  496. /endoreg_db/data/requirement_operator/{lab_operators.yaml → _old/lab_operators.yaml} +0 -0
  497. /endoreg_db/data/requirement_operator/{model_operators.yaml → _old/model_operators.yaml} +0 -0
  498. /endoreg_db/{models/media/video/refactor_plan.md → import_files/pseudonymization/__init__.py} +0 -0
  499. /endoreg_db/{models/media/video/video_file_frames.py → import_files/pseudonymization/pseudonymize.py} +0 -0
  500. /endoreg_db/models/{metadata/frame_ocr_result.py → report/__init__.py} +0 -0
  501. /endoreg_db/{urls/sensitive_meta.py → models/report/images.py} +0 -0
  502. /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
  503. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,52 +1,55 @@
1
+ from typing import TYPE_CHECKING, cast
2
+
1
3
  from django.db import models
2
4
 
5
+
3
6
  class FindingInterventionManager(models.Manager):
4
7
  def get_by_natural_key(self, name):
5
8
  return self.get(name=name)
6
-
9
+
10
+
7
11
  class FindingIntervention(models.Model):
8
12
  name = models.CharField(max_length=100, unique=True)
9
13
  description = models.TextField(blank=True, null=True)
10
-
11
- intervention_types = models.ManyToManyField(
12
- 'FindingInterventionType',
14
+ intervention_types = models.ManyToManyField("FindingInterventionType", blank=True, related_name="interventions")
15
+ information_sources = models.ManyToManyField(
16
+ "InformationSource",
17
+ related_name="finding_interventions",
13
18
  blank=True,
14
- related_name='interventions'
15
19
  )
20
+ objects = FindingInterventionManager()
16
21
 
17
- required_lab_values = models.ManyToManyField(
18
- 'LabValue',
19
- blank=True,
20
- related_name='required_by_finding_interventions'
21
- )
22
+ if TYPE_CHECKING:
23
+ from endoreg_db.models import Contraindication, FindingInterventionType, InformationSource, LabValue
22
24
 
23
- contraindications = models.ManyToManyField(
24
- 'Contraindication',
25
- blank=True,
26
- related_name='contraindicating_finding_interventions'
27
- )
25
+ intervention_types = cast(models.manager.RelatedManager["FindingInterventionType"], intervention_types)
26
+ information_sources = cast(models.manager.RelatedManager["InformationSource"], information_sources)
28
27
 
29
- objects = FindingInterventionManager()
30
-
31
28
  def natural_key(self):
32
29
  return (self.name,)
33
-
30
+
34
31
  def __str__(self):
35
32
  return str(self.name)
36
-
33
+
37
34
 
38
35
  class FindingInterventionTypeManager(models.Manager):
39
36
  def get_by_natural_key(self, name):
40
37
  return self.get(name=name)
41
-
38
+
39
+
42
40
  class FindingInterventionType(models.Model):
43
41
  name = models.CharField(max_length=100, unique=True)
44
42
  description = models.TextField(blank=True, null=True)
45
43
 
46
44
  objects = FindingInterventionTypeManager()
47
-
45
+
46
+ if TYPE_CHECKING:
47
+
48
+ @property
49
+ def interventions(self) -> "models.manager.RelatedManager[FindingIntervention]": ...
50
+
48
51
  def natural_key(self):
49
52
  return (self.name,)
50
-
53
+
51
54
  def __str__(self):
52
55
  return str(self.name)
@@ -1,35 +1,36 @@
1
- from django.db import models
2
1
  from typing import TYPE_CHECKING
3
2
 
3
+ from django.db import models
4
+
5
+
4
6
  class FindingTypeManager(models.Manager):
5
7
  def get_by_natural_key(self, name):
6
8
  """
7
9
  Retrieve a FindingType instance by its unique name for natural key deserialization.
8
-
10
+
9
11
  Parameters:
10
12
  name (str): The unique name of the FindingType to retrieve.
11
-
13
+
12
14
  Returns:
13
15
  FindingType: The instance matching the given name.
14
16
  """
15
17
  return self.get(name=name)
16
-
18
+
19
+
17
20
  class FindingType(models.Model):
18
- name = models.CharField(max_length=100, unique=True)
21
+ name = models.CharField(max_length=100, unique=True)
19
22
  description = models.TextField(blank=True, null=True)
20
23
 
21
24
  objects = FindingTypeManager()
22
25
 
23
26
  if TYPE_CHECKING:
24
- from endoreg_db.models import (
25
- Finding, Examination, FindingClassification, FindingMorphologyClassification
26
- )
27
- finding_classifications: models.QuerySet['FindingClassification']
28
- morphology_classifications: models.QuerySet['FindingMorphologyClassification']
29
-
27
+ from endoreg_db.models import Examination, Finding, FindingClassification
28
+
29
+ @property
30
+ def finding_classifications(self) -> "models.manager.RelatedManager[FindingClassification]": ...
30
31
 
31
32
  def natural_key(self):
32
33
  return (self.name,)
33
-
34
+
34
35
  def __str__(self):
35
36
  return self.name
@@ -1,34 +1,26 @@
1
- from django.db import models#
2
1
  from typing import TYPE_CHECKING
3
2
 
3
+ from django.db import models #
4
+
5
+
4
6
  class EndoscopeManager(models.Manager):
5
7
  def get_by_natural_key(self, name, sn):
6
8
  return self.get(name=name, sn=sn)
7
9
 
10
+
8
11
  class Endoscope(models.Model):
9
12
  objects = EndoscopeManager()
10
13
 
11
- name = models.CharField(max_length=255)
14
+ name = models.CharField(max_length=255)
12
15
  sn = models.CharField(max_length=255)
13
- center = models.ForeignKey(
14
- 'Center',
15
- blank=True,
16
- null=True,
17
- on_delete=models.CASCADE,
18
- related_name='endoscopes'
19
- )
20
- endoscope_type = models.ForeignKey(
21
- 'EndoscopeType',
22
- blank=True,
23
- null=True,
24
- on_delete=models.CASCADE,
25
- related_name='endoscopes'
26
- )
16
+ center = models.ForeignKey("Center", blank=True, null=True, on_delete=models.CASCADE, related_name="endoscopes")
17
+ endoscope_type = models.ForeignKey("EndoscopeType", blank=True, null=True, on_delete=models.CASCADE, related_name="endoscopes")
27
18
 
28
19
  if TYPE_CHECKING:
29
- from endoreg_db.models.center.center import Center
30
- center: "Center"
31
- endoscope_type: "EndoscopeType"
20
+ from endoreg_db.models import Center
21
+
22
+ center: models.ForeignKey["Center|None"]
23
+ endoscope_type: models.ForeignKey["EndoscopeType|None"]
32
24
 
33
25
  def natural_key(self):
34
26
  return (self.name, self.sn)
@@ -37,14 +29,22 @@ class Endoscope(models.Model):
37
29
  return str(self.name)
38
30
 
39
31
  class Meta:
40
- ordering = ['name']
41
- verbose_name = 'Endoscope'
42
- verbose_name_plural = 'Endoscopes'
32
+ ordering = ["name"]
33
+ verbose_name = "Endoscope"
34
+ verbose_name_plural = "Endoscopes"
35
+
36
+ @property
37
+ def center_safe(self) -> "Center":
38
+ if self.center is None:
39
+ raise ValueError("Endoscope has no associated center.")
40
+ return self.center
41
+
43
42
 
44
43
  class EndoscopeTypeManager(models.Manager):
45
44
  def get_by_natural_key(self, name):
46
45
  return self.get(name=name)
47
46
 
47
+
48
48
  class EndoscopeType(models.Model):
49
49
  objects = EndoscopeTypeManager()
50
50
 
@@ -55,11 +55,11 @@ class EndoscopeType(models.Model):
55
55
 
56
56
  def natural_key(self) -> tuple[str]:
57
57
  return (self.name,)
58
-
58
+
59
59
  def __str__(self):
60
60
  return str(self.name)
61
61
 
62
62
  class Meta:
63
- ordering = ['name']
64
- verbose_name = 'Endoscope Type'
65
- verbose_name_plural = 'Endoscope Types'
63
+ ordering = ["name"]
64
+ verbose_name = "Endoscope Type"
65
+ verbose_name_plural = "Endoscope Types"
@@ -72,8 +72,8 @@ class EndoscopyProcessor(models.Model):
72
72
  return (self.name,)
73
73
 
74
74
  @classmethod
75
- def get_by_name(self, name):
76
- return self.objects.get(name=name)
75
+ def get_by_name(cls, name):
76
+ return cls.objects.get(name=name)
77
77
 
78
78
  def __str__(self) -> str:
79
79
  if self.name is None:
@@ -1,27 +1,31 @@
1
- from django.db import models
2
1
  import warnings
3
- from typing import TYPE_CHECKING
2
+ from typing import TYPE_CHECKING, Optional
3
+
4
+ from django.db import models
4
5
 
5
6
  if TYPE_CHECKING:
6
- from endoreg_db.models.other.unit import Unit
7
- from ...other.distribution import (
8
- SingleCategoricalValueDistribution,
9
- NumericValueDistribution,
10
- MultipleCategoricalValueDistribution,
7
+ from endoreg_db.models import (
11
8
  DateValueDistribution,
9
+ Gender,
10
+ MultipleCategoricalValueDistribution,
11
+ NumericValueDistribution,
12
+ Patient,
13
+ SingleCategoricalValueDistribution,
14
+ Unit,
12
15
  )
13
- from ...administration.person.patient import Patient # Added Patient for type hinting
14
16
 
15
17
  LANG = "de"
16
18
 
17
19
  from pydantic import BaseModel, ConfigDict
18
20
 
21
+
19
22
  class CommonLabValues(BaseModel):
20
23
  """
21
24
  A Pydantic model representing a lookup for common lab values.
22
25
  It is used to provide a structured way to access common lab values like
23
26
  hemoglobin, creatinine, and others
24
27
  """
28
+
25
29
  hb: "LabValue"
26
30
  wbc: "LabValue"
27
31
  plt: "LabValue"
@@ -32,31 +36,27 @@ class CommonLabValues(BaseModel):
32
36
  inr: "LabValue"
33
37
  crp: "LabValue"
34
38
 
35
- model_config = ConfigDict(
36
- from_attributes = True,
37
- arbitrary_types_allowed = True
38
- )
39
+ model_config = ConfigDict(from_attributes=True, arbitrary_types_allowed=True)
40
+
39
41
 
40
42
  class LabValueManager(models.Manager):
41
43
  def get_by_natural_key(self, name):
42
44
  """
43
45
  Retrieves a LabValue instance by its unique name.
44
-
46
+
45
47
  Args:
46
48
  name: The unique name of the LabValue.
47
-
49
+
48
50
  Returns:
49
51
  The LabValue instance with the specified name.
50
52
  """
51
53
  return self.get(name=name)
52
-
54
+
53
55
 
54
56
  class LabValue(models.Model):
55
- name = models.CharField(max_length=255, unique=True)
57
+ name = models.CharField(max_length=255, unique=True)
56
58
  abbreviation = models.CharField(max_length=10, blank=True, null=True)
57
- default_unit = models.ForeignKey(
58
- "Unit", on_delete=models.CASCADE, blank=True, null=True
59
- )
59
+ default_unit = models.ForeignKey("Unit", on_delete=models.CASCADE, blank=True, null=True)
60
60
  numeric_precision = models.IntegerField(default=3)
61
61
  default_single_categorical_value_distribution = models.ForeignKey(
62
62
  "SingleCategoricalValueDistribution",
@@ -91,23 +91,22 @@ class LabValue(models.Model):
91
91
  normal_range_gender_dependent = models.BooleanField(default=False)
92
92
  normal_range_special_case = models.BooleanField(default=False)
93
93
  bound_adjustment_factor = models.FloatField(
94
- default=0.1,
95
- help_text="Factor for adjusting bounds when generating increased/decreased values, e.g., 0.1 for 10%."
94
+ default=0.1, help_text="Factor for adjusting bounds when generating increased/decreased values, e.g., 0.1 for 10%."
96
95
  )
97
96
  objects = LabValueManager()
98
97
 
99
98
  if TYPE_CHECKING:
100
- default_unit: "Unit"
101
- default_single_categorical_value_distribution: "SingleCategoricalValueDistribution"
102
- default_numerical_value_distribution: "NumericValueDistribution"
103
- default_multiple_categorical_value_distribution: "MultipleCategoricalValueDistribution"
104
- default_date_value_distribution: "DateValueDistribution"
99
+ default_unit: models.ForeignKey["Unit|None"]
100
+ default_single_categorical_value_distribution: models.ForeignKey["SingleCategoricalValueDistribution|None"]
101
+ default_numerical_value_distribution: models.ForeignKey["NumericValueDistribution|None"]
102
+ default_multiple_categorical_value_distribution: models.ForeignKey["MultipleCategoricalValueDistribution|None"]
103
+ default_date_value_distribution: models.ForeignKey["DateValueDistribution|None"]
105
104
 
106
105
  @classmethod
107
106
  def get_common_lab_values(cls):
108
107
  """
109
108
  Retrieves a structured set of common laboratory values as a CommonLabValues instance.
110
-
109
+
111
110
  Returns:
112
111
  A CommonLabValues Pydantic model populated with LabValue objects for hemoglobin, white blood cells, platelets, creatinine, sodium, potassium, glucose, international normalized ratio, and C-reactive protein.
113
112
  """
@@ -122,10 +121,9 @@ class LabValue(models.Model):
122
121
  k=cls.objects.get(name="potassium"),
123
122
  glc=cls.objects.get(name="glucose"),
124
123
  inr=cls.objects.get(name="international_normalized_ratio"),
125
- crp=cls.objects.get(name="c_reactive_protein")
124
+ crp=cls.objects.get(name="c_reactive_protein"),
126
125
  )
127
126
 
128
-
129
127
  def natural_key(self):
130
128
  """Returns a tuple containing the unique name of this lab value instance."""
131
129
  return (self.name,)
@@ -160,13 +158,13 @@ class LabValue(models.Model):
160
158
  warnings.warn("No default distribution set for lab value")
161
159
  return None
162
160
 
163
- def get_normal_range(self, age: int = None, gender=None):
161
+ def get_normal_range(self, age: Optional[int] = None, gender: Optional["Gender"] = None):
164
162
  """
165
163
  Returns the normal range for this lab value, considering age and gender dependencies.
166
-
164
+
167
165
  If the normal range is gender-dependent, attempts to use the provided gender; defaults to "male" if gender is missing or unknown. Falls back to general min/max values if gender-specific data is unavailable. Issues warnings for unimplemented age-dependent or special case ranges, and when min or max values cannot be determined. Returns a dictionary with keys "min" and "max", which may be None if the range is not defined.
168
166
  """
169
- from ...other.gender import Gender
167
+ from endoreg_db.models import Gender
170
168
 
171
169
  assert isinstance(age, int) or age is None
172
170
  assert isinstance(gender, Gender) or gender is None
@@ -181,20 +179,15 @@ class LabValue(models.Model):
181
179
 
182
180
  gender_name_to_use = None
183
181
  if gender_dependent:
184
- if gender and hasattr(gender, 'name') and gender.name:
182
+ if gender and hasattr(gender, "name") and gender.name:
185
183
  gender_name_to_use = gender.name
186
184
  if gender_name_to_use not in current_range_source:
187
185
  warnings.warn(
188
- f"Normal range for gender '{gender_name_to_use}' not found for LabValue '{self.name}'. "
189
- f"Defaulting to 'male' range.",
190
- UserWarning
186
+ f"Normal range for gender '{gender_name_to_use}' not found for LabValue '{self.name}'. Defaulting to 'male' range.", UserWarning
191
187
  )
192
188
  gender_name_to_use = "male"
193
189
  else:
194
- warnings.warn(
195
- f"Gender not provided for gender-dependent LabValue '{self.name}'. Defaulting to 'male' range.",
196
- UserWarning
197
- )
190
+ warnings.warn(f"Gender not provided for gender-dependent LabValue '{self.name}'. Defaulting to 'male' range.", UserWarning)
198
191
  gender_name_to_use = "male"
199
192
 
200
193
  # Attempt gender-specific lookup
@@ -204,9 +197,8 @@ class LabValue(models.Model):
204
197
  max_value = gender_specific_data.get("max")
205
198
  else:
206
199
  warnings.warn(
207
- f"No gender-specific data found for '{gender_name_to_use}' in LabValue '{self.name}'. "
208
- f"Falling back to general range if available.",
209
- UserWarning
200
+ f"No gender-specific data found for '{gender_name_to_use}' in LabValue '{self.name}'. Falling back to general range if available.",
201
+ UserWarning,
210
202
  )
211
203
 
212
204
  # Fallback to general min/max if needed
@@ -229,16 +221,14 @@ class LabValue(models.Model):
229
221
  if min_value is None:
230
222
  context_parts = []
231
223
  if gender_dependent:
232
- gender_repr = (gender.name if gender and hasattr(gender, 'name') else 'None')
224
+ gender_repr = gender.name if gender and hasattr(gender, "name") else "None"
233
225
  if gender_name_to_use and gender_name_to_use != gender_repr:
234
226
  gender_repr = f"{gender_repr} (lookup attempted for: {gender_name_to_use})"
235
227
  context_parts.append(f"gender: {gender_repr}")
236
228
  if age_dependent:
237
229
  context_parts.append(f"age: {age}")
238
230
 
239
- warning_message = (
240
- f"Could not determine a 'min' normal range for LabValue '{self.name}'"
241
- )
231
+ warning_message = f"Could not determine a 'min' normal range for LabValue '{self.name}'"
242
232
  if context_parts:
243
233
  warning_message += f" with context ({', '.join(context_parts)})."
244
234
  else:
@@ -248,7 +238,7 @@ class LabValue(models.Model):
248
238
 
249
239
  return {"min": min_value, "max": max_value}
250
240
 
251
- def get_increased_value(self, patient: "Patient" = None):
241
+ def get_increased_value(self, patient: Optional["Patient"] = None): # -> Any | None:
252
242
  """
253
243
  Returns a value that is considered increased for this lab value.
254
244
  It prioritizes sampling from a numerical distribution if available,
@@ -263,19 +253,17 @@ class LabValue(models.Model):
263
253
  if patient:
264
254
  # Attempt to sample above the upper bound, or a high value if no bound
265
255
  for _ in range(10): # Try a few times to get a value if bounds are restrictive
266
- generated_value = self.default_numerical_value_distribution.generate_value(
267
- lab_value=self, patient=patient
268
- )
256
+ generated_value = self.default_numerical_value_distribution.generate_value(lab_value=self, patient=patient)
269
257
  if upper_bound is not None:
270
258
  if generated_value > upper_bound:
271
259
  return generated_value
272
260
  # Heuristic for "high" if no upper_bound, compare against mean + stddev
273
- elif hasattr(self.default_numerical_value_distribution, "mean") and \
274
- hasattr(self.default_numerical_value_distribution, "stddev") and \
275
- self.default_numerical_value_distribution.mean is not None and \
276
- self.default_numerical_value_distribution.stddev is not None and \
277
- generated_value > (
278
- self.default_numerical_value_distribution.mean + self.default_numerical_value_distribution.stddev
261
+ elif (
262
+ hasattr(self.default_numerical_value_distribution, "mean")
263
+ and hasattr(self.default_numerical_value_distribution, "stddev")
264
+ and self.default_numerical_value_distribution.mean is not None
265
+ and self.default_numerical_value_distribution.stddev is not None
266
+ and generated_value > (self.default_numerical_value_distribution.mean + self.default_numerical_value_distribution.stddev)
279
267
  ):
280
268
  return generated_value
281
269
  # Fallback if sampling fails to produce a clearly increased value
@@ -290,23 +278,19 @@ class LabValue(models.Model):
290
278
  if upper_bound is not None:
291
279
  return upper_bound + (abs(upper_bound * self.bound_adjustment_factor) if upper_bound != 0 else 1)
292
280
  else:
293
- warnings.warn(
294
- f"Cannot determine an increased value for {self.name} without an upper normal range or patient context for distribution."
295
- )
281
+ warnings.warn(f"Cannot determine an increased value for {self.name} without an upper normal range or patient context for distribution.")
296
282
  return None
297
283
 
298
284
  elif upper_bound is not None:
299
285
  return upper_bound + (abs(upper_bound * self.bound_adjustment_factor) if upper_bound != 0 else 1)
300
286
  else:
301
- warnings.warn(
302
- f"Cannot determine an increased value for {self.name} without a numerical distribution or an upper normal range."
303
- )
287
+ warnings.warn(f"Cannot determine an increased value for {self.name} without a numerical distribution or an upper normal range.")
304
288
  return None
305
289
 
306
- def get_normal_value(self, patient: "Patient" = None):
290
+ def get_normal_value(self, patient: Optional["Patient"] = None):
307
291
  """
308
292
  Returns a value considered normal for this lab value.
309
-
293
+
310
294
  If a numerical distribution and patient context are available, attempts to generate a value within the normal range. Falls back to the midpoint of the normal range or to available bounds if sampling fails or context is insufficient. Returns None if neither a normal range nor a distribution is available.
311
295
  """
312
296
  _age = patient.age() if patient else None
@@ -318,9 +302,7 @@ class LabValue(models.Model):
318
302
  if self.default_numerical_value_distribution:
319
303
  if patient:
320
304
  for _ in range(10): # Try a few times
321
- generated_value = self.default_numerical_value_distribution.generate_value(
322
- lab_value=self, patient=patient
323
- )
305
+ generated_value = self.default_numerical_value_distribution.generate_value(lab_value=self, patient=patient)
324
306
  if lower_bound is not None and upper_bound is not None:
325
307
  if lower_bound <= generated_value <= upper_bound:
326
308
  return generated_value
@@ -346,10 +328,7 @@ class LabValue(models.Model):
346
328
  elif upper_bound is not None:
347
329
  return upper_bound
348
330
  else:
349
- warnings.warn(
350
- f"Cannot determine a normal value for {self.name} without a normal range or patient context for distribution.",
351
- UserWarning
352
- )
331
+ warnings.warn(f"Cannot determine a normal value for {self.name} without a normal range or patient context for distribution.", UserWarning)
353
332
  return None
354
333
 
355
334
  elif lower_bound is not None and upper_bound is not None:
@@ -359,12 +338,10 @@ class LabValue(models.Model):
359
338
  elif upper_bound is not None: # Only max is defined
360
339
  return upper_bound
361
340
  else:
362
- warnings.warn(
363
- f"Cannot determine a normal value for {self.name} without a numerical distribution or a normal range."
364
- )
341
+ warnings.warn(f"Cannot determine a normal value for {self.name} without a numerical distribution or a normal range.")
365
342
  return None
366
343
 
367
- def get_decreased_value(self, patient: "Patient" = None):
344
+ def get_decreased_value(self, patient: Optional["Patient"] = None):
368
345
  """
369
346
  Returns a value that is considered decreased for this lab value.
370
347
  It prioritizes sampling from a numerical distribution if available,
@@ -378,19 +355,17 @@ class LabValue(models.Model):
378
355
  if self.default_numerical_value_distribution:
379
356
  if patient:
380
357
  for _ in range(10): # Try a few times
381
- generated_value = self.default_numerical_value_distribution.generate_value(
382
- lab_value=self, patient=patient
383
- )
358
+ generated_value = self.default_numerical_value_distribution.generate_value(lab_value=self, patient=patient)
384
359
  if lower_bound is not None:
385
360
  if generated_value < lower_bound:
386
361
  return generated_value
387
362
  # Heuristic for "low" if no lower_bound, compare against mean - stddev
388
- elif hasattr(self.default_numerical_value_distribution, "mean") and \
389
- hasattr(self.default_numerical_value_distribution, "stddev") and \
390
- self.default_numerical_value_distribution.mean is not None and \
391
- self.default_numerical_value_distribution.stddev is not None and \
392
- generated_value < (
393
- self.default_numerical_value_distribution.mean - self.default_numerical_value_distribution.stddev
363
+ elif (
364
+ hasattr(self.default_numerical_value_distribution, "mean")
365
+ and hasattr(self.default_numerical_value_distribution, "stddev")
366
+ and self.default_numerical_value_distribution.mean is not None
367
+ and self.default_numerical_value_distribution.stddev is not None
368
+ and generated_value < (self.default_numerical_value_distribution.mean - self.default_numerical_value_distribution.stddev)
394
369
  ):
395
370
  return generated_value
396
371
  # Fallback
@@ -405,15 +380,11 @@ class LabValue(models.Model):
405
380
  if lower_bound is not None:
406
381
  return lower_bound - (abs(lower_bound * self.bound_adjustment_factor) if lower_bound != 0 else 1)
407
382
  else:
408
- warnings.warn(
409
- f"Cannot determine a decreased value for {self.name} without a lower normal range or patient context for distribution."
410
- )
383
+ warnings.warn(f"Cannot determine a decreased value for {self.name} without a lower normal range or patient context for distribution.")
411
384
  return None
412
385
 
413
386
  elif lower_bound is not None:
414
387
  return lower_bound - (abs(lower_bound * self.bound_adjustment_factor) if lower_bound != 0 else 1)
415
388
  else:
416
- warnings.warn(
417
- f"Cannot determine a decreased value for {self.name} without a numerical distribution or a lower normal range."
418
- )
389
+ warnings.warn(f"Cannot determine a decreased value for {self.name} without a numerical distribution or a lower normal range.")
419
390
  return None
@@ -1,31 +1,43 @@
1
- '''Model for the medication.'''
1
+ """Model for the medication."""
2
+
3
+ from typing import TYPE_CHECKING
4
+
2
5
  from django.db import models
3
6
 
4
7
 
5
8
  class MedicationManager(models.Manager):
6
- '''Manager for the medication model.'''
9
+ """Manager for the medication model."""
10
+
7
11
  def get_by_natural_key(self, name):
8
- '''Retrieve a medication by its natural key.'''
12
+ """Retrieve a medication by its natural key."""
9
13
  return self.get(name=name)
10
14
 
15
+
11
16
  class Medication(models.Model):
12
- '''Model representing a medication.'''
13
- name = models.CharField(max_length=255, unique=True)
14
- adapt_to_renal_function = models.BooleanField(default = False)
17
+ """Model representing a medication."""
18
+
19
+ name = models.CharField(max_length=255, unique=True)
20
+ adapt_to_renal_function = models.BooleanField(default=False)
15
21
  adapt_to_hepatic_function = models.BooleanField(default=False)
16
22
  adapt_to_indication = models.BooleanField(default=False)
17
23
  adapt_to_age = models.BooleanField(default=False)
18
24
  adapt_to_weight = models.BooleanField(default=False)
19
25
  adapt_to_risk = models.BooleanField(default=False)
20
- default_unit = models.ForeignKey('Unit', on_delete=models.CASCADE)
21
-
26
+ default_unit = models.ForeignKey("Unit", on_delete=models.CASCADE)
22
27
 
23
28
  objects = MedicationManager()
24
29
 
30
+ if TYPE_CHECKING:
31
+ from endoreg_db.models import MedicationSchedule, Unit
32
+
33
+ default_unit: models.ForeignKey["Unit"]
34
+
35
+ @property
36
+ def medication_schedules(self) -> "models.manager.RelatedManager[MedicationSchedule]": ...
37
+
25
38
  def natural_key(self):
26
- '''Return the natural key for the medication.'''
39
+ """Return the natural key for the medication."""
27
40
  return (self.name,)
28
41
 
29
42
  def __str__(self):
30
43
  return str(self.name)
31
-
@@ -1,5 +1,7 @@
1
1
  """Model for medication indication."""
2
2
 
3
+ from typing import TYPE_CHECKING, cast
4
+
3
5
  from django.db import models
4
6
 
5
7
 
@@ -25,11 +27,35 @@ class MedicationIndication(models.Model):
25
27
  )
26
28
  diseases = models.ManyToManyField("Disease")
27
29
  events = models.ManyToManyField("Event")
28
- disease_classification_choices = models.ManyToManyField(
29
- "DiseaseClassificationChoice"
30
- )
30
+ disease_classification_choices = models.ManyToManyField("DiseaseClassificationChoice")
31
31
  sources = models.ManyToManyField("InformationSource")
32
32
 
33
+ if TYPE_CHECKING:
34
+ from endoreg_db.models import (
35
+ Disease,
36
+ DiseaseClassificationChoice,
37
+ Event,
38
+ InformationSource,
39
+ MedicationIndicationType,
40
+ MedicationSchedule,
41
+ )
42
+
43
+ indication_type: models.ForeignKey["MedicationIndicationType"]
44
+ medication_schedules = cast(
45
+ "models.manager.RelatedManager[MedicationSchedule]",
46
+ medication_schedules,
47
+ )
48
+ diseases = cast("models.manager.RelatedManager[Disease]", diseases)
49
+ events = cast("models.manager.RelatedManager[Event]", events)
50
+ disease_classification_choices = cast(
51
+ "models.manager.RelatedManager[DiseaseClassificationChoice]",
52
+ disease_classification_choices,
53
+ )
54
+ sources = cast(
55
+ "models.manager.RelatedManager[InformationSource]",
56
+ sources,
57
+ )
58
+
33
59
  def get_indication_links(self) -> dict:
34
60
  """Return a dictionary of all linked objects for this medication indication."""
35
61
  links = {