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
@@ -4,33 +4,27 @@
4
4
  # Class contains classmethod to create object from pdf file
5
5
  # objects contains methods to extract text, extract metadata from text and anonymize text from pdf file uzing agl_report_reader.ReportReader class
6
6
  # ------------------------------------------------------------------------------
7
- import os
8
- from typing import TYPE_CHECKING, Optional, Union
7
+ from typing import TYPE_CHECKING, Optional, cast
9
8
 
10
9
  from django.core.exceptions import ValidationError
11
10
  from django.core.files import File
12
11
  from django.core.validators import FileExtensionValidator
13
12
  from django.db import models
14
- from numpy import isin # Import Django File
15
13
 
16
14
  from endoreg_db.utils.file_operations import get_uuid_filename
17
15
  from endoreg_db.utils.hashs import get_pdf_hash
18
- from endoreg_db.utils.paths import PDF_DIR, RAW_PDF_DIR
19
-
20
- # Use the specific paths from the centralized paths module
21
- from ...utils import PDF_DIR
16
+ from endoreg_db.utils.paths import PDF_DIR
17
+ from endoreg_db.utils.storage import (
18
+ delete_field_file,
19
+ ensure_local_file,
20
+ file_exists,
21
+ save_local_file,
22
+ )
22
23
 
23
24
  if TYPE_CHECKING:
24
- from endoreg_db.models.administration.person import (
25
- Examiner,
26
- Patient,
27
- )
25
+ from django.db.models.fields.files import FieldFile
28
26
 
29
- from ...administration import Center
30
- from ...medical.patient import PatientExamination
31
- from ...metadata.pdf_meta import PdfType
32
- from ...state import RawPdfState
33
- from .report_file import AnonymExaminationReport
27
+ from endoreg_db.models.state import RawPdfState
34
28
 
35
29
  # setup logging to pdf_import.log
36
30
  import logging
@@ -42,6 +36,7 @@ logger = logging.getLogger("raw_pdf")
42
36
 
43
37
 
44
38
  class RawPdfFile(models.Model):
39
+ objects = models.Manager()
45
40
  # Fields from AbstractPdfFile
46
41
  pdf_hash = models.CharField(max_length=255, unique=True)
47
42
  pdf_type = models.ForeignKey(
@@ -55,50 +50,89 @@ class RawPdfFile(models.Model):
55
50
  on_delete=models.SET_NULL,
56
51
  blank=True,
57
52
  null=True,
58
- ) # type: ignore
53
+ )
59
54
  examination = models.ForeignKey(
60
55
  "PatientExamination",
61
56
  on_delete=models.SET_NULL,
62
57
  blank=True,
63
58
  null=True,
64
59
  related_name="raw_pdf_files",
65
- ) # type: ignore
60
+ )
66
61
  examiner = models.ForeignKey(
67
62
  "Examiner",
68
63
  on_delete=models.SET_NULL,
69
64
  blank=True,
70
65
  null=True,
71
- ) # type: ignore
66
+ )
72
67
  text = models.TextField(blank=True, null=True)
73
68
  date_created = models.DateTimeField(auto_now_add=True)
74
69
  date_modified = models.DateTimeField(auto_now=True)
75
- anonymized = models.BooleanField(
76
- default=False, help_text="True if the PDF has been anonymized."
77
- )
78
-
79
- # Fields specific to RawPdfFile (keeping existing related_names)
70
+ anonymized = models.BooleanField(default=False, help_text="True if the PDF has been anonymized.")
80
71
  file = models.FileField(
81
72
  # Use the relative path from the specific PDF_DIR
82
73
  upload_to=PDF_DIR.name,
83
74
  validators=[FileExtensionValidator(allowed_extensions=["pdf"])],
84
- ) # type: ignore
85
-
75
+ )
86
76
  anonymized_file = models.FileField(
87
77
  upload_to=PDF_DIR.name,
88
78
  validators=[FileExtensionValidator(allowed_extensions=["pdf"])],
89
79
  null=True,
90
80
  blank=True,
91
- ) # type: ignore
92
-
81
+ )
93
82
  state = models.OneToOneField(
94
83
  "RawPdfState",
95
84
  on_delete=models.SET_NULL,
96
85
  blank=True,
97
86
  null=True,
98
87
  related_name="raw_pdf_file",
99
- ) # type: ignore
88
+ )
89
+ patient = models.ForeignKey(
90
+ "Patient",
91
+ on_delete=models.SET_NULL,
92
+ blank=True,
93
+ null=True,
94
+ related_name="raw_pdf_files",
95
+ )
96
+ sensitive_meta = models.ForeignKey(
97
+ "SensitiveMeta",
98
+ on_delete=models.SET_NULL,
99
+ related_name="raw_pdf_files",
100
+ null=True,
101
+ blank=True,
102
+ )
103
+ state_report_processing_required = models.BooleanField(default=True)
104
+ state_report_processed = models.BooleanField(default=False)
105
+ raw_meta = models.JSONField(blank=True, null=True)
106
+ anonym_examination_report = models.OneToOneField(
107
+ "AnonymExaminationReport",
108
+ on_delete=models.SET_NULL,
109
+ blank=True,
110
+ null=True,
111
+ related_name="raw_pdf_file",
112
+ )
113
+ anonymized_text = models.TextField(blank=True, null=True)
100
114
 
