endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.0__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 (360) 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 +8 -31
  10. endoreg_db/data/_examples/disease.yaml +55 -0
  11. endoreg_db/data/_examples/disease_classification.yaml +13 -0
  12. endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
  13. endoreg_db/data/_examples/event.yaml +64 -0
  14. endoreg_db/data/_examples/examination.yaml +72 -0
  15. endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
  16. endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
  17. endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
  18. endoreg_db/data/_examples/finding/complication.yaml +16 -0
  19. endoreg_db/data/_examples/finding/data.yaml +105 -0
  20. endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
  21. endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
  22. endoreg_db/data/_examples/finding/outcome.yaml +12 -0
  23. endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
  24. endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
  25. endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
  26. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
  27. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
  28. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
  29. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
  30. endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
  31. endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
  32. endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
  33. endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
  34. endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
  35. endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
  36. endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
  37. endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
  38. endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
  39. endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
  40. endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
  41. endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
  42. endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
  43. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
  44. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
  45. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
  46. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
  47. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
  48. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
  49. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
  50. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
  51. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
  52. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
  53. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
  54. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
  55. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
  56. endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
  57. endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
  58. endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
  59. endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
  60. endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
  61. endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
  62. endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
  63. endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
  64. endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
  65. endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
  66. endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
  67. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  68. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  69. endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
  70. endoreg_db/data/_examples/finding_type/data.yaml +43 -0
  71. endoreg_db/data/_examples/requirement/age.yaml +26 -0
  72. endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
  73. endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
  74. endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
  75. endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
  76. endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
  77. endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
  78. endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
  79. endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
  80. endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
  81. endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
  82. endoreg_db/data/_examples/requirement/gender.yaml +25 -0
  83. endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
  84. endoreg_db/data/_examples/requirement/medication.yaml +93 -0
  85. endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
  86. endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
  87. endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
  88. endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
  89. endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
  90. endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
  91. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
  92. endoreg_db/data/event_classification/data.yaml +4 -0
  93. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  94. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
  95. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
  96. endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
  97. endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
  98. endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
  99. endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
  100. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
  101. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  102. endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
  103. endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
  104. endoreg_db/data/requirement_set/_old_ +109 -0
  105. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  106. endoreg_db/data/setup_config.yaml +4 -4
  107. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  108. endoreg_db/exceptions.py +5 -2
  109. endoreg_db/helpers/data_loader.py +1 -1
  110. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  111. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  112. endoreg_db/management/commands/import_video.py +9 -10
  113. endoreg_db/management/commands/import_video_with_classification.py +1 -1
  114. endoreg_db/management/commands/init_default_ai_model.py +1 -1
  115. endoreg_db/management/commands/list_routes.py +18 -0
  116. endoreg_db/management/commands/load_center_data.py +12 -12
  117. endoreg_db/management/commands/load_requirement_data.py +60 -31
  118. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  119. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  120. endoreg_db/management/commands/storage_management.py +271 -203
  121. endoreg_db/migrations/0001_initial.py +1799 -1300
  122. endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
  123. endoreg_db/migrations/_old/0001_initial.py +1857 -0
  124. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
  125. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
  126. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
  127. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
  128. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
  129. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
  130. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
  131. endoreg_db/models/__init__.py +78 -123
  132. endoreg_db/models/administration/__init__.py +21 -42
  133. endoreg_db/models/administration/ai/active_model.py +2 -2
  134. endoreg_db/models/administration/ai/ai_model.py +7 -6
  135. endoreg_db/models/administration/case/__init__.py +1 -15
  136. endoreg_db/models/administration/case/case.py +3 -3
  137. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  138. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  139. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  140. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  141. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  142. endoreg_db/models/administration/center/center.py +33 -19
  143. endoreg_db/models/administration/center/center_product.py +12 -9
  144. endoreg_db/models/administration/center/center_resource.py +25 -19
  145. endoreg_db/models/administration/center/center_shift.py +21 -17
  146. endoreg_db/models/administration/center/center_waste.py +16 -8
  147. endoreg_db/models/administration/person/__init__.py +2 -0
  148. endoreg_db/models/administration/person/employee/employee.py +10 -5
  149. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  150. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  151. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  152. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  153. endoreg_db/models/administration/person/patient/patient.py +103 -100
  154. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  155. endoreg_db/models/administration/person/person.py +4 -0
  156. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  157. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  158. endoreg_db/models/administration/product/product.py +20 -15
  159. endoreg_db/models/administration/product/product_material.py +17 -18
  160. endoreg_db/models/administration/product/product_weight.py +12 -8
  161. endoreg_db/models/administration/product/reference_product.py +23 -55
  162. endoreg_db/models/administration/qualification/qualification.py +7 -3
  163. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  164. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  165. endoreg_db/models/administration/shift/shift.py +16 -12
  166. endoreg_db/models/administration/shift/shift_type.py +23 -31
  167. endoreg_db/models/label/__init__.py +7 -8
  168. endoreg_db/models/label/annotation/image_classification.py +10 -9
  169. endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
  170. endoreg_db/models/label/label.py +15 -15
  171. endoreg_db/models/label/label_set.py +19 -6
  172. endoreg_db/models/label/label_type.py +1 -1
  173. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  174. endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
  175. endoreg_db/models/label/video_segmentation_label.py +4 -0
  176. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  177. endoreg_db/models/media/frame/frame.py +22 -22
  178. endoreg_db/models/media/pdf/raw_pdf.py +110 -182
  179. endoreg_db/models/media/pdf/report_file.py +25 -29
  180. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
  181. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  182. endoreg_db/models/media/video/__init__.py +1 -0
  183. endoreg_db/models/media/video/create_from_file.py +48 -56
  184. endoreg_db/models/media/video/pipe_2.py +8 -9
  185. endoreg_db/models/media/video/video_file.py +150 -108
  186. endoreg_db/models/media/video/video_file_ai.py +288 -74
  187. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  188. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  189. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  190. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  191. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  192. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  193. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  194. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  195. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  196. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  197. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  198. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  199. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  200. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  201. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  202. endoreg_db/models/media/video/video_file_io.py +109 -62
  203. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  204. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  205. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  206. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  207. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  208. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  209. endoreg_db/models/media/video/video_file_segments.py +24 -17
  210. endoreg_db/models/media/video/video_metadata.py +19 -35
  211. endoreg_db/models/media/video/video_processing.py +96 -95
  212. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  213. endoreg_db/models/medical/disease.py +22 -16
  214. endoreg_db/models/medical/event.py +31 -18
  215. endoreg_db/models/medical/examination/__init__.py +13 -6
  216. endoreg_db/models/medical/examination/examination.py +17 -18
  217. endoreg_db/models/medical/examination/examination_indication.py +26 -25
  218. endoreg_db/models/medical/examination/examination_time.py +16 -6
  219. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  220. endoreg_db/models/medical/examination/examination_type.py +3 -4
  221. endoreg_db/models/medical/finding/finding.py +38 -39
  222. endoreg_db/models/medical/finding/finding_classification.py +37 -48
  223. endoreg_db/models/medical/finding/finding_intervention.py +27 -22
  224. endoreg_db/models/medical/finding/finding_type.py +13 -12
  225. endoreg_db/models/medical/hardware/endoscope.py +20 -26
  226. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  227. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  228. endoreg_db/models/medical/medication/medication.py +22 -10
  229. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  230. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  231. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  232. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  233. endoreg_db/models/medical/organ/__init__.py +15 -12
  234. endoreg_db/models/medical/patient/medication_examples.py +1 -5
  235. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  236. endoreg_db/models/medical/patient/patient_event.py +19 -22
  237. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  238. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  239. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  240. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  241. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  242. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  243. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  244. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  245. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  246. endoreg_db/models/medical/risk/risk.py +7 -6
  247. endoreg_db/models/medical/risk/risk_type.py +8 -5
  248. endoreg_db/models/metadata/model_meta.py +60 -29
  249. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  250. endoreg_db/models/metadata/pdf_meta.py +19 -24
  251. endoreg_db/models/metadata/sensitive_meta.py +102 -85
  252. endoreg_db/models/metadata/sensitive_meta_logic.py +192 -173
  253. endoreg_db/models/metadata/video_meta.py +51 -31
  254. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  255. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  256. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  257. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  258. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  259. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  260. endoreg_db/models/other/emission/emission_factor.py +18 -8
  261. endoreg_db/models/other/gender.py +10 -5
  262. endoreg_db/models/other/information_source.py +25 -25
  263. endoreg_db/models/other/material.py +9 -5
  264. endoreg_db/models/other/resource.py +6 -4
  265. endoreg_db/models/other/tag.py +10 -5
  266. endoreg_db/models/other/transport_route.py +13 -8
  267. endoreg_db/models/other/unit.py +10 -6
  268. endoreg_db/models/other/waste.py +6 -5
  269. endoreg_db/models/requirement/requirement.py +580 -272
  270. endoreg_db/models/requirement/requirement_error.py +85 -0
  271. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  272. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  273. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  274. endoreg_db/models/requirement/requirement_operator.py +36 -33
  275. endoreg_db/models/requirement/requirement_set.py +74 -57
  276. endoreg_db/models/state/__init__.py +4 -4
  277. endoreg_db/models/state/abstract.py +2 -2
  278. endoreg_db/models/state/anonymization.py +12 -0
  279. endoreg_db/models/state/audit_ledger.py +46 -47
  280. endoreg_db/models/state/label_video_segment.py +9 -0
  281. endoreg_db/models/state/raw_pdf.py +40 -46
  282. endoreg_db/models/state/sensitive_meta.py +6 -2
  283. endoreg_db/models/state/video.py +58 -53
  284. endoreg_db/models/upload_job.py +32 -55
  285. endoreg_db/models/utils.py +1 -2
  286. endoreg_db/root_urls.py +21 -2
  287. endoreg_db/serializers/__init__.py +0 -2
  288. endoreg_db/serializers/anonymization.py +18 -10
  289. endoreg_db/serializers/meta/report_meta.py +1 -1
  290. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  291. endoreg_db/serializers/misc/file_overview.py +11 -99
  292. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  293. endoreg_db/serializers/video/segmentation.py +2 -1
  294. endoreg_db/serializers/video/video_processing_history.py +20 -5
  295. endoreg_db/services/anonymization.py +75 -73
  296. endoreg_db/services/lookup_service.py +37 -24
  297. endoreg_db/services/pdf_import.py +166 -68
  298. endoreg_db/services/storage_aware_video_processor.py +140 -114
  299. endoreg_db/services/video_import.py +193 -283
  300. endoreg_db/urls/__init__.py +7 -20
  301. endoreg_db/urls/media.py +108 -67
  302. endoreg_db/urls/root_urls.py +29 -0
  303. endoreg_db/utils/__init__.py +15 -5
  304. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  305. endoreg_db/utils/case_generator/__init__.py +3 -0
  306. endoreg_db/utils/dataloader.py +88 -16
  307. endoreg_db/utils/defaults/set_default_center.py +32 -0
  308. endoreg_db/utils/names.py +22 -16
  309. endoreg_db/utils/permissions.py +2 -1
  310. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  311. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
  312. endoreg_db/utils/setup_config.py +8 -5
  313. endoreg_db/utils/storage.py +115 -0
  314. endoreg_db/utils/validate_endo_roi.py +8 -2
  315. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  316. endoreg_db/views/__init__.py +0 -10
  317. endoreg_db/views/anonymization/media_management.py +198 -163
  318. endoreg_db/views/anonymization/overview.py +4 -1
  319. endoreg_db/views/anonymization/validate.py +174 -40
  320. endoreg_db/views/media/__init__.py +2 -0
  321. endoreg_db/views/media/pdf_media.py +131 -152
  322. endoreg_db/views/media/sensitive_metadata.py +46 -6
  323. endoreg_db/views/media/video_media.py +89 -82
  324. endoreg_db/views/media/video_segments.py +2 -3
  325. endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
  326. endoreg_db/views/patient/patient.py +5 -4
  327. endoreg_db/views/pdf/pdf_stream.py +20 -21
  328. endoreg_db/views/pdf/reimport.py +11 -32
  329. endoreg_db/views/requirement/evaluate.py +188 -187
  330. endoreg_db/views/requirement/lookup.py +17 -3
  331. endoreg_db/views/requirement/requirement_utils.py +89 -0
  332. endoreg_db/views/video/__init__.py +0 -2
  333. endoreg_db/views/video/correction.py +2 -2
  334. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
  335. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +341 -245
  336. endoreg_db/models/administration/permissions/__init__.py +0 -44
  337. endoreg_db/models/media/video/video_file_frames.py +0 -0
  338. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  339. endoreg_db/models/rule/__init__.py +0 -13
  340. endoreg_db/models/rule/rule.py +0 -27
  341. endoreg_db/models/rule/rule_applicator.py +0 -224
  342. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  343. endoreg_db/models/rule/rule_type.py +0 -20
  344. endoreg_db/models/rule/ruleset.py +0 -17
  345. endoreg_db/serializers/video/video_metadata.py +0 -105
  346. endoreg_db/urls/report.py +0 -48
  347. endoreg_db/urls/video.py +0 -61
  348. endoreg_db/utils/case_generator/case_generator.py +0 -159
  349. endoreg_db/utils/case_generator/utils.py +0 -30
  350. endoreg_db/views/report/__init__.py +0 -9
  351. endoreg_db/views/report/report_list.py +0 -112
  352. endoreg_db/views/report/report_with_secure_url.py +0 -28
  353. endoreg_db/views/report/start_examination.py +0 -7
  354. endoreg_db/views.py +0 -0
  355. /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
  356. /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
  357. /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
  358. /endoreg_db/{models/media/video/refactor_plan.md → views/pdf/pdf_stream_views.py} +0 -0
  359. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
  360. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -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,16 @@ 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
