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
@@ -0,0 +1,56 @@
1
+ import logging
2
+ from pathlib import Path
3
+ from typing import Iterable
4
+ from endoreg_db.utils.paths import (
5
+ ANONYM_REPORT_DIR,
6
+ ANONYM_VIDEO_DIR,
7
+ IMPORT_REPORT_DIR,
8
+ IMPORT_VIDEO_DIR,
9
+ SENSITIVE_REPORT_DIR,
10
+ SENSITIVE_VIDEO_DIR,
11
+ )
12
+
13
+ dirs = [
14
+ ANONYM_REPORT_DIR,
15
+ ANONYM_VIDEO_DIR,
16
+ IMPORT_REPORT_DIR,
17
+ IMPORT_VIDEO_DIR,
18
+ SENSITIVE_REPORT_DIR,
19
+ SENSITIVE_VIDEO_DIR,
20
+ ]
21
+
22
+
23
+ logger = logging.getLogger(__name__)
24
+
25
+
26
+ def validate_directories(dirs: Iterable[Path] = dirs) -> bool:
27
+ """
28
+ Ensure all directories in `dirs` exist.
29
+ Missing directories are created automatically.
30
+
31
+ Args:
32
+ dirs: Iterable of Path objects representing directories.
33
+
34
+ Returns:
35
+ bool: True if all directories exist or were created successfully,
36
+ False if any directory could not be created.
37
+ """
38
+ ok = True
39
+
40
+ for d in dirs:
41
+ try:
42
+ if not d.exists():
43
+ logger.info(f"Directory missing, creating: {d}")
44
+ d.mkdir(parents=True, exist_ok=True)
45
+
46
+ if not d.is_dir():
47
+ logger.error(f"Path exists but is not a directory: {d}")
48
+ ok = False
49
+
50
+ except Exception as e:
51
+ logger.error(f"Failed to create or validate directory '{d}': {e}")
52
+ ok = False
53
+
54
+ return ok
55
+
56
+ validate_directories(dirs)
@@ -0,0 +1,15 @@
1
+ # storage/__init__.py
2
+
3
+ from . import create_report_file
4
+ from . import create_video_file
5
+ from . import sensitive_meta_storage
6
+ from . import state_management
7
+ from . import storage
8
+
9
+ __all__ = [
10
+ "create_report_file",
11
+ "create_video_file",
12
+ "sensitive_meta_storage",
13
+ "state_management",
14
+ "storage",
15
+ ]
@@ -0,0 +1,76 @@
1
+ # endoreg_db/import_files/storage/create_report_file.py
2
+ import logging
3
+ from typing import Tuple
4
+
5
+ from endoreg_db.import_files.context.import_context import ImportContext
6
+ from endoreg_db.models.media import RawPdfFile
7
+ from endoreg_db.models.media.processing_history.processing_history import ProcessingHistory
8
+ from endoreg_db.import_files.context.ensure_center import ensure_center
9
+ logger = logging.getLogger(__name__)
10
+
11
+
12
+ def create_or_retrieve_report_file(
13
+ ctx: ImportContext,
14
+ ) -> Tuple[RawPdfFile, bool]:
15
+ """
16
+ Create a new or retrieve an existing RawPdfFile for the given context.
17
+
18
+ Returns:
19
+ pdf : RawPdfFile instance
20
+ needs_processing: True if the pipeline should run for this file
21
+ (no successful history yet for this object/file_type key)
22
+ """
23
+ file_path = ctx.file_path
24
+ center_name = ctx.center_name
25
+ delete_source = ctx.delete_source
26
+ file_type = ctx.file_type # logical key for history; can be None
27
+
28
+ # 1) Determine the RawPdfFile instance to work with
29
+ if ctx.current_report is not None:
30
+ pdf = ctx.current_report
31
+ logger.info("Using existing RawPdfFile from context: pk=%s", pdf.pk)
32
+ else:
33
+ logger.info("Creating new RawPdfFile from %s for center %s", file_path, center_name)
34
+
35
+ pdf = RawPdfFile.create_from_file_initialized(
36
+ file_path=file_path,
37
+ center_name=center_name,
38
+ delete_source=delete_source,
39
+ )
40
+
41
+ center = ensure_center(pdf, ctx.center_name)
42
+
43
+ logger.info(f"Successfully set up report file from {center.name}")
44
+
45
+
46
+
47
+ # 3) Check if we already have a successful history entry for this object+file_type
48
+ has_success_history = ProcessingHistory.has_history_for_object(
49
+ obj=pdf,
50
+ success=True,
51
+ )
52
+
53
+ if has_success_history:
54
+ logger.info(
55
+ "RawPdfFile %s already has successful processing history (file_type=%s) - short-circuiting",
56
+ getattr(pdf, str(pdf.file_path)),
57
+ file_type,
58
+ )
59
+ # No need to run the pipeline again
60
+ return pdf, False
61
+
62
+ # 4) No successful history yet → ensure there is a history entry marking it as "in progress"/failed
63
+ ProcessingHistory.get_or_create_for_object(
64
+ obj=pdf,
65
+ # At this point we haven't finished anonymization; treat as not-success yet.
66
+ success=False,
67
+ )
68
+
69
+ logger.info(
70
+ "Report instance ready for processing: pk=%s, file_type=%s (needs_processing=True)",
71
+ pdf.pk,
72
+ file_type,
73
+ )
74
+
75
+ # Signal to the caller that the anonymization pipeline should run
76
+ return pdf, True
@@ -0,0 +1,75 @@
1
+ # endoreg_db/import_files/storage/create_video_file.py
2
+ import logging
3
+ from typing import Tuple
4
+
5
+ from django.db import IntegrityError
6
+
7
+ from endoreg_db.import_files.context.import_context import ImportContext
8
+ from endoreg_db.import_files.context.ensure_center import ensure_center
9
+ from endoreg_db.models.media import VideoFile
10
+ from endoreg_db.models.media.processing_history.processing_history import ProcessingHistory
11
+ from endoreg_db.utils.hashs import get_video_hash
12
+
13
+ logger = logging.getLogger(__name__)
14
+
15
+
16
+ def create_or_retrieve_video_file(
17
+ ctx: ImportContext,
18
+ ) -> Tuple[VideoFile, bool]:
19
+ """
20
+ Create a new or retrieve an existing VideoFile for the given context.
21
+
22
+ Returns:
23
+ video : VideoFile instance
24
+ retry : whether we are re-processing an existing file
25
+ """
26
+ file_path = ctx.file_path
27
+ center_name = ctx.center_name
28
+ processor_name = ctx.processor_name
29
+ delete_source = ctx.delete_source
30
+ file_type = ctx.file_type
31
+
32
+ # 1) Determine the VideoFile instance to work with
33
+ if ctx.current_video is not None:
34
+ video = ctx.current_video
35
+ logger.info("Using existing VideoFIle from context: pk =%s", video.pk)
36
+ else:
37
+ logger.info("Creating new VideoFIle from %s for center %s", file_path, center_name)
38
+ video = VideoFile.create_from_file_initialized(
39
+ file_path=file_path,
40
+ center_name=center_name,
41
+ processor_name=processor_name,
42
+ delete_source=delete_source,
43
+ )
44
+ center = ensure_center(video, ctx.center_name)
45
+
46
+ logger.info(f"Successfully set up report file from {center.name}")
47
+ # 3) Check if we already have a successful history entry for this object+file_type
48
+ has_success_history = ProcessingHistory.has_history_for_object(
49
+ obj=video,
50
+ success=True,
51
+ )
52
+
53
+ if has_success_history:
54
+ logger.info(
55
+ "VideoFile %s already has successful processing history. (file_type:%s) - short-circuiting",
56
+ getattr(video, str(video.active_file_path)),
57
+ file_type,
58
+ )
59
+
60
+ # 4) No successful history yet → ensure there is a history entry marking it as "in progress"/failed
61
+ ProcessingHistory.get_or_create_for_object(
62
+ obj=video,
63
+ success=False
64
+ )
65
+
66
+
67
+ logger.info(
68
+ "Video instance ready for processing: pk=%s, file_type=%s (needs_processing=True)",
69
+ video.pk,
70
+ file_type,
71
+ )
72
+
73
+ # Signal to the caller that the anonymization pipeline should run
74
+ return video, True
75
+
@@ -0,0 +1,39 @@
1
+ # endoreg_db/import_files/storage/sensitive_meta_storage.py
2
+ from typing import Union, Dict, Any
3
+
4
+ from endoreg_db.models.media import RawPdfFile, VideoFile
5
+ from endoreg_db.models.metadata import SensitiveMeta
6
+ from endoreg_db.import_files.processing.sensitive_meta_adapter import (
7
+ normalize_lx_sensitive_meta,
8
+ )
9
+ from endoreg_db.import_files.context.default_sensitive_meta import default_sensitive_meta
10
+ from logging import getLogger
11
+ from lx_anonymizer.sensitive_meta_interface import SensitiveMeta as LxSM
12
+ #
13
+
14
+ logger = getLogger(__name__)
15
+
16
+ def sensitive_meta_storage(
17
+ sensitive_meta: LxSM,
18
+ instance: Union[RawPdfFile, VideoFile],
19
+ ) -> bool:
20
+ """
21
+ Merge lx_anonymizer.SensitiveMeta into instance.sensitive_meta in the DB.
22
+
23
+ - Normalizes the dataclass into the dict format expected by the model logic
24
+ - Delegates to SensitiveMeta.update_from_dict() (which already calls logic.update_*)
25
+ """
26
+ local_meta = instance.sensitive_meta # Django SensitiveMeta model instance
27
+ if not isinstance(local_meta, SensitiveMeta):
28
+ # If sensitice meta doesnt exist yet, ensure it
29
+ local_meta = default_sensitive_meta(instance)
30
+ assert isinstance(local_meta, SensitiveMeta)
31
+
32
+ try:
33
+ payload = normalize_lx_sensitive_meta(sensitive_meta)
34
+ local_meta.update_from_dict(payload) # this calls your big logic.update_*
35
+ except Exception as e:
36
+ logger.error(f"{e}")
37
+ return False
38
+
39
+ return True
@@ -0,0 +1,400 @@
1
+ from endoreg_db.models.media.processing_history.processing_history import ProcessingHistory
2
+ from endoreg_db.utils.paths import IMPORT_REPORT_DIR, IMPORT_VIDEO_DIR, ANONYM_REPORT_DIR, ANONYM_VIDEO_DIR
3
+
4
+
5
+ import logging
6
+ import shutil
7
+ from pathlib import Path
8
+ from typing import Optional, Union
9
+
10
+ from django.db import transaction
11
+
12
+ from endoreg_db.import_files.context.import_context import ImportContext
13
+ from endoreg_db.models.media import RawPdfFile, VideoFile
14
+ from endoreg_db.models.state import RawPdfState, VideoState
15
+ from endoreg_db.utils import paths as path_utils
16
+
17
+ logger = logging.getLogger(__name__)
18
+
19
+
20
+ def _ensure_instance_state(instance: Union[VideoFile, RawPdfFile]) -> Optional[Union[RawPdfState, VideoState]]:
21
+ """
22
+ Helper: ensure instance.state exists and return it.
23
+ Mirrors PdfImportService._ensure_state.
24
+ """
25
+ if isinstance(instance, RawPdfFile):
26
+ state = getattr(instance, "state", None)
27
+ else:
28
+ state = getattr(instance, "state", None)
29
+
30
+ if state is not None:
31
+ return state
32
+
33
+ if hasattr(instance, "get_or_create_state"):
34
+ state = instance.get_or_create_state()
35
+ instance.save()
36
+ return state
37
+
38
+ return None
39
+
40
+ def mark_instance_processing_started(
41
+ instance: Union[RawPdfFile, VideoFile],
42
+ ctx: ImportContext,):
43
+ state = _ensure_instance_state(instance)
44
+
45
+ with transaction.atomic():
46
+ if state is not None:
47
+
48
+ # In the old code, processing_started was set earlier; we guard here
49
+ if not getattr(state, "processing_started", False) and hasattr(
50
+ state, "mark_processing_started"
51
+ ):
52
+ state.mark_processing_started()
53
+
54
+
55
+ def finalize_report_success(
56
+ ctx: ImportContext,
57
+ ) -> None:
58
+ """
59
+ Finalize a successful instance import/anonymization.
60
+
61
+ - Move anonymized Report from temp to canonical anonymized dir
62
+ - Update RawPdfFile.processed_file and .anonymized flag
63
+ - Mark RawPdfState as anonymized + sensitive_meta_processed
64
+ - Mark ProcessingHistory.success = True
65
+ """
66
+ instance = ctx.current_report
67
+ if not isinstance(instance, RawPdfFile):
68
+ logger.warning("finalize_success called with unsaved instance")
69
+ return
70
+ if not instance.pk:
71
+ logger.warning("finalize_success called with unsaved instance")
72
+ return
73
+
74
+ # --- Move anonymized path into final storage (if we have one) ---
75
+ final_path: Optional[Path] = None
76
+ if ctx.anonymized_path is None:
77
+ logger.warning(
78
+ "No anonymized_path for instance %s (hash=%s); skipping file move.",
79
+ instance.pk,
80
+ getattr(instance, "pdf_hash", None),
81
+ )
82
+ final_path = None
83
+ else:
84
+ pdf_hash = getattr(instance, "pdf_hash", None) or instance.pk
85
+ expected_final_path = ANONYM_REPORT_DIR / f"{pdf_hash}.pdf"
86
+
87
+ src = Path(ctx.anonymized_path)
88
+
89
+ logger.debug(
90
+ "finalize_report_success: src=%s (exists=%s, resolved=%s), expected_final=%s",
91
+ src,
92
+ src.exists(),
93
+ src.resolve(),
94
+ expected_final_path,
95
+ )
96
+
97
+ # If anonymizer already wrote to the final path, don't move
98
+ if src.resolve() == expected_final_path.resolve():
99
+ logger.info(
100
+ "Anonymizer output already at final path %s; skipping move.",
101
+ expected_final_path,
102
+ )
103
+ final_path = expected_final_path
104
+ else:
105
+ # Only move if the source actually exists
106
+ if not src.exists():
107
+ logger.error(
108
+ "Anonymized file %s does not exist; cannot move to %s",
109
+ src,
110
+ expected_final_path,
111
+ )
112
+ final_path = None
113
+ else:
114
+ ANONYM_REPORT_DIR.mkdir(parents=True, exist_ok=True)
115
+ if expected_final_path.exists():
116
+ expected_final_path.unlink()
117
+ shutil.move(str(src), str(expected_final_path))
118
+ final_path = expected_final_path
119
+ logger.info("Moved anonymized report to %s", final_path)
120
+
121
+ # Update FileField if we have a final path
122
+ if final_path is not None:
123
+ relative_name = path_utils.to_storage_relative(final_path)
124
+ current_name = getattr(instance.processed_file, "name", None)
125
+ if current_name != relative_name:
126
+ instance.processed_file.name = relative_name
127
+ logger.info("Updated processed_file to %s", relative_name)
128
+ try:
129
+ relative_name = str(ctx.anonymized_path)
130
+ except ValueError:
131
+ # Fallback: absolute path if outside STORAGE_DIR
132
+ relative_name = str(final_path)
133
+
134
+ current_name = getattr(instance.processed_file, "name", None)
135
+ if current_name != relative_name:
136
+ instance.processed_file.name = relative_name
137
+ logger.info(
138
+ "Updated processed_file reference to: %s",
139
+ instance.processed_file.name,
140
+ )
141
+
142
+
143
+ # --- Update RawPdfState flags (mirrors _finalize_processing) ---
144
+ state = _ensure_instance_state(instance)
145
+
146
+ with transaction.atomic():
147
+ if state is not None:
148
+
149
+ # In the old code, processing_started was set earlier; we guard here
150
+ if not getattr(state, "processing_started", False) and hasattr(
151
+ state, "mark_processing_started"
152
+ ):
153
+ state.mark_processing_started()
154
+
155
+ # We consider text/meta extraction + anonymization done at this point
156
+ if hasattr(state, "mark_anonymized"):
157
+ state.mark_anonymized()
158
+ if hasattr(state, "mark_sensitive_meta_processed"):
159
+ state.mark_sensitive_meta_processed()
160
+
161
+ state.save()
162
+
163
+ instance.save()
164
+
165
+ # --- ProcessingHistory entry ---
166
+ try:
167
+ with transaction.atomic():
168
+ ProcessingHistory.get_or_create_for_object(
169
+ obj=instance,
170
+ success=True,
171
+ )
172
+ except Exception as e:
173
+ logger.debug(
174
+ "Saving not possible; %s"
175
+ f"skipping ProcessingHistory.{e}",
176
+ instance.pk,
177
+ )
178
+
179
+ def finalize_video_success(
180
+ ctx: ImportContext,
181
+ ) -> None:
182
+ """
183
+ Finalize a successful video import/anonymization.
184
+
185
+ - Move anonymized video from temp to canonical anonymized dir
186
+ - Update VideoFile.processed_file
187
+ - Mark VideoState as anonymized + sensitive_meta_processed
188
+ - Mark ProcessingHistory.success = True
189
+ """
190
+ instance = ctx.current_video
191
+ if not isinstance(instance, VideoFile):
192
+ logger.warning("finalize_video_success called with non-VideoFile instance")
193
+ return
194
+ if not instance.pk:
195
+ logger.warning("finalize_video_success called with unsaved instance")
196
+ return
197
+
198
+ # --- Move anonymized path into final storage (if we have one) ---
199
+ final_path: Optional[Path] = None
200
+
201
+ if ctx.anonymized_path is None:
202
+ logger.warning(
203
+ "No anonymized_path for video instance %s (hash=%s); skipping file move.",
204
+ instance.pk,
205
+ getattr(instance, "video_hash", None),
206
+ )
207
+ else:
208
+ # Use a stable naming convention: <video_hash>.mp4
209
+ video_hash = getattr(instance, "video_hash", None) or instance.pk
210
+ expected_final_path = ANONYM_VIDEO_DIR / f"{video_hash}.mp4"
211
+
212
+ src = Path(ctx.anonymized_path)
213
+
214
+ logger.debug(
215
+ "finalize_video_success: src=%s (exists=%s, resolved=%s), expected_final=%s",
216
+ src,
217
+ src.exists(),
218
+ src.resolve(),
219
+ expected_final_path,
220
+ )
221
+
222
+ # If anonymizer already wrote to the final path, don't move
223
+ try:
224
+ same_target = src.resolve() == expected_final_path.resolve()
225
+ except FileNotFoundError:
226
+ # src might not exist anymore
227
+ same_target = False
228
+
229
+ if same_target:
230
+ logger.info(
231
+ "Anonymizer output already at final video path %s; skipping move.",
232
+ expected_final_path,
233
+ )
234
+ final_path = expected_final_path
235
+ else:
236
+ if not src.exists():
237
+ logger.error(
238
+ "Anonymized video %s does not exist; cannot move to %s",
239
+ src,
240
+ expected_final_path,
241
+ )
242
+ final_path = None
243
+ else:
244
+ ANONYM_VIDEO_DIR.mkdir(parents=True, exist_ok=True)
245
+ if expected_final_path.exists():
246
+ try:
247
+ expected_final_path.unlink()
248
+ except Exception as e:
249
+ logger.warning(
250
+ "Could not remove existing anonymized video %s: %s",
251
+ expected_final_path,
252
+ e,
253
+ )
254
+ shutil.move(str(src), str(expected_final_path))
255
+ final_path = expected_final_path
256
+ logger.info("Moved anonymized video to %s", final_path)
257
+
258
+ # Update FileField if we have a final path
259
+ if final_path is not None:
260
+ relative_name = path_utils.to_storage_relative(final_path)
261
+ current_name = getattr(instance.processed_file, "name", None)
262
+ if current_name != relative_name:
263
+ instance.processed_file.name = relative_name
264
+ logger.info("Updated video processed_file to %s", relative_name)
265
+
266
+ # --- Update VideoState flags (mirrors report) ---
267
+ state = _ensure_instance_state(instance)
268
+
269
+ with transaction.atomic():
270
+ if state is not None:
271
+ if not getattr(state, "processing_started", False) and hasattr(
272
+ state, "mark_processing_started"
273
+ ):
274
+ state.mark_processing_started()
275
+
276
+ if hasattr(state, "mark_anonymized"):
277
+ state.mark_anonymized()
278
+ if hasattr(state, "mark_sensitive_meta_processed"):
279
+ state.mark_sensitive_meta_processed()
280
+
281
+ state.save()
282
+
283
+ instance.save()
284
+
285
+ # --- ProcessingHistory entry ---
286
+ try:
287
+ with transaction.atomic():
288
+ ProcessingHistory.get_or_create_for_object(
289
+ obj=instance,
290
+ success=True,
291
+ )
292
+ except Exception as e:
293
+ logger.debug(
294
+ "Saving not possible for video %s; skipping ProcessingHistory. Error: %s",
295
+ instance.pk,
296
+ e,
297
+ )
298
+
299
+
300
+ def finalize_failure(
301
+ ctx: ImportContext,
302
+ ) -> None:
303
+ """
304
+ Finalize a failed instance import/anonymization.
305
+
306
+ - Reset RawPdfState flags to "not processed"
307
+ - Mark ProcessingHistory.success = False
308
+ """
309
+ if ctx.instance is None:
310
+ if isinstance(ctx.current_report, RawPdfFile):
311
+ ctx.instance = ctx.current_report
312
+ elif isinstance(ctx.current_video, VideoFile):
313
+ ctx.instance = ctx.current_video
314
+ else:
315
+ raise Exception
316
+ # Reset state flags similar to _mark_processing_incomplete / _cleanup_on_error
317
+ state = _ensure_instance_state(ctx.instance)
318
+
319
+ if state is not None:
320
+ try:
321
+ state.mark_processing_not_started()
322
+
323
+ state.save()
324
+ logger.info(
325
+ "Reset instance state for failed processing (instance pk=%s)",
326
+ ctx.instance.pk,
327
+ )
328
+ except Exception as e:
329
+ logger.warning(
330
+ "Failed to reset State for instance %s: %s",
331
+ ctx.instance.pk,
332
+ e,
333
+ )
334
+
335
+ try:
336
+ delete_associated_files(ctx)
337
+ except Exception as e:
338
+ logger.warning(f"There might be files remaining. {e}")
339
+
340
+ # History entry with success=False
341
+ if ctx.file_hash:
342
+ ProcessingHistory.get_or_create_for_object(
343
+ obj=ctx.instance,
344
+ success=False,
345
+ )
346
+ else:
347
+ logger.debug(
348
+ "No file_hash in context for instance %s when finalizing failure; "
349
+ "skipping ProcessingHistory.",
350
+ ctx.instance.pk,
351
+ )
352
+
353
+ logger.error(
354
+ "Report processing failed for %s",
355
+ ctx.file_path,
356
+ )
357
+
358
+ def delete_associated_files(ctx:ImportContext):
359
+ try:
360
+ assert isinstance(ctx.original_path, Path)
361
+ except AssertionError as e:
362
+ logger.warning(f"Original file restored from sensitive copy. This is because the original file is gone. {e}")
363
+ if ctx.file_type =="video":
364
+ if isinstance(ctx.sensitive_path, Path):
365
+ try:
366
+ ctx.original_path = Path(shutil.copy2(ctx.sensitive_path, IMPORT_VIDEO_DIR))
367
+ except Exception as e:
368
+ logger.error(f"Error during safety copy: {e} Original file could not be restored!")
369
+ raise
370
+ elif ctx.file_type == "report":
371
+ if isinstance(ctx.sensitive_path, Path):
372
+ try:
373
+ ctx.original_path = Path(shutil.copy2(ctx.sensitive_path, IMPORT_REPORT_DIR))
374
+ except Exception as e:
375
+ logger.error(f"Error during safety copy: {e} Original file could not be restored!")
376
+ raise
377
+
378
+ if isinstance(ctx.anonymized_path, Path):
379
+ try:
380
+ Path.unlink(ctx.anonymized_path)
381
+ assert(ctx.anonymized_path is not Path)
382
+
383
+ except Exception as e:
384
+ logger.error(f"Error when unlinking anonymized path. {e}")
385
+ raise
386
+
387
+ ctx.anonymized_path = None
388
+
389
+ if isinstance(ctx.sensitive_path, Path):
390
+ try:
391
+ Path.unlink(ctx.sensitive_path)
392
+ assert(ctx.sensitive_path is not Path)
393
+
394
+ except Exception as e:
395
+ logger.error(f"Error when unlinking anonymized path. {e}")
396
+
397
+ raise
398
+
399
+ ctx.sensitive_path = None
400
+
@@ -0,0 +1,36 @@
1
+ import logging
2
+ from pathlib import Path
3
+ from endoreg_db.models.media.video.create_from_file import atomic_copy_with_fallback, atomic_move_with_fallback
4
+ logger = logging.getLogger(__name__)
5
+
6
+
7
+ def ensure_dir(path: Path) -> None:
8
+ path.mkdir(parents=True, exist_ok=True)
9
+
10
+
11
+ def create_sensitive_copy(src: Path, sensitive_root: Path) -> Path:
12
+ """
13
+ Create a sensitive copy of `src` in `sensitive_root`.
14
+
15
+ Returns:
16
+ Path to the sensitive copy.
17
+ """
18
+ ensure_dir(sensitive_root)
19
+ dest = sensitive_root / src.name
20
+ logger.info("Creating sensitive copy: %s -> %s", src, dest)
21
+ atomic_copy_with_fallback(src, dest)
22
+ return dest
23
+
24
+
25
+ def move_to_anonymized(temp_path: Path, anonymized_root: Path) -> Path:
26
+ """
27
+ Move a (temporary) anonymized file into the canonical anonymized root.
28
+
29
+ Returns:
30
+ Final path inside anonymized_root.
31
+ """
32
+ ensure_dir(anonymized_root)
33
+ dest = anonymized_root / temp_path.name
34
+ logger.info("Moving anonymized file: %s -> %s", temp_path, dest)
35
+ atomic_move_with_fallback(temp_path, dest)
36
+ return dest