101
- objects = models.Manager()
115
+ # Type hinting is needed, improve and use correct django types
116
+ if TYPE_CHECKING:
117
+ from endoreg_db.models import (
118
+ AnonymExaminationReport,
119
+ Center,
120
+ Examiner,
121
+ Patient,
122
+ PatientExamination,
123
+ RawPdfState,
124
+ SensitiveMeta,
125
+ )
126
+
127
+ center: models.ForeignKey["Center | None"]
128
+ examination: models.ForeignKey["PatientExamination | None"]
129
+ examiner: models.ForeignKey["Examiner | None"]
130
+ state: models.ForeignKey["RawPdfState | None"]
131
+ patient: models.ForeignKey["Patient | None"]
132
+ sensitive_meta: models.ForeignKey["SensitiveMeta | None"]
133
+ anonym_examination_report: models.OneToOneField["AnonymExaminationReport | None"]
134
+ file = cast(FieldFile, file)
135
+ anonymized_file = cast(FieldFile, anonymized_file)
102
136
 
103
137
  @property
104
138
  def uuid(self):
@@ -130,7 +164,10 @@ class RawPdfFile(models.Model):
130
164
  """
131
165
  Sets the file path of the stored PDF file.
132
166
  """
133
- self.file = File(file_path) # type: ignore
167
+ if not file_path.exists():
168
+ raise FileNotFoundError(f"File path does not exist: {file_path}")
169
+
170
+ save_local_file(self.file, file_path, name=file_path.name, save=False)
134
171
  self.save(update_fields=["file"])
135
172
 
136
173
  @property
@@ -149,7 +186,10 @@ class RawPdfFile(models.Model):
149
186
  """
150
187
  Sets the file path of the anonymized PDF file.
151
188
  """
152
- self.anonymized_file = File(file_path) # type: ignore
189
+ if not file_path.exists():
190
+ raise FileNotFoundError(f"File path does not exist: {file_path}")
191
+
192
+ save_local_file(self.anonymized_file, file_path, name=file_path.name, save=False)
153
193
  self.save(update_fields=["anonymized_file"])
154
194
 
155
195
  def get_raw_file_path(self) -> Optional[Path]:
@@ -228,55 +268,10 @@ class RawPdfFile(models.Model):
228
268
  Returns the URL of the stored PDF file if available; otherwise, returns None.
