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

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

Potentially problematic release.


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

Files changed (503) hide show
  1. endoreg_db/authz/auth.py +74 -0
  2. endoreg_db/authz/backends.py +168 -0
  3. endoreg_db/authz/management/commands/list_routes.py +18 -0
  4. endoreg_db/authz/middleware.py +83 -0
  5. endoreg_db/authz/permissions.py +127 -0
  6. endoreg_db/authz/policy.py +218 -0
  7. endoreg_db/authz/views_auth.py +66 -0
  8. endoreg_db/config/env.py +13 -8
  9. endoreg_db/data/__init__.py +2 -11
  10. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +3 -3
  11. endoreg_db/data/event_classification/data.yaml +4 -0
  12. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  13. endoreg_db/data/examination/examinations/data.yaml +114 -14
  14. endoreg_db/data/examination/time-type/data.yaml +0 -3
  15. endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
  16. endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
  17. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
  18. endoreg_db/data/finding/00_generic.yaml +35 -0
  19. endoreg_db/data/finding/00_generic_complication.yaml +9 -0
  20. endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
  21. endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
  22. endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
  23. endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
  24. endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
  25. endoreg_db/data/finding_classification/00_generic.yaml +44 -0
  26. endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
  27. endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
  28. endoreg_db/data/finding_classification/02_colonoscopy_baseline.yaml +83 -0
  29. endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
  30. endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
  31. endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
  32. endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
  33. endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
  34. endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
  35. endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
  36. endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
  37. endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
  38. endoreg_db/data/finding_classification_choice/{colonoscopy_not_complete_reason.yaml → 02_colonoscopy_generic.yaml} +1 -1
  39. endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
  40. endoreg_db/data/finding_classification_choice/{colonoscopy_location.yaml → 02_colonoscopy_location.yaml} +23 -4
  41. endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
  42. endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
  43. endoreg_db/data/finding_classification_choice/{colon_lesion_paris.yaml → 02_colonoscopy_polyp_morphology.yaml} +26 -8
  44. endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
  45. endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
  46. endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
  47. endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
  48. endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
  49. endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
  50. endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
  51. endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
  52. endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
  53. endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
  54. endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
  55. endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
  56. endoreg_db/data/finding_type/data.yaml +8 -12
  57. endoreg_db/data/requirement/01_patient_data.yaml +93 -0
  58. endoreg_db/data/requirement/old/colon_polyp_intervention.yaml +49 -0
  59. endoreg_db/data/requirement/old/coloreg_colon_polyp.yaml +49 -0
  60. endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
  61. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +29 -12
  62. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  63. endoreg_db/data/requirement_set/{endoscopy_bleeding_risk.yaml → 02_endoscopy_bleeding_risk.yaml} +0 -6
  64. endoreg_db/data/requirement_set/90_coloreg.yaml +190 -0
  65. endoreg_db/data/requirement_set/_old_ +109 -0
  66. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  67. endoreg_db/data/setup_config.yaml +4 -4
  68. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  69. endoreg_db/exceptions.py +4 -2
  70. endoreg_db/forms/examination_form.py +1 -1
  71. endoreg_db/helpers/data_loader.py +125 -53
  72. endoreg_db/helpers/default_objects.py +116 -81
  73. endoreg_db/import_files/__init__.py +27 -0
  74. endoreg_db/import_files/context/__init__.py +7 -0
  75. endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
  76. endoreg_db/import_files/context/ensure_center.py +17 -0
  77. endoreg_db/import_files/context/file_lock.py +66 -0
  78. endoreg_db/import_files/context/import_context.py +43 -0
  79. endoreg_db/import_files/context/validate_directories.py +56 -0
  80. endoreg_db/import_files/file_storage/__init__.py +15 -0
  81. endoreg_db/import_files/file_storage/create_report_file.py +76 -0
  82. endoreg_db/import_files/file_storage/create_video_file.py +75 -0
  83. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
  84. endoreg_db/import_files/file_storage/state_management.py +400 -0
  85. endoreg_db/import_files/file_storage/storage.py +36 -0
  86. endoreg_db/import_files/import_service.md +26 -0
  87. endoreg_db/import_files/processing/__init__.py +11 -0
  88. endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
  89. endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
  90. endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
  91. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
  92. endoreg_db/import_files/pseudonymization/fake.py +52 -0
  93. endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
  94. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
  95. endoreg_db/import_files/report_import_service.py +141 -0
  96. endoreg_db/import_files/video_import_service.py +150 -0
  97. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  98. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  99. endoreg_db/management/commands/import_report.py +130 -65
  100. endoreg_db/management/commands/import_video.py +9 -10
  101. endoreg_db/management/commands/import_video_with_classification.py +2 -2
  102. endoreg_db/management/commands/list_routes.py +18 -0
  103. endoreg_db/management/commands/load_ai_model_data.py +5 -5
  104. endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
  105. endoreg_db/management/commands/load_base_db_data.py +5 -134
  106. endoreg_db/management/commands/load_center_data.py +12 -12
  107. endoreg_db/management/commands/load_contraindication_data.py +14 -16
  108. endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
  109. endoreg_db/management/commands/load_disease_classification_data.py +15 -18
  110. endoreg_db/management/commands/load_disease_data.py +25 -28
  111. endoreg_db/management/commands/load_endoscope_data.py +20 -27
  112. endoreg_db/management/commands/load_event_data.py +14 -16
  113. endoreg_db/management/commands/load_examination_data.py +31 -44
  114. endoreg_db/management/commands/load_examination_indication_data.py +20 -21
  115. endoreg_db/management/commands/load_finding_data.py +52 -80
  116. endoreg_db/management/commands/load_information_source.py +21 -23
  117. endoreg_db/management/commands/load_lab_value_data.py +17 -26
  118. endoreg_db/management/commands/load_medication_data.py +13 -12
  119. endoreg_db/management/commands/load_organ_data.py +15 -19
  120. endoreg_db/management/commands/load_pdf_type_data.py +19 -18
  121. endoreg_db/management/commands/load_profession_data.py +14 -17
  122. endoreg_db/management/commands/load_qualification_data.py +20 -23
  123. endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
  124. endoreg_db/management/commands/load_requirement_data.py +62 -39
  125. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  126. endoreg_db/management/commands/load_risk_data.py +7 -6
  127. endoreg_db/management/commands/load_shift_data.py +20 -23
  128. endoreg_db/management/commands/load_tag_data.py +8 -11
  129. endoreg_db/management/commands/load_unit_data.py +17 -19
  130. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  131. endoreg_db/management/commands/start_filewatcher.py +46 -37
  132. endoreg_db/management/commands/storage_management.py +271 -203
  133. endoreg_db/management/commands/validate_video_files.py +1 -5
  134. endoreg_db/migrations/0001_initial.py +297 -250
  135. endoreg_db/models/__init__.py +78 -123
  136. endoreg_db/models/administration/__init__.py +21 -42
  137. endoreg_db/models/administration/ai/active_model.py +2 -2
  138. endoreg_db/models/administration/ai/ai_model.py +7 -6
  139. endoreg_db/models/administration/case/__init__.py +1 -15
  140. endoreg_db/models/administration/case/case.py +3 -3
  141. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  142. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  143. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  144. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  145. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  146. endoreg_db/models/administration/center/center.py +33 -19
  147. endoreg_db/models/administration/center/center_product.py +12 -9
  148. endoreg_db/models/administration/center/center_resource.py +25 -19
  149. endoreg_db/models/administration/center/center_shift.py +21 -17
  150. endoreg_db/models/administration/center/center_waste.py +16 -8
  151. endoreg_db/models/administration/person/__init__.py +2 -0
  152. endoreg_db/models/administration/person/employee/employee.py +10 -5
  153. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  154. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  155. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  156. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  157. endoreg_db/models/administration/person/patient/patient.py +129 -100
  158. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  159. endoreg_db/models/administration/person/person.py +4 -0
  160. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  161. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  162. endoreg_db/models/administration/product/product.py +20 -15
  163. endoreg_db/models/administration/product/product_material.py +17 -18
  164. endoreg_db/models/administration/product/product_weight.py +12 -8
  165. endoreg_db/models/administration/product/reference_product.py +23 -55
  166. endoreg_db/models/administration/qualification/qualification.py +7 -3
  167. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  168. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  169. endoreg_db/models/administration/shift/shift.py +16 -12
  170. endoreg_db/models/administration/shift/shift_type.py +23 -31
  171. endoreg_db/models/label/__init__.py +8 -9
  172. endoreg_db/models/label/annotation/image_classification.py +10 -9
  173. endoreg_db/models/label/annotation/video_segmentation_annotation.py +23 -28
  174. endoreg_db/models/label/label.py +15 -15
  175. endoreg_db/models/label/label_set.py +19 -6
  176. endoreg_db/models/label/label_type.py +1 -1
  177. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  178. endoreg_db/models/label/label_video_segment/label_video_segment.py +98 -102
  179. endoreg_db/models/label/video_segmentation_label.py +4 -0
  180. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  181. endoreg_db/models/media/frame/frame.py +22 -22
  182. endoreg_db/models/media/pdf/raw_pdf.py +194 -194
  183. endoreg_db/models/media/pdf/report_file.py +25 -29
  184. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +55 -47
  185. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  186. endoreg_db/models/media/processing_history/__init__.py +5 -0
  187. endoreg_db/models/media/processing_history/processing_history.py +96 -0
  188. endoreg_db/models/media/video/__init__.py +1 -0
  189. endoreg_db/models/media/video/create_from_file.py +139 -77
  190. endoreg_db/models/media/video/pipe_2.py +8 -9
  191. endoreg_db/models/media/video/video_file.py +174 -112
  192. endoreg_db/models/media/video/video_file_ai.py +288 -74
  193. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  194. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  195. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  196. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  197. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  198. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  199. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  200. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  201. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  202. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  203. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  204. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  205. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  206. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  207. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  208. endoreg_db/models/media/video/video_file_io.py +113 -61
  209. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  210. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  211. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  212. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  213. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  214. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  215. endoreg_db/models/media/video/video_file_segments.py +24 -17
  216. endoreg_db/models/media/video/video_metadata.py +19 -35
  217. endoreg_db/models/media/video/video_processing.py +96 -95
  218. endoreg_db/models/medical/contraindication/README.md +1 -0
  219. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  220. endoreg_db/models/medical/disease.py +22 -16
  221. endoreg_db/models/medical/event.py +31 -18
  222. endoreg_db/models/medical/examination/__init__.py +13 -6
  223. endoreg_db/models/medical/examination/examination.py +39 -20
  224. endoreg_db/models/medical/examination/examination_indication.py +30 -95
  225. endoreg_db/models/medical/examination/examination_time.py +23 -8
  226. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  227. endoreg_db/models/medical/examination/examination_type.py +3 -4
  228. endoreg_db/models/medical/finding/finding.py +32 -40
  229. endoreg_db/models/medical/finding/finding_classification.py +42 -72
  230. endoreg_db/models/medical/finding/finding_intervention.py +25 -22
  231. endoreg_db/models/medical/finding/finding_type.py +13 -12
  232. endoreg_db/models/medical/hardware/endoscope.py +26 -26
  233. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  234. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  235. endoreg_db/models/medical/medication/medication.py +22 -10
  236. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  237. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  238. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  239. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  240. endoreg_db/models/medical/organ/__init__.py +15 -12
  241. endoreg_db/models/medical/patient/medication_examples.py +6 -6
  242. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  243. endoreg_db/models/medical/patient/patient_event.py +19 -22
  244. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  245. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  246. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  247. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  248. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  249. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  250. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  251. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  252. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  253. endoreg_db/models/medical/risk/risk.py +7 -6
  254. endoreg_db/models/medical/risk/risk_type.py +8 -5
  255. endoreg_db/models/metadata/model_meta.py +60 -29
  256. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  257. endoreg_db/models/metadata/pdf_meta.py +31 -24
  258. endoreg_db/models/metadata/sensitive_meta.py +105 -85
  259. endoreg_db/models/metadata/sensitive_meta_logic.py +198 -103
  260. endoreg_db/models/metadata/video_meta.py +51 -31
  261. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  262. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  263. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  264. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  265. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  266. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  267. endoreg_db/models/other/emission/emission_factor.py +18 -8
  268. endoreg_db/models/other/gender.py +10 -5
  269. endoreg_db/models/other/information_source.py +50 -29
  270. endoreg_db/models/other/material.py +9 -5
  271. endoreg_db/models/other/resource.py +6 -4
  272. endoreg_db/models/other/tag.py +10 -5
  273. endoreg_db/models/other/transport_route.py +13 -8
  274. endoreg_db/models/other/unit.py +10 -6
  275. endoreg_db/models/other/waste.py +6 -5
  276. endoreg_db/models/report/report.py +6 -0
  277. endoreg_db/models/requirement/requirement.py +329 -361
  278. endoreg_db/models/requirement/requirement_error.py +85 -0
  279. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  280. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  281. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  282. endoreg_db/models/requirement/requirement_operator.py +103 -112
  283. endoreg_db/models/requirement/requirement_set.py +74 -57
  284. endoreg_db/models/state/__init__.py +4 -4
  285. endoreg_db/models/state/abstract.py +2 -2
  286. endoreg_db/models/state/anonymization.py +12 -0
  287. endoreg_db/models/state/audit_ledger.py +49 -51
  288. endoreg_db/models/state/label_video_segment.py +9 -0
  289. endoreg_db/models/state/raw_pdf.py +101 -68
  290. endoreg_db/models/state/sensitive_meta.py +6 -2
  291. endoreg_db/models/state/video.py +110 -90
  292. endoreg_db/models/upload_job.py +35 -34
  293. endoreg_db/models/utils.py +28 -25
  294. endoreg_db/queries/__init__.py +3 -1
  295. endoreg_db/root_urls.py +21 -2
  296. endoreg_db/schemas/examination_evaluation.py +1 -1
  297. endoreg_db/serializers/__init__.py +2 -10
  298. endoreg_db/serializers/anonymization.py +18 -10
  299. endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
  300. endoreg_db/serializers/meta/__init__.py +1 -6
  301. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  302. endoreg_db/serializers/misc/file_overview.py +11 -99
  303. endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
  304. endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
  305. endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
  306. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  307. endoreg_db/serializers/video/segmentation.py +2 -1
  308. endoreg_db/serializers/video/video_file_list.py +65 -34
  309. endoreg_db/serializers/video/video_processing_history.py +20 -5
  310. endoreg_db/services/__old/pdf_import.py +1487 -0
  311. endoreg_db/services/__old/video_import.py +1306 -0
  312. endoreg_db/services/anonymization.py +128 -89
  313. endoreg_db/services/lookup_service.py +65 -52
  314. endoreg_db/services/lookup_store.py +2 -2
  315. endoreg_db/services/pdf_import.py +0 -1382
  316. endoreg_db/services/report_import.py +10 -0
  317. endoreg_db/services/video_import.py +6 -1255
  318. endoreg_db/tasks/upload_tasks.py +79 -70
  319. endoreg_db/tasks/video_ingest.py +8 -4
  320. endoreg_db/urls/__init__.py +5 -32
  321. endoreg_db/urls/ai.py +32 -0
  322. endoreg_db/urls/media.py +121 -83
  323. endoreg_db/urls/root_urls.py +29 -0
  324. endoreg_db/utils/__init__.py +15 -5
  325. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  326. endoreg_db/utils/case_generator/__init__.py +3 -0
  327. endoreg_db/utils/dataloader.py +142 -40
  328. endoreg_db/utils/defaults/set_default_center.py +32 -0
  329. endoreg_db/utils/names.py +22 -16
  330. endoreg_db/utils/paths.py +110 -46
  331. endoreg_db/utils/permissions.py +2 -1
  332. endoreg_db/utils/pipelines/Readme.md +1 -1
  333. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  334. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +655 -0
  335. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
  336. endoreg_db/utils/setup_config.py +8 -5
  337. endoreg_db/utils/storage.py +115 -0
  338. endoreg_db/utils/validate_endo_roi.py +8 -2
  339. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  340. endoreg_db/views/__init__.py +85 -183
  341. endoreg_db/views/ai/__init__.py +8 -0
  342. endoreg_db/views/ai/label.py +155 -0
  343. endoreg_db/views/anonymization/media_management.py +202 -166
  344. endoreg_db/views/anonymization/overview.py +99 -67
  345. endoreg_db/views/anonymization/validate.py +182 -44
  346. endoreg_db/views/media/__init__.py +7 -20
  347. endoreg_db/views/media/pdf_media.py +197 -174
  348. endoreg_db/views/media/sensitive_metadata.py +193 -138
  349. endoreg_db/views/media/video_media.py +89 -82
  350. endoreg_db/views/meta/__init__.py +0 -8
  351. endoreg_db/views/misc/__init__.py +1 -7
  352. endoreg_db/views/misc/upload_views.py +94 -93
  353. endoreg_db/views/patient/patient.py +5 -4
  354. endoreg_db/views/report/__init__.py +5 -7
  355. endoreg_db/views/{pdf → report}/reimport.py +22 -22
  356. endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +46 -39
  357. endoreg_db/views/requirement/evaluate.py +188 -187
  358. endoreg_db/views/requirement/lookup.py +17 -3
  359. endoreg_db/views/requirement/lookup_store.py +22 -90
  360. endoreg_db/views/requirement/requirement_utils.py +89 -0
  361. endoreg_db/views/video/__init__.py +23 -24
  362. endoreg_db/views/video/correction.py +201 -172
  363. endoreg_db/views/video/reimport.py +1 -1
  364. endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +77 -40
  365. endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
  366. endoreg_db/views/video/video_stream.py +7 -8
  367. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +7 -3
  368. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +391 -413
  369. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
  370. endoreg_db/data/finding/anatomy_colon.yaml +0 -128
  371. endoreg_db/data/finding/colonoscopy.yaml +0 -40
  372. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
  373. endoreg_db/data/finding/complication.yaml +0 -16
  374. endoreg_db/data/finding/data.yaml +0 -105
  375. endoreg_db/data/finding/examination_setting.yaml +0 -16
  376. endoreg_db/data/finding/medication_related.yaml +0 -18
  377. endoreg_db/data/finding/outcome.yaml +0 -12
  378. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +0 -95
  379. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
  380. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
  381. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  382. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  383. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -68
  384. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  385. endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -80
  386. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
  387. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
  388. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
  389. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
  390. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
  391. endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
  392. endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
  393. endoreg_db/data/finding_classification/histology_colo.yaml +0 -51
  394. endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
  395. endoreg_db/data/finding_classification/medication_related.yaml +0 -23
  396. endoreg_db/data/finding_classification/visualized.yaml +0 -33
  397. endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  398. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  399. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  400. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  401. endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  402. endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  403. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  404. endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  405. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
  406. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  407. endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
  408. endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
  409. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  410. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
  411. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
  412. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  413. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  414. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
  415. endoreg_db/data/requirement/age.yaml +0 -26
  416. endoreg_db/data/requirement/gender.yaml +0 -25
  417. endoreg_db/management/commands/init_default_ai_model.py +0 -112
  418. endoreg_db/management/commands/reset_celery_schedule.py +0 -9
  419. endoreg_db/management/commands/validate_video.py +0 -204
  420. endoreg_db/migrations/0002_add_video_correction_models.py +0 -52
  421. endoreg_db/migrations/0003_add_center_display_name.py +0 -30
  422. endoreg_db/models/administration/permissions/__init__.py +0 -44
  423. endoreg_db/models/rule/__init__.py +0 -13
  424. endoreg_db/models/rule/rule.py +0 -27
  425. endoreg_db/models/rule/rule_applicator.py +0 -224
  426. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  427. endoreg_db/models/rule/rule_type.py +0 -20
  428. endoreg_db/models/rule/ruleset.py +0 -17
  429. endoreg_db/renames.yml +0 -8
  430. endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
  431. endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
  432. endoreg_db/serializers/_old/video.py +0 -71
  433. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
  434. endoreg_db/serializers/meta/report_meta.py +0 -53
  435. endoreg_db/serializers/report/__init__.py +0 -9
  436. endoreg_db/serializers/report/mixins.py +0 -45
  437. endoreg_db/serializers/report/report.py +0 -105
  438. endoreg_db/serializers/report/report_list.py +0 -22
  439. endoreg_db/serializers/report/secure_file_url.py +0 -26
  440. endoreg_db/serializers/video/video_metadata.py +0 -105
  441. endoreg_db/services/requirements_object.py +0 -147
  442. endoreg_db/services/storage_aware_video_processor.py +0 -344
  443. endoreg_db/urls/files.py +0 -6
  444. endoreg_db/urls/label_video_segment_validate.py +0 -33
  445. endoreg_db/urls/label_video_segments.py +0 -46
  446. endoreg_db/urls/report.py +0 -48
  447. endoreg_db/urls/video.py +0 -61
  448. endoreg_db/utils/case_generator/case_generator.py +0 -159
  449. endoreg_db/utils/case_generator/utils.py +0 -30
  450. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +0 -368
  451. endoreg_db/views/label/__init__.py +0 -5
  452. endoreg_db/views/label/label.py +0 -15
  453. endoreg_db/views/label_video_segment/__init__.py +0 -16
  454. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
  455. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
  456. endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
  457. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
  458. endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
  459. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
  460. endoreg_db/views/label_video_segment/validate.py +0 -226
  461. endoreg_db/views/media/segments.py +0 -71
  462. endoreg_db/views/meta/available_files_list.py +0 -146
  463. endoreg_db/views/meta/report_meta.py +0 -53
  464. endoreg_db/views/meta/sensitive_meta_detail.py +0 -148
  465. endoreg_db/views/misc/secure_file_serving_view.py +0 -80
  466. endoreg_db/views/misc/secure_file_url_view.py +0 -84
  467. endoreg_db/views/misc/secure_url_validate.py +0 -79
  468. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
  469. endoreg_db/views/patient_finding_location/__init__.py +0 -5
  470. endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
  471. endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
  472. endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
  473. endoreg_db/views/pdf/__init__.py +0 -8
  474. endoreg_db/views/report/report_list.py +0 -112
  475. endoreg_db/views/report/report_with_secure_url.py +0 -28
  476. endoreg_db/views/report/start_examination.py +0 -7
  477. endoreg_db/views/video/segmentation.py +0 -274
  478. endoreg_db/views/video/task_status.py +0 -49
  479. endoreg_db/views/video/timeline.py +0 -46
  480. endoreg_db/views/video/video_analyze.py +0 -52
  481. endoreg_db/views.py +0 -0
  482. /endoreg_db/data/requirement/{colonoscopy_baseline_austria.yaml → old/colonoscopy_baseline_austria.yaml} +0 -0
  483. /endoreg_db/data/requirement/{disease_cardiovascular.yaml → old/disease_cardiovascular.yaml} +0 -0
  484. /endoreg_db/data/requirement/{disease_classification_choice_cardiovascular.yaml → old/disease_classification_choice_cardiovascular.yaml} +0 -0
  485. /endoreg_db/data/requirement/{disease_hepatology.yaml → old/disease_hepatology.yaml} +0 -0
  486. /endoreg_db/data/requirement/{disease_misc.yaml → old/disease_misc.yaml} +0 -0
  487. /endoreg_db/data/requirement/{disease_renal.yaml → old/disease_renal.yaml} +0 -0
  488. /endoreg_db/data/requirement/{endoscopy_bleeding_risk.yaml → old/endoscopy_bleeding_risk.yaml} +0 -0
  489. /endoreg_db/data/requirement/{event_cardiology.yaml → old/event_cardiology.yaml} +0 -0
  490. /endoreg_db/data/requirement/{event_requirements.yaml → old/event_requirements.yaml} +0 -0
  491. /endoreg_db/data/requirement/{finding_colon_polyp.yaml → old/finding_colon_polyp.yaml} +0 -0
  492. /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
  493. /endoreg_db/data/requirement/{lab_value.yaml → old/lab_value.yaml} +0 -0
  494. /endoreg_db/data/requirement/{medication.yaml → old/medication.yaml} +0 -0
  495. /endoreg_db/data/requirement_operator/{age.yaml → _old/age.yaml} +0 -0
  496. /endoreg_db/data/requirement_operator/{lab_operators.yaml → _old/lab_operators.yaml} +0 -0
  497. /endoreg_db/data/requirement_operator/{model_operators.yaml → _old/model_operators.yaml} +0 -0
  498. /endoreg_db/{models/media/video/refactor_plan.md → import_files/pseudonymization/__init__.py} +0 -0
  499. /endoreg_db/{models/media/video/video_file_frames.py → import_files/pseudonymization/pseudonymize.py} +0 -0
  500. /endoreg_db/models/{metadata/frame_ocr_result.py → report/__init__.py} +0 -0
  501. /endoreg_db/{urls/sensitive_meta.py → models/report/images.py} +0 -0
  502. /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
  503. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,58 +1,72 @@