+
43
36
 
44
37
  class EndoscopeTypeManager(models.Manager):
45
38
  def get_by_natural_key(self, name):
46
39
  return self.get(name=name)
47
40
 
41
+
48
42
  class EndoscopeType(models.Model):
49
43
  objects = EndoscopeTypeManager()
50
44
 
@@ -55,11 +49,11 @@ class EndoscopeType(models.Model):
55
49
 
56
50
  def natural_key(self) -> tuple[str]:
57
51
  return (self.name,)
58
-
52
+
59
53
  def __str__(self):
60
54
  return str(self.name)
61
55
 
62
56
  class Meta:
63
- ordering = ['name']
64
- verbose_name = 'Endoscope Type'
65
- verbose_name_plural = 'Endoscope Types'
57
+ ordering = ["name"]
58
+ verbose_name = "Endoscope Type"
59
+ 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 = {
@@ -1,39 +1,50 @@
1
- '''Model for medication indication type.'''
2
- from django.db import models
1
+ """Model for medication indication type."""
2
+
3
3
  from typing import TYPE_CHECKING
4
4
 
5
+ from django.db import models
6
+
5
7
  if TYPE_CHECKING:
6
8
  from endoreg_db.models import MedicationIndication
7
9
 
10
+
8
11
  class MedicationIndicationTypeManager(models.Manager):
9
- '''Manager for the medication indication type model.'''
12
+ """Manager for the medication indication type model."""
13
+
10
14
  def get_by_natural_key(self, name):
11
- '''Retrieve a medication indication type by its natural key.'''
15
+ """Retrieve a medication indication type by its natural key."""
12
16
  return self.get(name=name)
13
17
 
18
+
14
19
  class MedicationIndicationType(models.Model):
15
- '''Model representing a medication indication type.'''
16
- name = models.CharField(max_length=255, unique=True)
20
+ """Model representing a medication indication type."""
21
+
22
+ name = models.CharField(max_length=255, unique=True)
17
23
 
18
24
  objects = MedicationIndicationTypeManager()
19
25
 
20
26
  if TYPE_CHECKING:
21
- medication_indications: models.QuerySet["MedicationIndication"]
27
+
28
+ @property
29
+ def medication_indications(self) -> "models.manager.RelatedManager[MedicationIndication]": ...
22
30
 
23
31
  def natural_key(self):
24
- '''Return the natural key for the medication indication type.'''
32
+ """Return the natural key for the medication indication type."""
25
33
  return (self.name,)
26
34
 
27
35
  def __str__(self):
28
36
  return str(self.name)
29
-
37
+
30
38
  @classmethod
31
39
  def get_random_indication_by_type(cls, name) -> "MedicationIndication":
32
- '''Return a random medication indication of the given type.'''
33
- return cls.objects.get(name=name).medication_indications.order_by('?').first()
34
-
40
+ """Return a random medication indication of the given type."""
41
+ med_indication = cls.objects.get(name=name).medication_indications.order_by("?").first()
42
+ if not med_indication:
43
+ raise cls.DoesNotExist(f"No medication indication found for type: {name}")
44
+ return med_indication
35
45
 
36
46
  def get_random_medication_indication(self):
37
- '''Return a random medication indication of this type.'''
47
+ """Return a random medication indication of this type."""
38
48
  from endoreg_db.models import MedicationIndication
39
- return MedicationIndication.objects.filter(indication_type=self).order_by('?').first()
49
+
50
+ return MedicationIndication.objects.filter(indication_type=self).order_by("?").first()