229
269
  """
230
270
  try:
231
- return (
232
- self.anonymized_file.url
233
- if self.anonymized_file and self.anonymized_file.name
234
- else None
235
- )
271
+ return self.anonymized_file.url if self.anonymized_file and self.anonymized_file.name else None
236
272
  except (ValueError, AttributeError):
237
273
  return None
238
274
 
239
- patient = models.ForeignKey(
240
- "Patient",
241
- on_delete=models.SET_NULL,
242
- blank=True,
243
- null=True,
244
- related_name="raw_pdf_files",
245
- ) # type: ignore
246
- sensitive_meta = models.ForeignKey(
247
- "SensitiveMeta",
248
- on_delete=models.SET_NULL,
249
- related_name="raw_pdf_files",
250
- null=True,
251
- blank=True,
252
- ) # type: ignore
253
- state_report_processing_required = models.BooleanField(default=True)
254
- state_report_processed = models.BooleanField(default=False)
255
- raw_meta = models.JSONField(blank=True, null=True)
256
- anonym_examination_report = models.OneToOneField(
257
- "AnonymExaminationReport",
258
- on_delete=models.SET_NULL,
259
- blank=True,
260
- null=True,
261
- related_name="raw_pdf_file",
262
- ) # type: ignore
263
- anonymized_text = models.TextField(blank=True, null=True)
264
-
265
- # Type hinting is needed, improve and use correct django types
266
- if TYPE_CHECKING:
267
- file: Optional[Union[models.FieldFile, models.FileField]]
268
- anonymized_file: Optional[Union[models.FieldFile, models.FileField]]
269
- pdf_type: Optional[models.ForeignKey]
270
- examination: Optional[models.ForeignKey["PatientExamination"]]
271
- examiner: Optional[models.ForeignKey["Examiner"]]
272
- patient: Optional[models.ForeignKey["Patient"]]
273
- center: Optional[models.ForeignKey["Center"]]
274
- anonym_examination_report: Optional[
275
- models.OneToOneField["AnonymExaminationReport"]
276
- ]
277
- sensitive_meta: Optional[models.ForeignKey["SensitiveMeta"]]
278
- state: Optional[models.ForeignKey["RawPdfState"]]
279
-
280
275
  def __str__(self):
281
276
  """
282
277
  Return a string representation of the RawPdfFile, including its PDF hash, type, and center.
@@ -290,35 +285,17 @@ class RawPdfFile(models.Model):
290
285
 
291
286
  This method ensures that the physical PDF file is deleted from the file system after the database record is removed. Logs warnings or errors if the file cannot be found or deleted.
292
287
  """
293
- # Call the original delete method first to remove DB record
294
- if self.file:
295
- try:
296
- if self.file_path:
297
- os.remove(Path(self.file_path))
298
- logger.info("Original file removed: %s", self.file)
299
- except Exception as e:
300
- logger.warning(
301
- f"Could not get file path for {self.file.name} before deletion: {e}"
302
- )
303
- if self.anonymized_file:
304
- try:
305
- if self.anonymized_file_path:
306
- os.remove(Path(self.anonymized_file_path))
307
- logger.info(
308
- "Anonymized file removed: %s", self.anonymized_file.name
309
- )
310
- except OSError as e:
311
- logger.error(
312
- "Error removing anonymized file %s: %s",
313
- self.anonymized_file.name,
314
- e,
315
- )
288
+ primary_name = self.file.name if self.file and self.file.name else None
289
+ anonymized_name = self.anonymized_file.name if self.anonymized_file and self.anonymized_file.name else None
290
+
291
+ if delete_field_file(self.file, missing_ok=True, save=False):
292
+ logger.info("Original file removed from storage: %s", primary_name)
293
+ if delete_field_file(self.anonymized_file, missing_ok=True, save=False):
294
+ logger.info("Anonymized file removed from storage: %s", anonymized_name)
316
295
 
317
296
  super().delete(*args, **kwargs)
318
297
 
319
- def validate_metadata_annotation(
320
- self, extracted_data_dict: Optional[dict] = None
321
- ) -> bool:
298
+ def validate_metadata_annotation(self, extracted_data_dict: Optional[dict] = None) -> bool:
322
299
  """
323
300
  Validate the metadata of the RawPdf instance.
324
301
 
@@ -334,8 +311,10 @@ class RawPdfFile(models.Model):
334
311
  logger.error("No extracted data provided for validation.")
335
312
  return False
336
313
 
337
- # Update sensitive meta with the provided data
338
- self.sensitive_meta.update_from_dict(extracted_data_dict)
314
+ if extracted_data_dict:
315
+ self.sensitive_meta.update_from_dict(extracted_data_dict)
316
+ else:
317
+ return False
339
318
 
340
319
  # Save the sensitive meta to ensure changes are persisted
341
320
  self.sensitive_meta.save()
@@ -345,21 +324,12 @@ class RawPdfFile(models.Model):
345
324
 
346
325
  logger.info(f"Metadata for PDF {self.pk} validated and updated successfully.")
347
326
 
348
- if self.file_path:
349
- try:
350
- os.unlink(self.file_path) # Delete the original file if it exists
351
- except OSError as e:
352
- logger.error(f"Error removing original file {self.file_path}: {e}")
353
-
354
- if self.anonymized_file_path:
355
- try:
356
- os.unlink(self.anonymized_file_path)
357
- except OSError as e:
358
- logger.error(
359
- f"Error removing anonymized file {self.anonymized_file_path}: {e}"
360
- )
327
+ deleted_original = delete_field_file(self.file, missing_ok=True, save=False)
328
+ deleted_anonymized = delete_field_file(self.anonymized_file, missing_ok=True, save=False)
329
+ self.get_or_create_state().mark_anonymization_validated()
361
330
 