1
1
  # endoreg_db/services/anonymization.py
2
+ import logging
2
3
  from pathlib import Path
4
+ from typing import Optional
5
+
3
6
  from django.db import transaction
4
- from django.conf import settings
5
- from endoreg_db.models import VideoFile, RawPdfFile
6
- from endoreg_db.services.video_import import VideoImportService
7
- from endoreg_db.services.pdf_import import PdfImportService
7
+
8
+ from endoreg_db.models import RawPdfFile, VideoFile
9
+ from endoreg_db.services.__old.video_import import VideoImportService
10
+ from endoreg_db.services.report_import import ReportImportService
8
11
  from endoreg_db.utils.paths import STORAGE_DIR
9
- import logging
12
+ from endoreg_db.utils.storage import ensure_local_file, file_exists
10
13
 
11
14
  logger = logging.getLogger(__name__)
12
15
 
16
+
13
17
  class AnonymizationService:
14
18
  """
15
19
  Orchestrates long‑running anonymization tasks so the view only
16
20
  does HTTP <-> Service translation.
17
21
  """
18
-
19
- def __init__(self, project_root: Path = None):
22
+
23
+ def __init__(self, project_root: Optional[Path] = None):
20
24
  """
21
25
  Initialize the AnonymizationService with service instances.
22
-
26
+
23
27
  Args:
24
28
  project_root: Path to the project root. If None, uses settings.BASE_DIR
25
29
  """
