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

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

Potentially problematic release.


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

Files changed (402) hide show
  1. endoreg_db/data/__init__.py +22 -8
  2. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +0 -1
  3. endoreg_db/data/examination/examinations/data.yaml +114 -14
  4. endoreg_db/data/examination/time-type/data.yaml +0 -3
  5. endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
  6. endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
  7. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
  8. endoreg_db/data/finding/00_generic.yaml +35 -0
  9. endoreg_db/data/finding/00_generic_complication.yaml +9 -0
  10. endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
  11. endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
  12. endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
  13. endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
  14. endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
  15. endoreg_db/data/finding_classification/00_generic.yaml +44 -0
  16. endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
  17. endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
  18. endoreg_db/data/finding_classification/{colonoscopy_bowel_preparation.yaml → 02_colonoscopy_baseline.yaml} +35 -20
  19. endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
  20. endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
  21. endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
  22. endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
  23. endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
  24. endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
  25. endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
  26. endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
  27. endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
  28. endoreg_db/data/{_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml → finding_classification_choice/02_colonoscopy_generic.yaml} +1 -1
  29. endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
  30. endoreg_db/data/{_examples/finding_classification_choice/colonoscopy_location.yaml → finding_classification_choice/02_colonoscopy_location.yaml} +23 -4
  31. endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
  32. endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
  33. endoreg_db/data/{_examples/finding_classification_choice/colon_lesion_paris.yaml → finding_classification_choice/02_colonoscopy_polyp_morphology.yaml} +26 -8
  34. endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
  35. endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
  36. endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
  37. endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
  38. endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
  39. endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
  40. endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
  41. endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
  42. endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
  43. endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
  44. endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
  45. endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
  46. endoreg_db/data/finding_type/data.yaml +8 -12
  47. endoreg_db/data/requirement/01_patient_data.yaml +93 -0
  48. endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
  49. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +0 -2
  50. endoreg_db/data/requirement_set/90_coloreg.yaml +20 -8
  51. endoreg_db/exceptions.py +0 -1
  52. endoreg_db/forms/examination_form.py +1 -1
  53. endoreg_db/helpers/data_loader.py +124 -52
  54. endoreg_db/helpers/default_objects.py +116 -81
  55. endoreg_db/import_files/__init__.py +27 -0
  56. endoreg_db/import_files/context/__init__.py +7 -0
  57. endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
  58. endoreg_db/import_files/context/ensure_center.py +17 -0
  59. endoreg_db/import_files/context/file_lock.py +66 -0
  60. endoreg_db/import_files/context/import_context.py +43 -0
  61. endoreg_db/import_files/context/validate_directories.py +56 -0
  62. endoreg_db/import_files/file_storage/__init__.py +15 -0
  63. endoreg_db/import_files/file_storage/create_report_file.py +76 -0
  64. endoreg_db/import_files/file_storage/create_video_file.py +75 -0
  65. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
  66. endoreg_db/import_files/file_storage/state_management.py +400 -0
  67. endoreg_db/import_files/file_storage/storage.py +36 -0
  68. endoreg_db/import_files/import_service.md +26 -0
  69. endoreg_db/import_files/processing/__init__.py +11 -0
  70. endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
  71. endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
  72. endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
  73. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
  74. endoreg_db/import_files/pseudonymization/fake.py +52 -0
  75. endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
  76. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
  77. endoreg_db/import_files/report_import_service.py +141 -0
  78. endoreg_db/import_files/video_import_service.py +150 -0
  79. endoreg_db/management/commands/import_report.py +130 -65
  80. endoreg_db/management/commands/import_video_with_classification.py +1 -1
  81. endoreg_db/management/commands/load_ai_model_data.py +5 -5
  82. endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
  83. endoreg_db/management/commands/load_base_db_data.py +5 -134
  84. endoreg_db/management/commands/load_contraindication_data.py +14 -16
  85. endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
  86. endoreg_db/management/commands/load_disease_classification_data.py +15 -18
  87. endoreg_db/management/commands/load_disease_data.py +25 -28
  88. endoreg_db/management/commands/load_endoscope_data.py +20 -27
  89. endoreg_db/management/commands/load_event_data.py +14 -16
  90. endoreg_db/management/commands/load_examination_data.py +31 -44
  91. endoreg_db/management/commands/load_examination_indication_data.py +20 -21
  92. endoreg_db/management/commands/load_finding_data.py +52 -80
  93. endoreg_db/management/commands/load_information_source.py +21 -23
  94. endoreg_db/management/commands/load_lab_value_data.py +17 -26
  95. endoreg_db/management/commands/load_medication_data.py +13 -12
  96. endoreg_db/management/commands/load_organ_data.py +15 -19
  97. endoreg_db/management/commands/load_pdf_type_data.py +19 -18
  98. endoreg_db/management/commands/load_profession_data.py +14 -17
  99. endoreg_db/management/commands/load_qualification_data.py +20 -23
  100. endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
  101. endoreg_db/management/commands/load_requirement_data.py +14 -20
  102. endoreg_db/management/commands/load_risk_data.py +7 -6
  103. endoreg_db/management/commands/load_shift_data.py +20 -23
  104. endoreg_db/management/commands/load_tag_data.py +8 -11
  105. endoreg_db/management/commands/load_unit_data.py +17 -19
  106. endoreg_db/management/commands/start_filewatcher.py +46 -37
  107. endoreg_db/management/commands/validate_video_files.py +1 -5
  108. endoreg_db/migrations/0001_initial.py +1360 -1812
  109. endoreg_db/models/administration/person/patient/patient.py +72 -46
  110. endoreg_db/models/label/__init__.py +2 -2
  111. endoreg_db/models/label/annotation/video_segmentation_annotation.py +18 -26
  112. endoreg_db/models/label/label_video_segment/label_video_segment.py +23 -1
  113. endoreg_db/models/media/pdf/raw_pdf.py +136 -64
  114. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +34 -10
  115. endoreg_db/models/media/processing_history/__init__.py +5 -0
  116. endoreg_db/models/media/processing_history/processing_history.py +96 -0
  117. endoreg_db/models/media/video/create_from_file.py +101 -31
  118. endoreg_db/models/media/video/video_file.py +125 -105
  119. endoreg_db/models/media/video/video_file_io.py +31 -26
  120. endoreg_db/models/medical/contraindication/README.md +1 -0
  121. endoreg_db/models/medical/examination/examination.py +28 -8
  122. endoreg_db/models/medical/examination/examination_indication.py +13 -79
  123. endoreg_db/models/medical/examination/examination_time.py +8 -3
  124. endoreg_db/models/medical/finding/finding.py +5 -12
  125. endoreg_db/models/medical/finding/finding_classification.py +18 -37
  126. endoreg_db/models/medical/finding/finding_intervention.py +7 -9
  127. endoreg_db/models/medical/hardware/endoscope.py +6 -0
  128. endoreg_db/models/medical/patient/medication_examples.py +5 -1
  129. endoreg_db/models/medical/patient/patient_finding.py +1 -1
  130. endoreg_db/models/metadata/pdf_meta.py +22 -10
  131. endoreg_db/models/metadata/sensitive_meta.py +3 -0
  132. endoreg_db/models/metadata/sensitive_meta_logic.py +200 -124
  133. endoreg_db/models/other/information_source.py +27 -6
  134. endoreg_db/models/report/__init__.py +0 -0
  135. endoreg_db/models/report/images.py +0 -0
  136. endoreg_db/models/report/report.py +6 -0
  137. endoreg_db/models/requirement/requirement.py +59 -399
  138. endoreg_db/models/requirement/requirement_operator.py +86 -98
  139. endoreg_db/models/state/audit_ledger.py +4 -5
  140. endoreg_db/models/state/raw_pdf.py +69 -30
  141. endoreg_db/models/state/video.py +64 -49
  142. endoreg_db/models/upload_job.py +33 -9
  143. endoreg_db/models/utils.py +27 -23
  144. endoreg_db/queries/__init__.py +3 -1
  145. endoreg_db/schemas/examination_evaluation.py +1 -1
  146. endoreg_db/serializers/__init__.py +2 -8
  147. endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
  148. endoreg_db/serializers/meta/__init__.py +1 -6
  149. endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
  150. endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
  151. endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
  152. endoreg_db/serializers/video/video_file_list.py +65 -34
  153. endoreg_db/services/__old/pdf_import.py +1487 -0
  154. endoreg_db/services/__old/video_import.py +1306 -0
  155. endoreg_db/services/anonymization.py +63 -26
  156. endoreg_db/services/lookup_service.py +28 -28
  157. endoreg_db/services/lookup_store.py +2 -2
  158. endoreg_db/services/pdf_import.py +0 -1480
  159. endoreg_db/services/report_import.py +10 -0
  160. endoreg_db/services/video_import.py +6 -1165
  161. endoreg_db/tasks/upload_tasks.py +79 -70
  162. endoreg_db/tasks/video_ingest.py +8 -4
  163. endoreg_db/urls/__init__.py +0 -14
  164. endoreg_db/urls/ai.py +32 -0
  165. endoreg_db/urls/media.py +21 -24
  166. endoreg_db/utils/dataloader.py +87 -57
  167. endoreg_db/utils/paths.py +110 -46
  168. endoreg_db/utils/pipelines/Readme.md +1 -1
  169. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
  170. endoreg_db/views/__init__.py +85 -173
  171. endoreg_db/views/ai/__init__.py +8 -0
  172. endoreg_db/views/ai/label.py +155 -0
  173. endoreg_db/views/anonymization/media_management.py +8 -7
  174. endoreg_db/views/anonymization/overview.py +97 -68
  175. endoreg_db/views/anonymization/validate.py +25 -21
  176. endoreg_db/views/media/__init__.py +5 -20
  177. endoreg_db/views/media/pdf_media.py +109 -65
  178. endoreg_db/views/media/sensitive_metadata.py +163 -148
  179. endoreg_db/views/meta/__init__.py +0 -8
  180. endoreg_db/views/misc/__init__.py +1 -7
  181. endoreg_db/views/misc/upload_views.py +94 -93
  182. endoreg_db/views/report/__init__.py +7 -0
  183. endoreg_db/views/{pdf → report}/reimport.py +45 -24
  184. endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +40 -32
  185. endoreg_db/views/requirement/lookup_store.py +22 -90
  186. endoreg_db/views/video/__init__.py +23 -22
  187. endoreg_db/views/video/correction.py +201 -172
  188. endoreg_db/views/video/reimport.py +1 -1
  189. endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +75 -37
  190. endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
  191. endoreg_db/views/video/video_stream.py +7 -8
  192. {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +2 -2
  193. {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +217 -335
  194. {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
  195. endoreg_db/data/_examples/disease.yaml +0 -55
  196. endoreg_db/data/_examples/disease_classification.yaml +0 -13
  197. endoreg_db/data/_examples/disease_classification_choice.yaml +0 -62
  198. endoreg_db/data/_examples/event.yaml +0 -64
  199. endoreg_db/data/_examples/examination.yaml +0 -72
  200. endoreg_db/data/_examples/finding/anatomy_colon.yaml +0 -128
  201. endoreg_db/data/_examples/finding/colonoscopy.yaml +0 -40
  202. endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +0 -56
  203. endoreg_db/data/_examples/finding/complication.yaml +0 -16
  204. endoreg_db/data/_examples/finding/data.yaml +0 -105
  205. endoreg_db/data/_examples/finding/examination_setting.yaml +0 -16
  206. endoreg_db/data/_examples/finding/medication_related.yaml +0 -18
  207. endoreg_db/data/_examples/finding/outcome.yaml +0 -12
  208. endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +0 -68
  209. endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +0 -22
  210. endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +0 -25
  211. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  212. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  213. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +0 -68
  214. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  215. endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +0 -80
  216. endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +0 -21
  217. endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +0 -20
  218. endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +0 -26
  219. endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +0 -22
  220. endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +0 -53
  221. endoreg_db/data/_examples/finding_classification/complication_generic.yaml +0 -25
  222. endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +0 -40
  223. endoreg_db/data/_examples/finding_classification/histology_colo.yaml +0 -51
  224. endoreg_db/data/_examples/finding_classification/intervention_required.yaml +0 -26
  225. endoreg_db/data/_examples/finding_classification/medication_related.yaml +0 -23
  226. endoreg_db/data/_examples/finding_classification/visualized.yaml +0 -33
  227. endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +0 -78
  228. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  229. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  230. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  231. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  232. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  233. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  234. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  235. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  236. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +0 -82
  237. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  238. endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +0 -15
  239. endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +0 -15
  240. endoreg_db/data/_examples/finding_classification_choice/histology.yaml +0 -24
  241. endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +0 -20
  242. endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +0 -19
  243. endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +0 -11
  244. endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +0 -48
  245. endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +0 -43
  246. endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  247. endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +0 -128
  248. endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +0 -32
  249. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  250. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  251. endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +0 -15
  252. endoreg_db/data/_examples/finding_type/data.yaml +0 -43
  253. endoreg_db/data/_examples/requirement/age.yaml +0 -26
  254. endoreg_db/data/_examples/requirement/gender.yaml +0 -25
  255. endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +0 -48
  256. endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +0 -57
  257. endoreg_db/data/_examples/requirement_set/endoscopy_bleeding_risk.yaml +0 -52
  258. endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
  259. endoreg_db/data/finding/anatomy_colon.yaml +0 -128
  260. endoreg_db/data/finding/colonoscopy.yaml +0 -40
  261. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
  262. endoreg_db/data/finding/complication.yaml +0 -16
  263. endoreg_db/data/finding/data.yaml +0 -105
  264. endoreg_db/data/finding/examination_setting.yaml +0 -16
  265. endoreg_db/data/finding/medication_related.yaml +0 -18
  266. endoreg_db/data/finding/outcome.yaml +0 -12
  267. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
  268. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
  269. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  270. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  271. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -38
  272. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  273. endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -49
  274. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
  275. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
  276. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
  277. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
  278. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
  279. endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
  280. endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
  281. endoreg_db/data/finding_classification/histology_colo.yaml +0 -43
  282. endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
  283. endoreg_db/data/finding_classification/medication_related.yaml +0 -23
  284. endoreg_db/data/finding_classification/visualized.yaml +0 -33
  285. endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  286. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  287. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  288. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  289. endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  290. endoreg_db/data/finding_classification_choice/colon_lesion_paris.yaml +0 -57
  291. endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  292. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  293. endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  294. endoreg_db/data/finding_classification_choice/colonoscopy_location.yaml +0 -229
  295. endoreg_db/data/finding_classification_choice/colonoscopy_not_complete_reason.yaml +0 -19
  296. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
  297. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  298. endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
  299. endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
  300. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  301. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
  302. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
  303. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  304. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  305. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
  306. endoreg_db/data/requirement/age.yaml +0 -26
  307. endoreg_db/data/requirement/colonoscopy_baseline_austria.yaml +0 -45
  308. endoreg_db/data/requirement/disease_cardiovascular.yaml +0 -79
  309. endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +0 -41
  310. endoreg_db/data/requirement/disease_hepatology.yaml +0 -12
  311. endoreg_db/data/requirement/disease_misc.yaml +0 -12
  312. endoreg_db/data/requirement/disease_renal.yaml +0 -96
  313. endoreg_db/data/requirement/endoscopy_bleeding_risk.yaml +0 -59
  314. endoreg_db/data/requirement/event_cardiology.yaml +0 -251
  315. endoreg_db/data/requirement/event_requirements.yaml +0 -145
  316. endoreg_db/data/requirement/finding_colon_polyp.yaml +0 -50
  317. endoreg_db/data/requirement/gender.yaml +0 -25
  318. endoreg_db/data/requirement/lab_value.yaml +0 -441
  319. endoreg_db/data/requirement/medication.yaml +0 -93
  320. endoreg_db/data/requirement_operator/age.yaml +0 -13
  321. endoreg_db/data/requirement_operator/lab_operators.yaml +0 -129
  322. endoreg_db/data/requirement_operator/model_operators.yaml +0 -96
  323. endoreg_db/management/commands/init_default_ai_model.py +0 -112
  324. endoreg_db/management/commands/reset_celery_schedule.py +0 -9
  325. endoreg_db/management/commands/validate_video.py +0 -204
  326. endoreg_db/migrations/0002_requirementset_depends_on.py +0 -18
  327. endoreg_db/migrations/_old/0001_initial.py +0 -1857
  328. endoreg_db/migrations/_old/0002_add_video_correction_models.py +0 -52
  329. endoreg_db/migrations/_old/0003_add_center_display_name.py +0 -30
  330. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +0 -68
  331. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +0 -77
  332. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +0 -14
  333. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +0 -68
  334. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +0 -89
  335. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +0 -27
  336. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +0 -21
  337. endoreg_db/renames.yml +0 -8
  338. endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
  339. endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
  340. endoreg_db/serializers/_old/video.py +0 -71
  341. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
  342. endoreg_db/serializers/meta/report_meta.py +0 -53
  343. endoreg_db/serializers/report/__init__.py +0 -9
  344. endoreg_db/serializers/report/mixins.py +0 -45
  345. endoreg_db/serializers/report/report.py +0 -105
  346. endoreg_db/serializers/report/report_list.py +0 -22
  347. endoreg_db/serializers/report/secure_file_url.py +0 -26
  348. endoreg_db/services/requirements_object.py +0 -147
  349. endoreg_db/services/storage_aware_video_processor.py +0 -370
  350. endoreg_db/urls/files.py +0 -6
  351. endoreg_db/urls/label_video_segment_validate.py +0 -33
  352. endoreg_db/urls/label_video_segments.py +0 -46
  353. endoreg_db/views/label/__init__.py +0 -5
  354. endoreg_db/views/label/label.py +0 -15
  355. endoreg_db/views/label_video_segment/__init__.py +0 -16
  356. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
  357. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
  358. endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
  359. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
  360. endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
  361. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
  362. endoreg_db/views/label_video_segment/validate.py +0 -226
  363. endoreg_db/views/media/segments.py +0 -71
  364. endoreg_db/views/meta/available_files_list.py +0 -146
  365. endoreg_db/views/meta/report_meta.py +0 -53
  366. endoreg_db/views/meta/sensitive_meta_detail.py +0 -85
  367. endoreg_db/views/misc/secure_file_serving_view.py +0 -80
  368. endoreg_db/views/misc/secure_file_url_view.py +0 -84
  369. endoreg_db/views/misc/secure_url_validate.py +0 -79
  370. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
  371. endoreg_db/views/patient_finding_location/__init__.py +0 -5
  372. endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
  373. endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
  374. endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
  375. endoreg_db/views/pdf/__init__.py +0 -8
  376. endoreg_db/views/video/segmentation.py +0 -274
  377. endoreg_db/views/video/task_status.py +0 -49
  378. endoreg_db/views/video/timeline.py +0 -46
  379. endoreg_db/views/video/video_analyze.py +0 -52
  380. /endoreg_db/data/requirement/{colon_polyp_intervention.yaml → old/colon_polyp_intervention.yaml} +0 -0
  381. /endoreg_db/data/{_examples/requirement → requirement/old}/colonoscopy_baseline_austria.yaml +0 -0
  382. /endoreg_db/data/requirement/{coloreg_colon_polyp.yaml → old/coloreg_colon_polyp.yaml} +0 -0
  383. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_cardiovascular.yaml +0 -0
  384. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_classification_choice_cardiovascular.yaml +0 -0
  385. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_hepatology.yaml +0 -0
  386. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_misc.yaml +0 -0
  387. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_renal.yaml +0 -0
  388. /endoreg_db/data/{_examples/requirement → requirement/old}/endoscopy_bleeding_risk.yaml +0 -0
  389. /endoreg_db/data/{_examples/requirement → requirement/old}/event_cardiology.yaml +0 -0
  390. /endoreg_db/data/{_examples/requirement → requirement/old}/event_requirements.yaml +0 -0
  391. /endoreg_db/data/{_examples/requirement → requirement/old}/finding_colon_polyp.yaml +0 -0
  392. /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
  393. /endoreg_db/data/{_examples/requirement → requirement/old}/lab_value.yaml +0 -0
  394. /endoreg_db/data/{_examples/requirement → requirement/old}/medication.yaml +0 -0
  395. /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/age.yaml +0 -0
  396. /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/lab_operators.yaml +0 -0
  397. /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/model_operators.yaml +0 -0
  398. /endoreg_db/{urls/sensitive_meta.py → import_files/pseudonymization/__init__.py} +0 -0
  399. /endoreg_db/{views/pdf/pdf_stream_views.py → import_files/pseudonymization/pseudonymize.py} +0 -0
  400. /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
  401. /endoreg_db/utils/requirement_operator_logic/{model_evaluators.py → _old/model_evaluators.py} +0 -0
  402. {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
@@ -80,7 +80,9 @@ def parse_any_date(s: str) -> Optional[date]:
80
80
  # Try dateparser with German locale preference
81
81
  import dateparser
82
82
 
83
- dt = dateparser.parse(s, settings={"DATE_ORDER": "DMY", "PREFER_DAY_OF_MONTH": "first"})
83
+ dt = dateparser.parse(
84
+ s, settings={"DATE_ORDER": "DMY", "PREFER_DAY_OF_MONTH": "first"}
85
+ )
84
86
  return dt.date() if dt else None
85
87
  except Exception as e:
86
88
  logger.debug(f"Dateparser fallback failed for '{s}': {e}")
@@ -173,7 +175,9 @@ def calculate_patient_hash(instance: "SensitiveMeta", salt: str = SECRET_SALT) -
173
175
  return sha256(hash_str.encode()).hexdigest()
174
176
 
175
177
 
176
- def calculate_examination_hash(instance: "SensitiveMeta", salt: str = SECRET_SALT) -> str:
178
+ def calculate_examination_hash(
179
+ instance: "SensitiveMeta", salt: str = SECRET_SALT
180
+ ) -> str:
177
181
  """Calculates the examination hash for the instance."""
178
182
  dob = instance.patient_dob
179
183
  first_name = instance.patient_first_name
@@ -211,17 +215,25 @@ def create_pseudo_examiner_logic(instance: "SensitiveMeta") -> "Examiner":
211
215
  center = instance.center # Should be set before calling save
212
216
 
213
217
  if not first_name or not last_name or not center:
214
- logger.warning(f"Incomplete examiner info for SensitiveMeta (pk={instance.pk or 'new'}). Using default examiner.")
218
+ logger.warning(
219
+ f"Incomplete examiner info for SensitiveMeta (pk={instance.pk or 'new'}). Using default examiner."
220
+ )
215
221
  # Ensure default center exists or handle appropriately
216
222
  try:
217
223
  default_center = Center.objects.get(name="endoreg_db_demo")
218
224
  except Center.DoesNotExist:
219
- logger.error("Default center 'endoreg_db_demo' not found. Cannot create default examiner.")
225
+ logger.error(
226
+ "Default center 'endoreg_db_demo' not found. Cannot create default examiner."
227
+ )
220
228
  raise ValueError("Default center 'endoreg_db_demo' not found.")
221
229
 
222
- examiner, _created = Examiner.custom_get_or_create(first_name="Unknown", last_name="Unknown", center=default_center)
230
+ examiner, _created = Examiner.custom_get_or_create(
231
+ first_name="Unknown", last_name="Unknown", center=default_center
232
+ )
223
233
  else:
224
- examiner, _created = Examiner.custom_get_or_create(first_name=first_name, last_name=last_name, center=center)
234
+ examiner, _created = Examiner.custom_get_or_create(
235
+ first_name=first_name, last_name=last_name, center=center
236
+ )
225
237
 
226
238
  return examiner
227
239
 
@@ -267,11 +279,13 @@ def get_or_create_pseudo_patient_examination_logic(
267
279
  pseudo_patient, _created = get_or_create_pseudo_patient_logic(instance)
268
280
  instance.pseudo_patient = pseudo_patient # Assign FK directly
269
281
 
270
- patient_examination, _created = PatientExamination.get_or_create_pseudo_patient_examination_by_hash(
271
- patient_hash=instance.patient_hash,
272
- examination_hash=instance.examination_hash,
273
- # Optionally pass pseudo_patient if the method requires it
274
- # pseudo_patient=instance.pseudo_patient
282
+ patient_examination, _created = (
283
+ PatientExamination.get_or_create_pseudo_patient_examination_by_hash(
284
+ patient_hash=instance.patient_hash,
285
+ examination_hash=instance.examination_hash,
286
+ # Optionally pass pseudo_patient if the method requires it
287
+ # pseudo_patient=instance.pseudo_patient
288
+ )
275
289
  )
276
290
  return patient_examination, _created
277
291
 
@@ -285,7 +299,7 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
285
299
  This function is called on every save() operation and implements a two-phase approach:
286
300
 
287
301
  **Phase 1: Initial Creation (with defaults)**
288
- - When a SensitiveMeta is first created (e.g., via get_or_create_sensitive_meta()),
302
+ - When a SensitiveMeta is first created (e.g., via create_from_dict),
289
303
  it may have missing patient data (names, DOB, etc.)
290
304
  - Default values are set to prevent hash calculation errors:
291
305
  * patient_first_name: "unknown"
@@ -334,10 +348,14 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
334
348
 
335
349
  # 1. Ensure DOB and Examination Date exist
336
350
  if not instance.patient_dob:
337
- logger.debug(f"SensitiveMeta (pk={instance.pk or 'new'}): Patient DOB missing, generating random.")
351
+ logger.debug(
352
+ f"SensitiveMeta (pk={instance.pk or 'new'}): Patient DOB missing, generating random."
353
+ )
338
354
  instance.patient_dob = generate_random_dob()
339
355
  if not instance.examination_date:
340
- logger.debug(f"SensitiveMeta (pk={instance.pk or 'new'}): Examination date missing, generating random.")
356
+ logger.debug(
357
+ f"SensitiveMeta (pk={instance.pk or 'new'}): Examination date missing, generating random."
358
+ )
341
359
  instance.examination_date = generate_random_examination_date()
342
360
 
343
361
  # 2. Ensure Center exists (should be set before calling save)
@@ -389,7 +407,9 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
389
407
  first_name = instance.patient_first_name
390
408
  gender_str = guess_name_gender(first_name)
391
409
  if not gender_str:
392
- raise ValueError("Patient gender could not be determined and must be set before saving.")
410
+ raise ValueError(
411
+ "Patient gender could not be determined and must be set before saving."
412
+ )
393
413
  # Convert string to Gender object
394
414
  try:
395
415
  gender_obj = Gender.objects.get(name=gender_str)
@@ -416,7 +436,9 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
416
436
 
417
437
  # 6. Get or Create Pseudo Examination (depends on hashes)
418
438
  # Assign directly to the FK field
419
- pseudo_examination, _created = get_or_create_pseudo_patient_examination_logic(instance)
439
+ pseudo_examination, _created = get_or_create_pseudo_patient_examination_logic(
440
+ instance
441
+ )
420
442
  instance.pseudo_examination = pseudo_examination
421
443
 
422
444
  # 7. Get or Create Pseudo Examiner (depends on names, center)
@@ -430,7 +452,9 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
430
452
  return examiner_instance
431
453
 
432
454
 
433
- def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str, Any]) -> "SensitiveMeta":
455
+ def create_sensitive_meta_from_dict(
456
+ cls: Type["SensitiveMeta"], data: Dict[str, Any]
457
+ ) -> "SensitiveMeta":
434
458
  """
435
459
  Create a SensitiveMeta instance from a dictionary.
436
460
 
@@ -479,7 +503,11 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
479
503
  ValueError: If center_name does not match any Center in database
480
504
  """
481
505
 
482
- field_names = {f.name for f in cls._meta.get_fields() if not f.is_relation or f.one_to_one or (f.many_to_one and f.related_model)}
506
+ field_names = {
507
+ f.name
508
+ for f in cls._meta.get_fields()
509
+ if not f.is_relation or f.one_to_one or (f.many_to_one and f.related_model)
510
+ }
483
511
  selected_data = {k: v for k, v in data.items() if k in field_names}
484
512
 
485
513
  # --- Convert patient_dob if it's a date object ---
@@ -506,9 +534,13 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
506
534
  try:
507
535
  import dateparser
508
536
 
509
- parsed_dob = dateparser.parse(dob, languages=["de"], settings={"DATE_ORDER": "DMY"})
537
+ parsed_dob = dateparser.parse(
538
+ dob, languages=["de"], settings={"DATE_ORDER": "DMY"}
539
+ )
510
540
  if parsed_dob:
511
- aware_dob = timezone.make_aware(parsed_dob.replace(hour=0, minute=0, second=0, microsecond=0))
541
+ aware_dob = timezone.make_aware(
542
+ parsed_dob.replace(hour=0, minute=0, second=0, microsecond=0)
543
+ )
512
544
  selected_data["patient_dob"] = aware_dob
513
545
  logger.debug(
514
546
  "Parsed string patient_dob '%s' to aware datetime: %s",
@@ -562,7 +594,9 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
562
594
  # Fall back to dateparser for complex formats
563
595
  import dateparser
564
596
 
565
- parsed_date = dateparser.parse(exam_date, languages=["de"], settings={"DATE_ORDER": "DMY"})
597
+ parsed_date = dateparser.parse(
598
+ exam_date, languages=["de"], settings={"DATE_ORDER": "DMY"}
599
+ )
566
600
  if parsed_date:
567
601
  selected_data["examination_date"] = parsed_date.date()
568
602
  logger.debug(
@@ -580,7 +614,9 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
580
614
  # Use dateparser for non-ISO formats
581
615
  import dateparser
582
616
 
583
- parsed_date = dateparser.parse(exam_date, languages=["de"], settings={"DATE_ORDER": "DMY"})
617
+ parsed_date = dateparser.parse(
618
+ exam_date, languages=["de"], settings={"DATE_ORDER": "DMY"}
619
+ )
584
620
  if parsed_date:
585
621
  selected_data["examination_date"] = parsed_date.date()
586
622
  logger.debug(
@@ -623,7 +659,9 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
623
659
  raise ValueError(f"Center with name '{center_name}' does not exist.")
624
660
  else:
625
661
  # Neither center nor center_name provided
626
- raise ValueError("Either 'center' (Center object) or 'center_name' (string) is required in data dictionary.")
662
+ raise ValueError(
663
+ "Either 'center' (Center object) or 'center_name' (string) is required in data dictionary."
664
+ )
627
665
 
628
666
  # Handle Names and Gender
629
667
  first_name = selected_data.get("patient_first_name") or DEFAULT_UNKNOWN
@@ -639,9 +677,13 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
639
677
  elif isinstance(patient_gender_input, str):
640
678
  # Input is a string (gender name)
641
679
  try:
642
- selected_data["patient_gender"] = Gender.objects.get(name=patient_gender_input)
680
+ selected_data["patient_gender"] = Gender.objects.get(
681
+ name=patient_gender_input
682
+ )
643
683
  except Gender.DoesNotExist:
644
- logger.warning(f"Gender with name '{patient_gender_input}' provided but not found. Attempting to guess or use default.")
684
+ logger.warning(
685
+ f"Gender with name '{patient_gender_input}' provided but not found. Attempting to guess or use default."
686
+ )
645
687
  # Fall through to guessing logic if provided string name is invalid
646
688
  normalized = (patient_gender_input or "").lower()
647
689
  if normalized in {"male", "female", "unknown"}:
@@ -656,13 +698,19 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
656
698
  else:
657
699
  patient_gender_input = None # Reset to trigger guessing
658
700
 
659
- if not isinstance(selected_data.get("patient_gender"), Gender): # If not already a Gender object (e.g. was None, or string lookup failed)
701
+ if not isinstance(
702
+ selected_data.get("patient_gender"), Gender
703
+ ): # If not already a Gender object (e.g. was None, or string lookup failed)
660
704
  gender_name_to_use = guess_name_gender(first_name)
661
705
  if not gender_name_to_use:
662
- logger.warning(f"Could not guess gender for name '{first_name}'. Setting Gender to unknown.")
706
+ logger.warning(
707
+ f"Could not guess gender for name '{first_name}'. Setting Gender to unknown."
708
+ )
663
709
  gender_name_to_use = "unknown"
664
710
  try:
665
- selected_data["patient_gender"] = Gender.objects.get(name=gender_name_to_use)
711
+ selected_data["patient_gender"] = Gender.objects.get(
712
+ name=gender_name_to_use
713
+ )
666
714
  except Gender.DoesNotExist:
667
715
  gender_obj, _ = Gender.objects.get_or_create(
668
716
  name=gender_name_to_use,
@@ -700,7 +748,9 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
700
748
  selected_data["examination_time"] = exam_time.time()
701
749
  elif isinstance(exam_time, date):
702
750
  # no time info — ignore
703
- logger.debug(f"examination_time value {exam_time} has no time component; skipping")
751
+ logger.debug(
752
+ f"examination_time value {exam_time} has no time component; skipping"
753
+ )
704
754
  else:
705
755
  selected_data["examination_time"] = exam_time
706
756
  except Exception as e:
@@ -709,10 +759,16 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
709
759
  anonymized_text = data.get("anonymized_text") or data.get("anonym_text")
710
760
  if anonymized_text:
711
761
  if isinstance(anonymized_text, (str, bytes)):
712
- selected_data["anonymized_text"] = anonymized_text.decode() if isinstance(anonymized_text, bytes) else anonymized_text
762
+ selected_data["anonymized_text"] = (
763
+ anonymized_text.decode()
764
+ if isinstance(anonymized_text, bytes)
765
+ else anonymized_text
766
+ )
713
767
  else:
714
768
  selected_data["anonymized_text"] = str(anonymized_text)
715
- logger.debug("Set anonymized_text (length=%d)", len(selected_data["anonymized_text"]))
769
+ logger.debug(
770
+ "Set anonymized_text (length=%d)", len(selected_data["anonymized_text"])
771
+ )
716
772
 
717
773
  # Update name DB
718
774
  update_name_db(first_name, last_name)
@@ -726,13 +782,15 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
726
782
  return sensitive_meta
727
783
 
728
784
 
729
- def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, Any]) -> "SensitiveMeta":
785
+ def update_sensitive_meta_from_dict(
786
+ instance: "SensitiveMeta", data: Dict[str, Any]
787
+ ) -> "SensitiveMeta":
730
788
  """
731
789
  Updates a SensitiveMeta instance from a dictionary of new values.
732
790
 
733
791
  **Integration with two-phase save pattern:**
734
792
  This function is typically called after initial SensitiveMeta creation when real
735
- patient data becomes available (e.g., extracted from video OCR, PDF parsing, or
793
+ patient data becomes available (e.g., extracted from video OCR, report parsing, or
736
794
  manual annotation).
737
795
 
738
796
  **Example workflow:**
@@ -771,10 +829,16 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
771
829
  Raises:
772
830
  Exception: If save fails or required conversions fail
773
831
  """
774
- field_names = {f.name for f in instance._meta.get_fields() if not f.is_relation or f.one_to_one or (f.many_to_one and f.related_model)}
832
+ field_names = {
833
+ f.name
834
+ for f in instance._meta.get_fields()
835
+ if not f.is_relation or f.one_to_one or (f.many_to_one and f.related_model)
836
+ }
775
837
  # Exclude FKs that should not be updated directly from dict keys (handled separately or via save logic)
776
838
  excluded_fields = {"pseudo_patient", "pseudo_examination"}
777
- selected_data = {k: v for k, v in data.items() if k in field_names and k not in excluded_fields}
839
+ selected_data = {
840
+ k: v for k, v in data.items() if k in field_names and k not in excluded_fields
841
+ }
778
842
 
779
843
  # Handle potential Center update - accept both center_name (string) and center (object)
780
844
  from ..administration import Center
@@ -788,7 +852,9 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
788
852
  instance.center = center
789
853
  logger.debug(f"Updated center from Center object: {center.name}")
790
854
  else:
791
- logger.warning(f"Invalid center type {type(center)}, expected Center instance. Ignoring.")
855
+ logger.warning(
856
+ f"Invalid center type {type(center)}, expected Center instance. Ignoring."
857
+ )
792
858
  # Remove from selected_data to prevent override
793
859
  selected_data.pop("center", None)
794
860
  elif center_name:
@@ -798,7 +864,9 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
798
864
  instance.center = center_obj
799
865
  logger.debug(f"Updated center from center_name string: {center_name}")
800
866
  except Center.DoesNotExist:
801
- logger.warning(f"Center '{center_name}' not found during update. Keeping existing center.")
867
+ logger.warning(
868
+ f"Center '{center_name}' not found during update. Keeping existing center."
869
+ )
802
870
  else:
803
871
  # Both are None/missing - remove 'center' from selected_data to preserve existing value
804
872
  selected_data.pop("center", None)
@@ -821,10 +889,14 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
821
889
  elif isinstance(patient_gender_input, str):
822
890
  gender_input_clean = patient_gender_input.strip()
823
891
  # Try direct case-insensitive DB lookup first
824
- gender_obj = Gender.objects.filter(name__iexact=gender_input_clean).first()
892
+ gender_obj = Gender.objects.filter(
893
+ name__iexact=gender_input_clean
894
+ ).first()
825
895
  if gender_obj:
826
896
  selected_data["patient_gender"] = gender_obj
827
- logger.debug(f"Successfully matched gender string '{patient_gender_input}' to Gender object via iexact lookup")
897
+ logger.debug(
898
+ f"Successfully matched gender string '{patient_gender_input}' to Gender object via iexact lookup"
899
+ )
828
900
  else:
829
901
  # Use mapping helper for fallback
830
902
  mapped = _map_gender_string_to_standard(gender_input_clean)
@@ -832,40 +904,70 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
832
904
  gender_obj = Gender.objects.filter(name__iexact=mapped).first()
833
905
  if gender_obj:
834
906
  selected_data["patient_gender"] = gender_obj
835
- logger.info(f"Mapped gender '{patient_gender_input}' to '{mapped}' via fallback mapping")
907
+ logger.info(
908
+ f"Mapped gender '{patient_gender_input}' to '{mapped}' via fallback mapping"
909
+ )
836
910
  else:
837
- logger.warning(f"Mapped gender '{patient_gender_input}' to '{mapped}', but no such Gender in DB. Trying 'unknown'.")
838
- unknown_gender = Gender.objects.filter(name__iexact="unknown").first()
911
+ logger.warning(
912
+ f"Mapped gender '{patient_gender_input}' to '{mapped}', but no such Gender in DB. Trying 'unknown'."
913
+ )
914
+ unknown_gender = Gender.objects.filter(
915
+ name__iexact="unknown"
916
+ ).first()
839
917
  if unknown_gender:
840
918
  selected_data["patient_gender"] = unknown_gender
841
- logger.warning(f"Using 'unknown' gender as fallback for '{patient_gender_input}'")
919
+ logger.warning(
920
+ f"Using 'unknown' gender as fallback for '{patient_gender_input}'"
921
+ )
842
922
  else:
843
- logger.error(f"No 'unknown' gender found in database. Cannot handle gender '{patient_gender_input}'. Skipping gender update.")
923
+ logger.error(
924
+ f"No 'unknown' gender found in database. Cannot handle gender '{patient_gender_input}'. Skipping gender update."
925
+ )
844
926
  selected_data.pop("patient_gender", None)
845
927
  else:
846
928
  # Last resort: try to get 'unknown' gender
847
- unknown_gender = Gender.objects.filter(name__iexact="unknown").first()
929
+ unknown_gender = Gender.objects.filter(
930
+ name__iexact="unknown"
931
+ ).first()
848
932
  if unknown_gender:
849
933
  selected_data["patient_gender"] = unknown_gender
850
- logger.warning(f"Using 'unknown' gender as fallback for '{patient_gender_input}' (no mapping)")
934
+ logger.warning(
935
+ f"Using 'unknown' gender as fallback for '{patient_gender_input}' (no mapping)"
936
+ )
851
937
  else:
852
- logger.error(f"No 'unknown' gender found in database. Cannot handle gender '{patient_gender_input}'. Skipping gender update.")
938
+ logger.error(
939
+ f"No 'unknown' gender found in database. Cannot handle gender '{patient_gender_input}'. Skipping gender update."
940
+ )
853
941
  selected_data.pop("patient_gender", None)
854
942
  else:
855
- logger.warning(f"Unexpected patient_gender type {type(patient_gender_input)}: {patient_gender_input}. Skipping gender update.")
943
+ logger.warning(
944
+ f"Unexpected patient_gender type {type(patient_gender_input)}: {patient_gender_input}. Skipping gender update."
945
+ )
856
946
  selected_data.pop("patient_gender", None)
857
947
  except Exception as e:
858
- logger.exception(f"Error handling patient_gender '{patient_gender_input}': {e}. Skipping gender update.")
948
+ logger.exception(
949
+ f"Error handling patient_gender '{patient_gender_input}': {e}. Skipping gender update."
950
+ )
859
951
  selected_data.pop("patient_gender", None)
860
952
 
861
953
  # TODO Review: Handle new optional fields on update
862
- for key in ("file_path", "casenumber", "examination_time", "anonymized_text", "anonym_text"):
954
+ for key in (
955
+ "file_path",
956
+ "casenumber",
957
+ "examination_time",
958
+ "anonymized_text",
959
+ "anonym_text",
960
+ ):
863
961
  if key in data and data[key] is not None:
864
962
  val = data[key]
865
963
  if key in ("file_path", "casenumber"):
866
964
  setattr(instance, key, str(val))
867
965
  elif key in ("anonymized_text", "anonym_text"):
868
- setattr(instance, "anonymized_text", val if isinstance(val, str) else str(val))
966
+ setattr(
967
+ instance,
968
+ "anonymized_text",
969
+ val if isinstance(val, str) else str(val),
970
+ )
869
971
  elif key == "examination_time":
870
972
  try:
871
973
  from datetime import time as dt_time
@@ -897,69 +999,37 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
897
999
  value_to_set = v
898
1000
  if k == "patient_dob":
899
1001
  if isinstance(v, date) and not isinstance(v, datetime):
900
- aware_dob = timezone.make_aware(datetime.combine(v, datetime.min.time()))
1002
+ aware_dob = timezone.make_aware(
1003
+ datetime.combine(v, datetime.min.time())
1004
+ )
901
1005
  value_to_set = aware_dob
902
1006
  logger.debug(
903
1007
  "Converted patient_dob from date to aware datetime during update: %s",
904
1008
  aware_dob,
905
1009
  )
906
1010
  elif isinstance(v, str):
907
- # Handle string DOB - check if it's a field name or actual date
908
- if v == "patient_dob" or v in [
909
- "patient_first_name",
910
- "patient_last_name",
911
- "examination_date",
912
- ]:
913
- logger.warning(
914
- "Skipping invalid patient_dob value '%s' during update - appears to be field name",
1011
+ parsed = parse_any_date(v)
1012
+ if parsed:
1013
+ aware_dob = timezone.make_aware(
1014
+ datetime.combine(parsed, datetime.min.time())
1015
+ )
1016
+ value_to_set = aware_dob
1017
+ logger.debug(
1018
+ "Parsed string patient_dob '%s' during update to aware datetime: %s",
915
1019
  v,
1020
+ aware_dob,
916
1021
  )
917
- continue # Skip this field
918
1022
  else:
919
- # Try to parse as date string
920
- try:
921
- import dateparser
922
-
923
- parsed_dob = dateparser.parse(v, languages=["de"], settings={"DATE_ORDER": "DMY"})
924
- if parsed_dob:
925
- value_to_set = timezone.make_aware(parsed_dob.replace(hour=0, minute=0, second=0, microsecond=0))
926
- logger.debug(
927
- "Parsed string patient_dob '%s' during update to aware datetime: %s",
928
- v,
929
- value_to_set,
930
- )
931
- else:
932
- logger.warning(
933
- "Could not parse patient_dob string '%s' during update, skipping",
934
- v,
935
- )
936
- continue
937
- except Exception as e:
938
- logger.warning(
939
- "Error parsing patient_dob string '%s' during update: %s, skipping",
940
- v,
941
- e,
942
- )
943
- continue
944
- elif k == "examination_date" and isinstance(v, str):
945
- if v == "examination_date" or v in [
946
- "patient_first_name",
947
- "patient_last_name",
948
- "patient_dob",
949
- ]:
950
- logger.warning(
951
- "Skipping invalid examination_date value '%s' during update - appears to be field name",
952
- v,
953
- )
954
- continue
955
- else:
956
- # Try to parse as date string
957
- try:
958
- import dateparser
959
-
960
- parsed_date = dateparser.parse(v, languages=["de"], settings={"DATE_ORDER": "DMY"})
961
- if parsed_date:
962
- value_to_set = parsed_date.date()
1023
+ logger.warning(
1024
+ "Could not parse patient_dob string '%s' during update, skipping",
1025
+ v,
1026
+ )
1027
+ continue
1028
+ elif k == "examination_date":
1029
+ if isinstance(v, str):
1030
+ parsed = parse_any_date(v)
1031
+ if parsed:
1032
+ value_to_set = parsed # field is DateField, so keep it as date
963
1033
  logger.debug(
964
1034
  "Parsed string examination_date '%s' during update to date: %s",
965
1035
  v,
@@ -971,23 +1041,24 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
971
1041
  v,
972
1042
  )
973
1043
  continue
974
- except Exception as e:
975
- logger.warning(
976
- "Error parsing examination_date string '%s' during update: %s, skipping",
977
- v,
978
- e,
979
- )
980
- continue
981
- # --- End Conversion ---
1044
+ elif isinstance(v, date):
1045
+ value_to_set = v
1046
+
1047
+ # --- End Conversion ---
982
1048
 
983
- # Check if patient name is changing
984
- if k in ["patient_first_name", "patient_last_name"] and getattr(instance, k) != value_to_set:
985
- patient_name_changed = True
1049
+ # Check if patient name is changing
1050
+ if (
1051
+ k in ["patient_first_name", "patient_last_name"]
1052
+ and getattr(instance, k) != value_to_set
1053
+ ):
1054
+ patient_name_changed = True
986
1055
 
987
- setattr(instance, k, value_to_set) # Use value_to_set
1056
+ setattr(instance, k, value_to_set) # Use value_to_set
988
1057
 
989
1058
  except Exception as e:
990
- logger.error(f"Error setting attribute '{k}' to '{v}': {e}. Skipping this field.")
1059
+ logger.error(
1060
+ f"Error setting attribute '{k}' to '{v}': {e}. Skipping this field."
1061
+ )
991
1062
  continue
992
1063
 
993
1064
  # Update name DB if patient names changed
@@ -1041,19 +1112,24 @@ def _map_gender_string_to_standard(gender_str: str) -> Optional[str]:
1041
1112
  return standard
1042
1113
  return None
1043
1114
 
1044
- def _create_anonymized_record(instance: "SensitiveMeta", DEFAULT_ANONYMIZED=None, DEFAULT_ANONYMIZED_DATE=timezone.make_aware(datetime(1900, 1, 1))) -> None:
1115
+
1116
+ def _create_anonymized_record(
1117
+ instance: "SensitiveMeta",
1118
+ DEFAULT_ANONYMIZED=None,
1119
+ DEFAULT_ANONYMIZED_DATE=timezone.make_aware(datetime(1900, 1, 1)),
1120
+ ) -> None:
1045
1121
  """
1046
1122
  Create a SensitiveMeta instance with all sensitive fields set to anonymized defaults.
1047
1123
  This is only called after anonymization and will delete all data that can identify a patient from the database.
1048
1124
  What is left will only be the patient hash.
1049
-
1125
+
1050
1126
  Args:
1051
1127
  instance: The existing SensitiveMeta instance to anonymize
1052
1128
  DEFAULT_ANONYMIZED: Usually None, The default string to use for anonymized fields (e.g., "anonymized,")
1053
1129
  """
1054
-
1130
+
1055
1131
  instance.refresh_from_db()
1056
- instance.get_patient_hash()
1132
+ instance.get_patient_hash()
1057
1133
  instance.get_patient_examination_hash()
1058
1134
 
1059
1135
  anonymized_data = {
@@ -1063,5 +1139,5 @@ def _create_anonymized_record(instance: "SensitiveMeta", DEFAULT_ANONYMIZED=None
1063
1139
  "examination_date": DEFAULT_ANONYMIZED_DATE,
1064
1140
  }
1065
1141
  sensitive_meta = update_sensitive_meta_from_dict(instance, anonymized_data)
1066
-
1067
- sensitive_meta.save()
1142
+
1143
+ sensitive_meta.save()
@@ -2,9 +2,6 @@ from typing import TYPE_CHECKING
2
2
 
3
3
  from django.db import models
4
4
 
5
- if TYPE_CHECKING:
6
- from endoreg_db.models import InformationSourceType
7
-
8
5
 
9
6
  def get_prediction_information_source():
10
7
  """
@@ -47,9 +44,33 @@ class InformationSource(models.Model):
47
44
  abbreviation = models.CharField(max_length=100, blank=True, null=True, unique=True)
48
45
 
49
46
  if TYPE_CHECKING:
50
- # information_source_types: models.QuerySet["InformationSourceType"]
51
- # Avoid self-referential import; use forward references instead
52
- pass
47
+ from endoreg_db.models import (
48
+ Examination,
49
+ ExaminationIndication,
50
+ ExaminationTime,
51
+ Finding,
52
+ FindingClassification,
53
+ FindingIntervention,
54
+ InformationSourceType,
55
+ )
56
+
57
+ @property
58
+ def examinations(self) -> "models.manager.RelatedManager[Examination]": ...
59
+
60
+ @property
61
+ def examination_indications(self) -> "models.manager.RelatedManager[ExaminationIndication]": ...
62
+
63
+ @property
64
+ def examination_times(self) -> "models.manager.RelatedManager[ExaminationTime]": ...
65
+
66
+ @property
67
+ def findings(self) -> "models.manager.RelatedManager[Finding]": ...
68
+
69
+ @property
70
+ def finding_interventions(self) -> "models.manager.RelatedManager[FindingIntervention]": ...
71
+
72
+ @property
73
+ def finding_classifications(self) -> "models.manager.RelatedManager[FindingClassification]": ...
53
74
 
54
75
  class Meta:
55
76
  verbose_name = "Information Source"
File without changes
File without changes
@@ -0,0 +1,6 @@
1
+ from django.db import models
2
+
3
+
4
+ class Report(models.Model):
5
+ name = models.CharField(max_length=100, unique=True)
6
+