362
- self.save() # Save the model to persist the cleared file fields
331
+ if deleted_original or deleted_anonymized:
332
+ self.save(update_fields=["file", "anonymized_file"]) # Persist cleared fields
363
333
 
364
334
  logger.info(f"Files for PDF {self.pk} deleted successfully.")
365
335
  return True
@@ -457,13 +427,9 @@ class RawPdfFile(models.Model):
457
427
  )
458
428
  # Re-save the file from the source to potentially fix it
459
429
  with file_path.open("rb") as f:
460
- django_file = File(
461
- f, name=Path(_file.name).name
462
- ) # Use existing name if possible
463
- existing_pdf_file.file = django_file # type: ignore
464
- existing_pdf_file.save(
465
- update_fields=["file"]
466
- ) # Only update file field
430
+ django_file = File(f, name=Path(_file.name).name) # Use existing name if possible
431
+ existing_pdf_file.file = django_file
432
+ existing_pdf_file.save(update_fields=["file"]) # Only update file field
467
433
  else:
468
434
  pass
469
435
  # logger.debug("File for existing RawPdfFile %s already exists in storage.", pdf_hash)
@@ -519,9 +485,7 @@ class RawPdfFile(models.Model):
519
485
  "File was not saved correctly to storage path %s after model save.",
520
486
  _file.name,
521
487
  )
522
- raise IOError(
523
- f"File not found at expected storage path after save: {_file.name}"
524
- )
488
+ raise IOError(f"File not found at expected storage path after save: {_file.name}")
525
489
 
526
490
  try:
527
491
  logger.info("File saved to absolute path: %s", _file.path)
@@ -532,18 +496,14 @@ class RawPdfFile(models.Model):
532
496
  )
533
497
 
534
498
  except Exception as e:
535
- logger.error(
536
- "Error processing or saving file %s for new record: %s", file_path, e
537
- )
499
+ logger.error("Error processing or saving file %s for new record: %s", file_path, e)
538
500
  raise
539
501
 
540
502
  # Delete source file *after* successful save and verification
541
503
  if delete_source:
542
504
  try:
543
505
  file_path.unlink()
544
- logger.info(
545
- "Deleted source file %s after creating new record.", file_path
546
- )
506
+ logger.info("Deleted source file %s after creating new record.", file_path)
547
507
  except OSError as e:
548
508
  logger.error("Error deleting source file %s: %s", file_path, e)
549
509
 