26
- if project_root is None:
27
- project_root = STORAGE_DIR
28
-
30
+ self.project_root: Path = project_root or STORAGE_DIR
29
31
  self.video_service = VideoImportService()
30
- self.pdf_service = PdfImportService()
32
+ self.pdf_service = ReportImportService()
31
33
 
32
34
  # ---------- READ ----------------------------------------------------
33
35
  @staticmethod
34
36
  def get_status(file_id: int):
35
37
  """
36
38
  Retrieve the anonymization status and media type for a file by its ID.
37
-
39
+
38
40
  Returns:
39
41
  dict or None: A dictionary containing the file's media type and anonymization status if found, or None if no matching file exists.
40
42
  """
41
- vf = VideoFile.objects.select_related("state", "sensitive_meta").filter(pk=file_id).first()
43
+ vf = (
44
+ VideoFile.objects.select_related("state", "sensitive_meta")
45
+ .filter(pk=file_id)
46
+ .first()
47
+ )
42
48
  if vf:
43
49
  return {
44
50
  "mediaType": "video",
45
- "anonymizationStatus": vf.state.anonymization_status if vf.state else "not_started",
46
- "fileExists": bool(vf.raw_file and hasattr(vf.raw_file, 'path')),
51
+ "anonymizationStatus": vf.state.anonymization_status
52
+ if vf.state
53
+ else "not_started",
54
+ "fileExists": file_exists(vf.raw_file),
47
55
  "uuid": str(vf.uuid) if vf.uuid else None,
48
56
  }