@@ -560,27 +520,15 @@ class RawPdfFile(models.Model):
560
520
  """
561
521
  if not self.pk and not self.pdf_hash and self.file:
562
522
  try:
563
- file_path = Path(self.file.path).resolve()
564
- if not file_path.exists():
565
- raise FileNotFoundError(f"File path does not exist: {file_path}")
566
- # Read from the file object before it's saved by storage
567
- self.file.open("rb") # Ensure file is open
568
- self.file.seek(0) # Go to beginning
569
- self.pdf_hash = get_pdf_hash(
570
- file_path
571
- ) # Assuming get_pdf_hash can handle file obj
572
- self.file.seek(0) # Reset position
573
- self.file.close() # Close after reading
574
- logger.info(f"Calculated hash during pre-save for {self.file.name}")
575
- except Exception as e:
523
+ with ensure_local_file(self.file) as local_path:
524
+ self.pdf_hash = get_pdf_hash(local_path)
525
+ logger.info("Calculated hash during pre-save for %s", self.file.name)
526
+ except Exception as exc:
576
527
  logger.warning(
577
528
  "Could not calculate hash before initial save for %s: %s",
578
529
  self.file.name,
579
- e,
530
+ exc,
580
531
  )
581
- # Ensure file is closed if opened
582
- if hasattr(self.file, "closed") and not self.file.closed:
583
- self.file.close()
584
532
 
585
533
  if self.file and not self.file.name.endswith(".pdf"):
586
534
  raise ValidationError("Only PDF files are allowed")
@@ -588,30 +536,16 @@ class RawPdfFile(models.Model):
588
536
  # If hash is still missing after potential creation logic (e.g., direct instantiation)
589
537
  # and the file exists in storage, try calculating it from storage path.
590
538
  # This is less ideal as it requires the file to be saved first.
591
- if (
592
- not self.pdf_hash
593
- and self.pk
594
- and self.file
595
- and self.file.storage.exists(self.file.name)
596
- ):
539
+ if not self.pdf_hash and self.pk and self.file and file_exists(self.file):
597
540
  try:
598
- file_path = Path(self.file.path).resolve()
599
- if not file_path.exists():
600
- raise FileNotFoundError(f"File path does not exist: {file_path}")
601
- logger.warning(
602
- f"Hash missing for saved file {self.file.name}. Recalculating."
603
- )
604
- with self.file.storage.open(self.file.name, "rb") as f:
605
- self.pdf_hash = get_pdf_hash(
606
- file_path
607
- ) # Assuming get_pdf_hash handles file obj
608
- # No need to save again just for hash unless update_fields is used carefully
609
- # Let the main super().save() handle saving the hash if it changed
610
- except Exception as e:
541
+ with ensure_local_file(self.file) as local_path:
542
+ logger.warning("Hash missing for saved file %s. Recalculating.", self.file.name)
543
+ self.pdf_hash = get_pdf_hash(local_path)
544
+ except Exception as exc:
611
545
  logger.error(
612
546
  "Could not calculate hash during save for existing file %s: %s",
613
547
  self.file.name,
614
- e,
548
+ exc,
615
549
  )
616
550
 
617
551
  # Derive related fields from sensitive_meta if available
@@ -663,18 +597,12 @@ class RawPdfFile(models.Model):
663
597
  assert _file is not None
664
598
  try:
665
599
  if not _file.field.storage.exists(_file.name):
666
- logger.warning(
667
- f"File missing at storage path {_file.name}. Attempting copy from fallback {fallback_file}"
668
- )
600
+ logger.warning(f"File missing at storage path {_file.name}. Attempting copy from fallback {fallback_file}")
669
601
  if fallback_file.exists():
670
602
  with fallback_file.open("rb") as f:
671
603
  # Use save method which handles storage backend
672
- _file.save(
673
- Path(_file.name).name, File(f), save=True
674
- ) # Re-save the file content
675
- logger.info(
676
- f"Successfully restored file from fallback {fallback_file} to {_file.name}"
677
- )
604
+ _file.save(Path(_file.name).name, File(f), save=True) # Re-save the file content
605
+ logger.info(f"Successfully restored file from fallback {fallback_file} to {_file.name}")
678
606
  else:
679
607
  logger.error(f"Fallback file {fallback_file} does not exist.")
680
608
  except Exception as e:
@@ -1,7 +1,9 @@
1
- from ...utils import DOCUMENT_DIR, STORAGE_DIR
2
- from django.db import models
3
1
  from typing import TYPE_CHECKING
4
2
 
3
+ from django.db import models
4
+
5
+ from ...utils import DOCUMENT_DIR, STORAGE_DIR
6
+
5
7
  if TYPE_CHECKING:
6
8
  from ...administration import (
7
9
  Center,
@@ -12,17 +14,21 @@ if TYPE_CHECKING:
12
14
  )
13
15
  from ...metadata import SensitiveMeta
14
16
 
17
+
15
18
  class DocumentTypeManager(models.Manager):
16
19
  """
17
20
  Custom manager for DocumentType.
18
21
  """
22
+
19
23
  def get_by_natural_key(self, name):
20
24
  return self.get(name=name)
21
25
 
26
+
22
27
  class DocumentType(models.Model):
23
28
  """
24
29
  Represents the type of a document.
25
30
  """
31
+
26
32
  name = models.CharField(max_length=255, unique=True)
27
33
  description = models.TextField(blank=True, null=True)
28
34
 
@@ -33,21 +39,23 @@ class DocumentType(models.Model):
33
39
 
34
40
  def __str__(self):
35
41
  return str(self.name)
36
-
42
+
37
43
  class Meta:
38
44
  verbose_name = "Document Type"
39
45
  verbose_name_plural = "Document Types"
40
46
 
47
+
41
48
  class AbstractDocument(models.Model):
42
49
  """
43
50
  Abstract base class for documents.