49
57
 
50
- pdf = RawPdfFile.objects.select_related("state", "sensitive_meta").filter(pk=file_id).first()
58
+ pdf = (
59
+ RawPdfFile.objects.select_related("state", "sensitive_meta")
60
+ .filter(pk=file_id)
61
+ .first()
62
+ )
51
63
  if pdf:
52
64
  return {
53
65
  "mediaType": "pdf",
54
- "anonymizationStatus": pdf.state.anonymization_status if pdf.state else "not_started",
55
- "fileExists": bool(pdf.file and hasattr(pdf.file, 'path')),
66
+ "anonymizationStatus": pdf.state.anonymization_status
67
+ if pdf.state
68
+ else "not_started",
69
+ "fileExists": file_exists(pdf.file),
56
70
  "hash": pdf.pdf_hash,
57
71
  }
58
72
  return None
@@ -62,116 +76,129 @@ class AnonymizationService:
62
76
  def start(self, file_id: int):
63
77
  """
64
78
  Start anonymization process for a file by its ID.
65
-
79
+
66
80
  Args:
67
81
  file_id: The ID of the file to anonymize
68
-
82
+
69
83
  Returns:
70
84
  str or None: Media type if successful, None if file not found
71
85
  """
72
86
  # Try VideoFile first
73
- vf = VideoFile.objects.select_related("state", "sensitive_meta", "center", "video_meta__processor").filter(pk=file_id).first()
87
+ vf = (
88
+ VideoFile.objects.select_related(
89
+ "state", "sensitive_meta", "center", "video_meta__processor"
90
+ )
91
+ .filter(pk=file_id)
92
+ .first()
93
+ )
74
94
  if vf:
75
95
  try:
76
96
  logger.info(f"Starting video anonymization for VideoFile ID: {file_id}")
77
-
97
+
78
98
  # Check if already processed
79
99
  if vf.state and vf.state.anonymized:
80
100
  logger.info(f"VideoFile {file_id} already anonymized, skipping")
81
101
  return "video"
82
-
102
+
83
103
  # Get file path
84
104
  file_path = vf.get_raw_file_path()
85
105
  if not file_path or not Path(file_path).exists():
86
- logger.error(f"Raw file not found for VideoFile {file_id}: {file_path}")
106
+ logger.error(
107
+ f"Raw file not found for VideoFile {file_id}: {file_path}"
108
+ )
87
109
  return None
88
-
110
+
89
111
  # Get processor name
90
112
  processor_name = None
91
113
  if vf.video_meta and vf.video_meta.processor:
92
114
  processor_name = vf.video_meta.processor.name
93
- elif hasattr(vf, 'processor') and vf.processor:
115
+ elif hasattr(vf, "processor") and vf.processor:
94
116
  processor_name = vf.processor.name
95
-
117
+
96
118
  # Get center name
97
119
  center_name = vf.center.name if vf.center else "unknown_center"
98
-
120
+
99
121
  # Mark as started
100
122
  if vf.state:
101
123
  vf.state.processing_started = True
102
124
  vf.state.save(update_fields=["processing_started"])
103
-
125
+
104
126
  # Use VideoImportService for anonymization
127
+ safe_processor_name = processor_name or "unknown_processor"
105
128
  self.video_service.import_and_anonymize(
106
129
  file_path=file_path,
107
130
  center_name=center_name,
108
- processor_name=processor_name,
131
+ processor_name=safe_processor_name,
109
132
  save_video=True,
110
- delete_source=False
133
+ delete_source=False,
134
+ )
135
+
136
+ logger.info(
137
+ f"Video anonymization completed for VideoFile ID: {file_id}"
111
138
  )
112
-
113
- logger.info(f"Video anonymization completed for VideoFile ID: {file_id}")
114
139
  return "video"
115
-
140
+
116
141
  except Exception as e:
117
142
  logger.error(f"Failed to anonymize VideoFile {file_id}: {e}")
118
143
  # Mark as failed if state exists
119
144
  if vf.state:
120
- vf.state.processing_started = False # Mark processing as not started due to failure
145
+ vf.state.processing_started = (
146
+ False # Mark processing as not started due to failure
147
+ )
121
148
  vf.state.save(update_fields=["processing_started"])
122
149
  raise
123
150
 
124
151
  # Try RawPdfFile
125
- pdf = RawPdfFile.objects.select_related("state", "sensitive_meta", "center").filter(pk=file_id).first()
152
+ pdf = (
153
+ RawPdfFile.objects.select_related("state", "sensitive_meta", "center")
154
+ .filter(pk=file_id)
155
+ .first()
156
+ )
126
157
  if pdf:
127
158
  try:
128
- logger.info(f"Starting PDF processing for RawPdfFile ID: {file_id}")
129
-
159
+ logger.info(f"Starting report processing for RawPdfFile ID: {file_id}")
160
+
130
161
  # Check if already processed
131
- if pdf.state and getattr(pdf.state, 'anonymized', False):
162
+ if pdf.state and getattr(pdf.state, "anonymized", False):
132
163
  logger.info(f"RawPdfFile {file_id} already processed, skipping")
133
164
  return "pdf"
134
-
135
- # Get file path
136
- if not pdf.file or not hasattr(pdf.file, 'path'):
137
- logger.error(f"PDF file not found for RawPdfFile {file_id}")
165
+
166
+ file_field = pdf.file
167
+ if not file_field or not file_field.name:
168
+ logger.error(f"report file not found for RawPdfFile {file_id}")
138
169
  return None
139
-
140
- file_path = Path(pdf.file.path)
141
- if not file_path.exists():
142
- logger.error(f"PDF file does not exist: {file_path}")
170
+
171
+ if not file_exists(file_field):
172
+ logger.error(
173
+ "report file missing from storage for RawPdfFile %s", file_id
174
+ )
143
175
  return None
144
-
176
+
145
177
  # Get center name
146
178
  center_name = pdf.center.name if pdf.center else "unknown_center"
147
-
179
+
148
180
  # Mark as started
149
181
  if pdf.state:
150
182
  pdf.state.processing_started = True
151
183
  pdf.state.save(update_fields=["processing_started"])
152
- elif pdf.sensitive_meta:
153
- pdf.sensitive_meta.anonymization_started = True
154
- pdf.sensitive_meta.save(update_fields=["anonymization_started"])
155
-
156
-
157
- # Use PdfImportService for processing
158
- # Use PdfImportService for processing
159
- self.pdf_service.import_and_anonymize(
160
- file_path=file_path,
161
- center_name=center_name,
162
- )
163
-
164
- logger.info(f"PDF processing completed for RawPdfFile ID: {file_id}")
184
+
185
+ with ensure_local_file(file_field) as local_path:
186
+ self.pdf_service.import_and_anonymize(
187
+ file_path=local_path,
188
+ center_name=center_name,
189
+ )
190
+
191
+ logger.info(f"report processing completed for RawPdfFile ID: {file_id}")
165
192
  return "pdf"
166
-
193
+
167
194
  except Exception as e:
168
195
  logger.error(f"Failed to process RawPdfFile {file_id}: {e}")
169
196
  # Mark as failed if state exists
170
- if pdf.state:
171
- pdf.state.processing_failed = True
197
+ if pdf.state and hasattr(pdf.state, "processing_failed"):
172
198
  pdf.state.save(update_fields=["processing_failed"])
173
- elif pdf.sensitive_meta:
174
- pdf.sensitive_meta.processing_failed = True
199
+ elif pdf.sensitive_meta and hasattr(
200
+ pdf.sensitive_meta, "processing_failed"
201
+ ):
175
202
  pdf.sensitive_meta.save(update_fields=["processing_failed"])
176
203
  raise
177
204
 
@@ -183,41 +210,53 @@ class AnonymizationService:
183
210
  def validate(file_id: int):
184
211
  vf = VideoFile.objects.select_related("state").filter(pk=file_id).first()
185
212
  if vf:
186
- vf.state.mark_anonymization_validated()
213
+ state = vf.state or vf.get_or_create_state()
214
+ if hasattr(state, "mark_anonymization_validated"):
215
+ state.mark_anonymization_validated()
187
216
  return "video"
188
217
 
189
218
  pdf = RawPdfFile.objects.select_related("state").filter(pk=file_id).first()
190
219
  if pdf:
191
- pdf.state.mark_anonymization_validated()
220
+ state = pdf.state or pdf.get_or_create_state()
221
+ if hasattr(state, "mark_anonymization_validated"):
222
+ state.mark_anonymization_validated()
192
223
  return "pdf"
193
224
 
194
225
  return None
195
-
226
+
227
+ @staticmethod
196
228
  def list_items():
197
229
  video_files = VideoFile.objects.select_related("state").all()
198
- pdf_files = RawPdfFile.objects.select_related("state").all() # was sensitive_meta
230
+ pdf_files = RawPdfFile.objects.select_related(
231
+ "state"
232
+ ).all() # was sensitive_meta
199
233
 
200
234
  data = []
201
235
  for vf in video_files:
202
- data.append({
203
- "id": vf.id,
204
- "mediaType": "video",
205
- "anonymizationStatus": vf.state.anonymization_status if vf.state else "not_started",
206
- "createdAt": vf.date_created,
207
- "updatedAt": vf.date_modified,
208
- })
209
-
210
-
236
+ data.append(
237
+ {
238
+ "id": vf.pk,
239
+ "mediaType": "video",
240
+ "anonymizationStatus": vf.state.anonymization_status
241
+ if vf.state
242
+ else "not_started",
243
+ "createdAt": vf.date_created,
244
+ "updatedAt": vf.date_modified,
245
+ }
246
+ )
211
247
 
212
248
  for pdf in pdf_files:
213
- data.append({
214
- "id": pdf.id,
215
- "mediaType": "pdf",
216
- "anonymizationStatus": pdf.state.anonymization_status if pdf.state else "not_started",
217
- "createdAt": pdf.date_created,
218
- "updatedAt": pdf.date_modified,
219
- })
249
+ data.append(
250
+ {
251
+ "id": pdf.pk,
252
+ "mediaType": "pdf",
253
+ "anonymizationStatus": pdf.state.anonymization_status
254
+ if pdf.state
255
+ else "not_started",
256
+ "createdAt": pdf.date_created,
257
+ "updatedAt": pdf.date_modified,
258
+ }
259
+ )
220
260
  return data
221
261
 
222
-
223
262
  return data
@@ -24,9 +24,10 @@ Architecture:
24
24
  # services/lookup_service.py
25
25
  from __future__ import annotations
26
26
 
27
- from typing import Any, Dict, List
27
+ import logging
28
+ from typing import Any, Dict, List, Optional
28
29
 
29
- from django.db.models import Prefetch
30
+ from django.db.models import Prefetch, QuerySet
30
31
 
31
32
  from endoreg_db.models.medical.examination import ExaminationRequirementSet
32
33
  from endoreg_db.models.medical.patient.patient_examination import PatientExamination
@@ -88,31 +89,43 @@ def load_patient_exam_for_eval(pk: int) -> PatientExamination:
88
89
  )
89
90
 
90
91
 
91
- def requirement_sets_for_patient_exam(pe: PatientExamination) -> List[RequirementSet]:
92
+ def requirement_sets_for_patient_exam(
93
+ pe: PatientExamination, user_tags: Optional[List[str]] = None
94
+ ) -> QuerySet:
92
95
  """
93
- Get all requirement sets applicable to a patient examination.
94
-
95
- This function resolves requirement sets through the examination's requirement set links.
96
- It follows the relationship: PatientExamination → Examination → ExaminationRequirementSet → RequirementSet
96
+ Retrieve all RequirementSets linked to a PatientExamination's examination.
97
97
 
98
98
  Args:
99
- pe: PatientExamination instance to get requirement sets for
99
+ pe: PatientExamination instance
100
+ user_tags: Optional list of tag names to filter requirement sets
100
101
 
101
102
  Returns:
102
- List of RequirementSet instances applicable to the examination, with related data prefetched
103
+ QuerySet of RequirementSet instances
103
104
  """
104
- exam = pe.examination
105
- if not exam:
106
- return []
107
- return list(
108
- RequirementSet.objects.filter(reqset_exam_links__examinations=exam)
109
- .select_related("requirement_set_type")
110
- .prefetch_related("requirements")
111
- .distinct()
112
- )
105
+ if not pe or not pe.examination:
106
+ from endoreg_db.models import RequirementSet
107
+
108
+ return RequirementSet.objects.none()
109
+
110
+ # Start with examination-linked requirement sets
111
+ req_sets = pe.examination.exam_reqset_links.select_related(
112
+ "requirement_set"
113
+ ).values_list("requirement_set", flat=True)
114
+
115
+ from endoreg_db.models import RequirementSet
113
116
 
117
+ qs = RequirementSet.objects.filter(pk__in=req_sets)
114
118
 
115
- def build_initial_lookup(pe: PatientExamination) -> Dict[str, Any]:
119
+ # Apply tag filtering if provided
120
+ if user_tags:
121
+ qs = qs.filter(tags__name__in=user_tags).distinct()
122
+
123
+ return qs
124
+
125
+
126
+ def build_initial_lookup(
127
+ pe: PatientExamination, user_tags: Optional[List[str]] = None
128
+ ) -> Dict[str, Any]:
116
129
  """
117
130
  Build the initial lookup dictionary for a patient examination.
118
131
 
@@ -135,14 +148,14 @@ def build_initial_lookup(pe: PatientExamination) -> Dict[str, Any]:
135
148
  Dictionary containing initial lookup data with the following keys:
136
149
  - patient_examination_id: ID of the patient examination
137
150
  - requirement_sets: List of available requirement sets with metadata
138
- - availableFindings: List of finding IDs available for the examination
139
- - requiredFindings: List of finding IDs that are required by defaults
140
- - requirementDefaults: Default findings per requirement
141
- - classificationChoices: Available classification choices per requirement
142
- - requirementsBySet: Empty dict (populated on selection)
143
- - requirementStatus: Empty dict (computed on evaluation)
144
- - requirementSetStatus: Empty dict (computed on evaluation)
145
- - suggestedActions: Empty dict (computed on evaluation)
151
+ - available_findings: List of finding IDs available for the examination
152
+ - required_findings: List of finding IDs that are required by defaults
153
+ - requirement_defaults: Default findings per requirement
154
+ - classification_choices: Available classification choices per requirement
155
+ - requirements_by_set: Empty dict (populated on selection)
156
+ - requirement_status: Empty dict (computed on evaluation)
157
+ - requirement_set_status: Empty dict (computed on evaluation)
158
+ - suggested_actions: Empty dict (computed on evaluation)
146
159
  """
147
160
  # Available + required findings
148
161
  available_findings = (
@@ -153,7 +166,7 @@ def build_initial_lookup(pe: PatientExamination) -> Dict[str, Any]:
153
166
  required_findings: List[int] = [] # fill by scanning requirements below
154
167
 
155
168
  # Requirement sets: ids + meta
156
- rs_objs = requirement_sets_for_patient_exam(pe)
169
+ rs_objs = requirement_sets_for_patient_exam(pe, user_tags=user_tags)
157
170
  requirement_sets = [
158
171
  {
159
172
  "id": rs.id,
@@ -189,20 +202,22 @@ def build_initial_lookup(pe: PatientExamination) -> Dict[str, Any]:
189
202
  return {
190
203
  "patient_examination_id": pe.id,
191
204
  "requirement_sets": requirement_sets,
192
- "availableFindings": available_findings,
193
- "requiredFindings": required_findings,
194
- "requirementDefaults": req_defaults,
195
- "classificationChoices": cls_choices,
205
+ "available_findings": available_findings,
206
+ "required_findings": required_findings,
207
+ "requirement_defaults": req_defaults,
208
+ "classification_choices": cls_choices,
196
209
  # New fields for expanded lookup payload
197
- "requirementsBySet": {}, # Will be populated when requirement sets are selected
198
- "requirementStatus": {}, # Status of each requirement (satisfied/unsatisfied)
199
- "requirementSetStatus": {}, # Status of each requirement set
200
- "suggestedActions": {}, # Suggested actions to satisfy requirements
210
+ "requirements_by_set": {}, # Will be populated when requirement sets are selected
211
+ "requirement_status": {}, # Status of each requirement (satisfied/unsatisfied)
212
+ "requirement_set_status": {}, # Status of each requirement set
213
+ "suggested_actions": {}, # Suggested actions to satisfy requirements
201
214
  # You can add "selectedRequirementSetIds" as the user makes choices
202
215
  }
203
216
 
204
217
 
205
- def create_lookup_token_for_pe(pe_id: int) -> str:
218
+ def create_lookup_token_for_pe(
219
+ pe_id: int, user_tags: Optional[List[str]] = None
220
+ ) -> str:
206
221
  """
207
222
  Create a lookup token for a patient examination.
208
223
 
@@ -220,7 +235,7 @@ def create_lookup_token_for_pe(pe_id: int) -> str:
220
235
  Exception: For any other errors during initialization
221
236
  """
222
237
  pe = load_patient_exam_for_eval(pe_id)
223
- token = LookupStore().init(build_initial_lookup(pe))
238
+ token = LookupStore().init(build_initial_lookup(pe, user_tags=user_tags))
224
239
  return token
225
240
 
226
241
 
@@ -245,18 +260,16 @@ def recompute_lookup(token: str) -> Dict[str, Any]:
245
260
 
246
261
  Returns:
247
262
  Dictionary of updates containing:
248
- - requirementsBySet: Requirements grouped by selected requirement sets
249
- - requirementStatus: Boolean status for each requirement
250
- - requirementSetStatus: Boolean status for each requirement set
251
- - requirementDefaults: Default findings per requirement
252
- - classificationChoices: Available choices per requirement
253
- - suggestedActions: UI actions to satisfy unsatisfied requirements
263
+ - requirements_by_set: Requirements grouped by selected requirement sets
264
+ - requirement_status: Boolean status for each requirement
265
+ - requirement_set_status: Boolean status for each requirement set
266
+ - requirement_defaults: Default findings per requirement
267
+ - classification_choices: Available choices per requirement
268
+ - suggested_actions: UI actions to satisfy unsatisfied requirements
254
269
 
255
270
  Raises:
256
271
  ValueError: If lookup data is invalid or patient examination not found
257
272
  """
258
- import logging
259
-
260
273
  logger = logging.getLogger(__name__)
261
274
 
262
275
  store = LookupStore(token=token)
@@ -383,12 +396,12 @@ def recompute_lookup(token: str) -> Dict[str, Any]:
383
396
  # if you implement server-side simulation later, adjust requirement_status with staged result here
384
397
 
385
398
  updates = {
386
- "requirementsBySet": requirements_by_set,
387
- "requirementStatus": requirement_status,
388
- "requirementSetStatus": set_status,
389
- "requirementDefaults": req_defaults, # keep your existing key
390
- "classificationChoices": cls_choices, # keep your existing key
391
- "suggestedActions": suggested_actions, # new
399
+ "requirements_by_set": requirements_by_set,
400
+ "requirement_status": requirement_status,
401
+ "requirement_set_status": set_status,
402
+ "requirement_defaults": req_defaults, # keep your existing key
403
+ "classification_choices": cls_choices, # keep your existing key
404
+ "suggested_actions": suggested_actions, # new
392
405
  }
393
406
 
394
407
  logger.debug(
@@ -173,8 +173,8 @@ class LookupStore:
173
173
  # Check if required fields are present
174
174
  required_fields = [
175
175
  "patient_examination_id",
176
- "requirementsBySet",
177
- "requirementStatus",
176
+ "requirements_by_set",
177
+ "requirement_status",
178
178
  ]
179
179
  missing_fields = [field for field in required_fields if field not in data]
180
180