44
51
  """
52
+
45
53
  meta = models.JSONField(blank=True, null=True)
46
54
  text = models.TextField(blank=True, null=True)
47
55
  date = models.DateField(blank=True, null=True)
48
56
  time = models.TimeField(blank=True, null=True)
49
57
  file = models.FileField(
50
- upload_to=DOCUMENT_DIR.relative_to(STORAGE_DIR),
58
+ upload_to=DOCUMENT_DIR.relative_to(STORAGE_DIR).as_posix(),
51
59
  blank=True,
52
60
  null=True,
53
61
  )
@@ -67,22 +75,19 @@ class AbstractDocument(models.Model):
67
75
  )
68
76
 
69
77
  if TYPE_CHECKING:
70
- center: "Center"
71
- type: "DocumentType"
78
+ center: models.ForeignKey["Center|None"]
79
+ type: models.ForeignKey["DocumentType|None"]
72
80
 
73
81
  class Meta:
74
82
  abstract = True
75
83
 
76
84
 
77
-
78
-
79
85
  class AbstractExaminationReport(AbstractDocument):
80
86
  """
81
87
  Abstract base class for examination reports.
82
88
  """
83
- patient = models.ForeignKey(
84
- "Patient", on_delete=models.DO_NOTHING, blank=True, null=True
85
- )
89
+
90
+ patient = models.ForeignKey("Patient", on_delete=models.DO_NOTHING, blank=True, null=True)
86
91
 
87
92
  patient_examination = models.ForeignKey(
88
93
  "PatientExamination",
@@ -96,25 +101,18 @@ class AbstractExaminationReport(AbstractDocument):
96
101
  blank=True,
97
102
  )
98
103
 
99
- sensitive_meta = models.ForeignKey(
100
- "SensitiveMeta",
101
- on_delete=models.SET_NULL,
102
- null=True,
103
- blank=True
104
- )
104
+ sensitive_meta = models.ForeignKey("SensitiveMeta", on_delete=models.SET_NULL, null=True, blank=True)
105
105
 
106
106
  if TYPE_CHECKING:
107
- center: "Center"
108
- type: "DocumentType"
109
- patient: "Patient"
110
- patient_examination: "PatientExamination"
111
- sensitive_meta: "SensitiveMeta"
112
-
107
+ center: models.ForeignKey["Center|None"]
108
+ type: models.ForeignKey["DocumentType|None"]
109
+ patient: models.ForeignKey["Patient|None"]
110
+ patient_examination: models.ForeignKey["PatientExamination|None"]
111
+ sensitive_meta: models.ForeignKey["SensitiveMeta|None"]
113
112
 
114
113
  class Meta:
115
114
  abstract = True
116
115
 
117
-
118
116
  def get_or_create_examiner(self, examiner_first_name, examiner_last_name):
119
117
  raise NotImplementedError("Subclasses must implement this method.")
120
118
 
@@ -122,10 +120,8 @@ class AbstractExaminationReport(AbstractDocument):
122
120
  raise NotImplementedError("Subclasses must implement this method.")
123
121
 
124
122
 
125
-
126
123
  class AnonymExaminationReport(AbstractExaminationReport):
127
-
128
- def get_or_create_examiner(self, examiner_first_name:str, examiner_last_name:str):
124
+ def get_or_create_examiner(self, examiner_first_name: str, examiner_last_name: str):
129
125
  from ...administration.person import Examiner
130
126
 
131
127
  examiner_center = self.center
@@ -139,7 +135,7 @@ class AnonymExaminationReport(AbstractExaminationReport):
139
135
  return examiner, created
140
136
 
141
137
  def set_examination_date_and_time(self, report_meta=None):
142
- #TODO
138
+ # TODO
143
139
  if not report_meta:
144
140
  report_meta = self.meta
145
141
  # examination_date_str = report_meta["examination_date"]
@@ -152,11 +148,11 @@ class AnonymExaminationReport(AbstractExaminationReport):
152
148
  # # TODO: get django TimeField compatible time from string (e.g. "12:00")
153
149
  # self.time = time.fromisoformat(examination_time_str)
154
150
 
151
+
155
152
  class AnonymHistologyReport(AbstractExaminationReport):
156
153
  """
157
154
  Represents a histology report.
158
155
  """
159
156
 
160
-
161
157
  def get_or_create_examiner(self, examiner_first_name, examiner_last_name):
162
158
  raise NotImplementedError("Subclasses must implement this method.")