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
@@ -4,35 +4,32 @@
4
4
  # Class contains classmethod to create object from pdf file
5
5
  # objects contains methods to extract text, extract metadata from text and anonymize text from pdf file uzing agl_report_reader.ReportReader class
6
6
  # ------------------------------------------------------------------------------
7
- import os
8
- from typing import TYPE_CHECKING, Optional, Union
7
+ from typing import TYPE_CHECKING, Optional, cast
9
8
 
10
9
  from django.core.exceptions import ValidationError
11
10
  from django.core.files import File
12
11
  from django.core.validators import FileExtensionValidator
13
12
  from django.db import models
14
- from numpy import isin # Import Django File
15
13
 
16
14
  from endoreg_db.utils.file_operations import get_uuid_filename
17
15
  from endoreg_db.utils.hashs import get_pdf_hash
18
- from endoreg_db.utils.paths import PDF_DIR, RAW_PDF_DIR
19
-
20
- # Use the specific paths from the centralized paths module
21
- from ...utils import PDF_DIR
16
+ from endoreg_db.utils.paths import (
17
+ ANONYM_REPORT_DIR,
18
+ IMPORT_REPORT_DIR,
19
+ SENSITIVE_REPORT_DIR,
20
+ )
21
+ from endoreg_db.utils.storage import (
22
+ delete_field_file,
23
+ ensure_local_file,
24
+ file_exists,
25
+ save_local_file,
26
+ )
22
27
 
23
28
  if TYPE_CHECKING:
24
- from endoreg_db.models.administration.person import (
25
- Examiner,
26
- Patient,
27
- )
29
+ from django.db.models.fields.files import FieldFile
28
30
 
29
- from ...administration import Center
30
- from ...medical.patient import PatientExamination
31
- from ...metadata.pdf_meta import PdfType
32
- from ...state import RawPdfState
33
- from .report_file import AnonymExaminationReport
31
+ from endoreg_db.models.state import RawPdfState
34
32
 
35
- # setup logging to pdf_import.log
36
33
  import logging
37
34
  from pathlib import Path
38
35
 
@@ -42,6 +39,7 @@ logger = logging.getLogger("raw_pdf")
42
39
 
43
40
 
44
41
  class RawPdfFile(models.Model):
42
+ objects = models.Manager()
45
43
  # Fields from AbstractPdfFile
46
44
  pdf_hash = models.CharField(max_length=255, unique=True)
47
45
  pdf_type = models.ForeignKey(
@@ -55,50 +53,91 @@ class RawPdfFile(models.Model):
55
53
  on_delete=models.SET_NULL,
56
54
  blank=True,
57
55
  null=True,
58
- ) # type: ignore
56
+ )
59
57
  examination = models.ForeignKey(
60
58
  "PatientExamination",
61
59
  on_delete=models.SET_NULL,
62
60
  blank=True,
63
61
  null=True,
64
62
  related_name="raw_pdf_files",
65
- ) # type: ignore
63
+ )
66
64
  examiner = models.ForeignKey(
67
65
  "Examiner",
68
66
  on_delete=models.SET_NULL,
69
67
  blank=True,
70
68
  null=True,
71
- ) # type: ignore
69
+ )
72
70
  text = models.TextField(blank=True, null=True)
73
71
  date_created = models.DateTimeField(auto_now_add=True)
74
72
  date_modified = models.DateTimeField(auto_now=True)
75
- anonymized = models.BooleanField(
76
- default=False, help_text="True if the PDF has been anonymized."
77
- )
78
73
 
79
- # Fields specific to RawPdfFile (keeping existing related_names)
80
74
  file = models.FileField(
81
- # Use the relative path from the specific PDF_DIR
82
- upload_to=PDF_DIR.name,
75
+ # Use the relative path from the specific REPORT_DIR
76
+ upload_to=SENSITIVE_REPORT_DIR.name,
83
77
  validators=[FileExtensionValidator(allowed_extensions=["pdf"])],
84
- ) # type: ignore
85
-
86
- anonymized_file = models.FileField(
87
- upload_to=PDF_DIR.name,
78
+ )
79
+ processed_file = models.FileField(
80
+ upload_to=ANONYM_REPORT_DIR.name,
88
81
  validators=[FileExtensionValidator(allowed_extensions=["pdf"])],
89
82
  null=True,
90
83
  blank=True,
91
- ) # type: ignore
92
-
84
+ )
93
85
  state = models.OneToOneField(
94
86
  "RawPdfState",
95
87
  on_delete=models.SET_NULL,
96
88
  blank=True,
97
89
  null=True,
98
90
  related_name="raw_pdf_file",
99
- ) # type: ignore
91
+ )
92
+ patient = models.ForeignKey(
93
+ "Patient",
94
+ on_delete=models.SET_NULL,
95
+ blank=True,
96
+ null=True,
97
+ related_name="raw_pdf_files",
98
+ )
99
+ sensitive_meta = models.ForeignKey(
100
+ "SensitiveMeta",
101
+ on_delete=models.SET_NULL,
102
+ related_name="raw_pdf_files",
103
+ null=True,
104
+ blank=True,
105
+ )
106
+ state_report_processing_required = models.BooleanField(default=True)
107
+ state_report_processed = models.BooleanField(default=False)
108
+ raw_meta = models.JSONField(blank=True, null=True)
109
+ anonym_examination_report = models.OneToOneField(
110
+ "AnonymExaminationReport",
111
+ on_delete=models.SET_NULL,
112
+ blank=True,
113
+ null=True,
114
+ related_name="raw_pdf_file",
115
+ )
116
+ anonymized_text = models.TextField(blank=True, null=True)
100
117
 
101
- objects = models.Manager()
118
+ # Type hinting is needed, improve and use correct django types
119
+ if TYPE_CHECKING:
120
+ from endoreg_db.models import (
121
+ AnonymExaminationReport,
122
+ Center,
123
+ Examiner,
124
+ Patient,
125
+ PatientExamination,
126
+ RawPdfState,
127
+ SensitiveMeta,
128
+ )
129
+
130
+ center: models.ForeignKey["Center | None"]
131
+ examination: models.ForeignKey["PatientExamination | None"]
132
+ examiner: models.ForeignKey["Examiner | None"]
133
+ state: models.ForeignKey["RawPdfState | None"]
134
+ patient: models.ForeignKey["Patient | None"]
135
+ sensitive_meta: models.ForeignKey["SensitiveMeta | None"]
136
+ anonym_examination_report: models.OneToOneField[
137
+ "AnonymExaminationReport | None"
138
+ ]
139
+ file = cast(FieldFile, file)
140
+ processed_file = cast(FieldFile, processed_file)
102
141
 
103
142
  @property
104
143
  def uuid(self):
@@ -113,7 +152,7 @@ class RawPdfFile(models.Model):
113
152
  @property
114
153
  def file_path(self) -> Path | None:
115
154
  """
116
- Returns the file path of the stored PDF file if available; otherwise, returns None.
155
+ Returns the file path of the stored report file if available; otherwise, returns None.
117
156
  """
118
157
  from django.db.models.fields.files import FieldFile
119
158
 
@@ -128,35 +167,41 @@ class RawPdfFile(models.Model):
128
167
 
129
168
  def set_file_path(self, file_path: Path):
130
169
  """
131
- Sets the file path of the stored PDF file.
170
+ Sets the file path of the stored report file.
132
171
  """
133
- self.file = File(file_path) # type: ignore
172
+ if not file_path.exists():
173
+ raise FileNotFoundError(f"File path does not exist: {file_path}")
174
+
175
+ save_local_file(self.file, file_path, name=file_path.name, save=False)
134
176
  self.save(update_fields=["file"])
135
177
 
136
178
  @property
137
179
  def anonymized_file_path(self) -> Path | None:
138
180
  """
139
- Returns the file path of the anonymized PDF file if available; otherwise, returns None.
181
+ Returns the file path of the anonymized report file if available; otherwise, returns None.
140
182
  """
141
- if self.anonymized_file and self.anonymized_file.name:
183
+ if self.processed_file and self.processed_file.name:
142
184
  try:
143
- return Path(self.anonymized_file.path)
185
+ return Path(self.processed_file.path)
144
186
  except (ValueError, AttributeError, NotImplementedError):
145
187
  return None
146
188
  return None
147
189
 
148
190
  def set_anonymized_file_path(self, file_path: Path):
149
191
  """
150
- Sets the file path of the anonymized PDF file.
192
+ Sets the file path of the anonymized report file.
151
193
  """
152
- self.anonymized_file = File(file_path) # type: ignore
153
- self.save(update_fields=["anonymized_file"])
194
+ if not file_path.exists():
195
+ raise FileNotFoundError(f"File path does not exist: {file_path}")
196
+
197
+ save_local_file(self.processed_file, file_path, name=file_path.name, save=False)
198
+ self.save(update_fields=["processed_file"])
154
199
 
155
200
  def get_raw_file_path(self) -> Optional[Path]:
156
201
  """
157
- Get the path to the raw PDF file, searching common locations.
202
+ Get the path to the raw report file, searching common locations.
158
203
 
159
- This method attempts to find the original raw PDF file by checking:
204
+ This method attempts to find the original raw report file by checking:
160
205
  1. Direct hash-based path in raw_pdfs/
161
206
  2. Scanning raw_pdfs/ directory for files matching the hash
162
207
  3. Checking the file field if it exists
@@ -171,17 +216,17 @@ class RawPdfFile(models.Model):
171
216
  try:
172
217
  file_path = Path(self.file.path)
173
218
  if file_path.exists():
174
- logger.debug(f"Found raw PDF via file field: {file_path}")
219
+ logger.debug(f"Found raw report via file field: {file_path}")
175
220
  return file_path
176
221
  except (ValueError, AttributeError, NotImplementedError):
177
222
  pass
178
223
 
179
224
  # Define potential raw directories
180
225
  raw_dirs = [
181
- PDF_DIR / "sensitive", # Files might be in sensitive dir
182
- Path(settings.BASE_DIR) / "data" / "raw_pdfs",
226
+ SENSITIVE_REPORT_DIR, # Files might be in sensitive dir
227
+ Path(settings.BASE_DIR) / "data" / "temporary_reports",
183
228
  Path(settings.BASE_DIR) / "data" / "pdfs" / "raw",
184
- PDF_DIR, # General PDF directory
229
+ IMPORT_REPORT_DIR, # General report directory
185
230
  ]
186
231
 
187
232
  # Check direct hash-based name in each directory
@@ -191,7 +236,7 @@ class RawPdfFile(models.Model):
191
236
 
192
237
  hash_path = raw_dir / f"{self.pdf_hash}.pdf"
193
238
  if hash_path.exists():
194
- logger.debug(f"Found raw PDF at: {hash_path}")
239
+ logger.debug(f"Found raw report at: {hash_path}")
195
240
  return hash_path
196
241
 
197
242
  # Scan directories for matching hash
@@ -203,19 +248,19 @@ class RawPdfFile(models.Model):
203
248
  try:
204
249
  file_hash = get_pdf_hash(file_path)
205
250
  if file_hash == self.pdf_hash:
206
- logger.debug(f"Found matching PDF by hash: {file_path}")
251
+ logger.debug(f"Found matching report by hash: {file_path}")
207
252
  return file_path
208
253
  except Exception as e:
209
254
  logger.debug(f"Error checking {file_path}: {e}")
210
255
  continue
211
256
 
212
- logger.warning(f"No raw file found for PDF hash: {self.pdf_hash}")
257
+ logger.warning(f"No raw file found for report hash: {self.pdf_hash}")
213
258
  return None
214
259
 
215
260
  @property
216
261
  def file_url(self):
217
262
  """
218
- Returns the URL of the stored PDF file if available; otherwise, returns None.
263
+ Returns the URL of the stored report file if available; otherwise, returns None.
219
264
  """
220
265
  try:
221
266
  return self.file.url if self.file and self.file.name else None
@@ -225,61 +270,20 @@ class RawPdfFile(models.Model):
225
270
  @property
226
271
  def anonymized_file_url(self):
227
272
  """
228
- Returns the URL of the stored PDF file if available; otherwise, returns None.
273
+ Returns the URL of the stored report file if available; otherwise, returns None.
229
274
  """
230
275
  try:
231
276
  return (
232
- self.anonymized_file.url
233
- if self.anonymized_file and self.anonymized_file.name
277
+ self.processed_file.url
278
+ if self.processed_file and self.processed_file.name
234
279
  else None
235
280
  )
236
281
  except (ValueError, AttributeError):
237
282
  return None
238
283
 
239
- patient = models.ForeignKey(
240
- "Patient",
241
- on_delete=models.SET_NULL,
242
- blank=True,
243
- null=True,
244
- related_name="raw_pdf_files",
245
- ) # type: ignore
246
- sensitive_meta = models.ForeignKey(
247
- "SensitiveMeta",
248
- on_delete=models.SET_NULL,
249
- related_name="raw_pdf_files",
250
- null=True,
251
- blank=True,
252
- ) # type: ignore
253
- state_report_processing_required = models.BooleanField(default=True)
254
- state_report_processed = models.BooleanField(default=False)
255
- raw_meta = models.JSONField(blank=True, null=True)
256
- anonym_examination_report = models.OneToOneField(
257
- "AnonymExaminationReport",
258
- on_delete=models.SET_NULL,
259
- blank=True,
260
- null=True,
261
- related_name="raw_pdf_file",
262
- ) # type: ignore
263
- anonymized_text = models.TextField(blank=True, null=True)
264
-
265
- # Type hinting is needed, improve and use correct django types
266
- if TYPE_CHECKING:
267
- file: Optional[Union[models.FieldFile, models.FileField]]
268
- anonymized_file: Optional[Union[models.FieldFile, models.FileField]]
269
- pdf_type: Optional[models.ForeignKey]
270
- examination: Optional[models.ForeignKey["PatientExamination"]]
271
- examiner: Optional[models.ForeignKey["Examiner"]]
272
- patient: Optional[models.ForeignKey["Patient"]]
273
- center: Optional[models.ForeignKey["Center"]]
274
- anonym_examination_report: Optional[
275
- models.OneToOneField["AnonymExaminationReport"]
276
- ]
277
- sensitive_meta: Optional[models.ForeignKey["SensitiveMeta"]]
278
- state: Optional[models.ForeignKey["RawPdfState"]]
279
-
280
284
  def __str__(self):
281
285
  """
282
- Return a string representation of the RawPdfFile, including its PDF hash, type, and center.
286
+ Return a string representation of the RawPdfFile, including its report hash, type, and center.
283
287
  """
284
288
  str_repr = f"{self.pdf_hash} ({self.pdf_type}, {self.center})"
285
289
  return str_repr
@@ -288,34 +292,54 @@ class RawPdfFile(models.Model):
288
292
  """
289
293
  Deletes the RawPdfFile instance from the database and removes the associated file from storage if it exists.
290
294
 
291
- This method ensures that the physical PDF file is deleted from the file system after the database record is removed. Logs warnings or errors if the file cannot be found or deleted.
295
+ This method ensures that the physical report file is deleted from the file system after the database record is removed. Logs warnings or errors if the file cannot be found or deleted.
292
296
  """
293
- # Call the original delete method first to remove DB record
294
- if self.file:
295
- try:
296
- if self.file_path:
297
- os.remove(Path(self.file_path))
298
- logger.info("Original file removed: %s", self.file)
299
- except Exception as e:
300
- logger.warning(
301
- f"Could not get file path for {self.file.name} before deletion: {e}"
302
- )
303
- if self.anonymized_file:
304
- try:
305
- if self.anonymized_file_path:
306
- os.remove(Path(self.anonymized_file_path))
307
- logger.info(
308
- "Anonymized file removed: %s", self.anonymized_file.name
309
- )
310
- except OSError as e:
311
- logger.error(
312
- "Error removing anonymized file %s: %s",
313
- self.anonymized_file.name,
314
- e,
315
- )
297
+ primary_name = self.file.name if self.file and self.file.name else None
298
+ anonymized_name = (
299
+ self.processed_file.name
300
+ if self.processed_file and self.processed_file.name
301
+ else None
302
+ )
303
+
304
+ if delete_field_file(self.file, missing_ok=True, save=False):
305
+ logger.info("Original file removed from storage: %s", primary_name)
306
+ if delete_field_file(self.processed_file, missing_ok=True, save=False):
307
+ logger.info("Anonymized file removed from storage: %s", anonymized_name)
316
308
 
317
309
  super().delete(*args, **kwargs)
318
310
 
311
+ # --- Convenience state/meta helpers used in tests and admin workflows ---
312
+
313
+ def mark_sensitive_meta_processed(self, *, save: bool = True) -> "RawPdfFile":
314
+ """
315
+ Mark this video's processing state as having its sensitive meta fully processed.
316
+ This proxies to the related VideoState and persists by default.
317
+ """
318
+ sm = self.sensitive_meta
319
+ from endoreg_db.models.metadata.sensitive_meta import SensitiveMeta
320
+
321
+ if not isinstance(sm, SensitiveMeta):
322
+ raise AttributeError()
323
+ state = self.get_or_create_state()
324
+ state.mark_sensitive_meta_processed(save=save)
325
+ return self
326
+
327
+ def mark_sensitive_meta_verified(self) -> "RawPdfFile":
328
+ """
329
+ Mark the associated SensitiveMeta as verified by setting both DOB and names as verified.
330
+ Ensures the SensitiveMeta and its state exist.
331
+ """
332
+ sm = self.sensitive_meta
333
+ # Use SensitiveMeta methods to update underlying SensitiveMetaState
334
+ from endoreg_db.models.metadata.sensitive_meta import SensitiveMeta
335
+
336
+ if not isinstance(sm, SensitiveMeta):
337
+ raise AttributeError()
338
+
339
+ sm.mark_dob_verified()
340
+ sm.mark_names_verified()
341
+ return self
342
+
319
343
  def validate_metadata_annotation(
320
344
  self, extracted_data_dict: Optional[dict] = None
321
345
  ) -> bool:
@@ -326,16 +350,17 @@ class RawPdfFile(models.Model):
326
350
  It also ensures the video file is properly saved after the metadata update.
327
351
  """
328
352
 
329
- if not self.sensitive_meta:
330
- logger.error("No sensitive meta data associated with this PDF file.")
331
- return False
353
+ self.mark_sensitive_meta_processed()
354
+ self.mark_sensitive_meta_verified()
332
355
 
333
356
  if not extracted_data_dict:
334
357
  logger.error("No extracted data provided for validation.")
335
358
  return False
336
359
 
337
- # Update sensitive meta with the provided data
338
- self.sensitive_meta.update_from_dict(extracted_data_dict)
360
+ if extracted_data_dict:
361
+ self.sensitive_meta.update_from_dict(extracted_data_dict)
362
+ else:
363
+ return False
339
364
 
340
365
  # Save the sensitive meta to ensure changes are persisted
341
366
  self.sensitive_meta.save()
@@ -343,25 +368,22 @@ class RawPdfFile(models.Model):
343
368
  # Save the RawPdfFile instance to ensure all changes are saved
344
369
  self.save()
345
370
 
346
- logger.info(f"Metadata for PDF {self.pk} validated and updated successfully.")
347
-
348
- if self.file_path:
349
- try:
350
- os.unlink(self.file_path) # Delete the original file if it exists
351
- except OSError as e:
352
- logger.error(f"Error removing original file {self.file_path}: {e}")
371
+ logger.info(
372
+ f"Metadata for report {self.pk} validated and updated successfully."
373
+ )
353
374
 
354
- if self.anonymized_file_path:
355
- try:
356
- os.unlink(self.anonymized_file_path)
357
- except OSError as e:
358
- logger.error(
359
- f"Error removing anonymized file {self.anonymized_file_path}: {e}"
360
- )
375
+ deleted_original = delete_field_file(self.file, missing_ok=True, save=False)
376
+ deleted_anonymized = delete_field_file(
377
+ self.processed_file, missing_ok=True, save=False
378
+ )
379
+ self.get_or_create_state().mark_anonymization_validated()
361
380
 
362
- self.save() # Save the model to persist the cleared file fields
381
+ if deleted_original or deleted_anonymized:
382
+ self.save(
383
+ update_fields=["file", "processed_file"]
384
+ ) # Persist cleared fields
363
385
 
364
- logger.info(f"Files for PDF {self.pk} deleted successfully.")
386
+ logger.info(f"Files for report {self.pk} deleted successfully.")
365
387
  return True
366
388
 
367
389
  @classmethod
@@ -375,8 +397,8 @@ class RawPdfFile(models.Model):
375
397
  Creates a RawPdfFile instance from a file and center name, ensuring an associated RawPdfState exists.
376
398
 
377
399
  Parameters:
378
- file_path (Path): Path to the source PDF file.
379
- center_name (str): Name of the center to associate with the PDF.
400
+ file_path (Path): Path to the source report file.
401
+ center_name (str): Name of the center to associate with the report.
380
402
  delete_source (bool): Whether to delete the source file after processing. Defaults to True.
381
403
 
382
404
  Returns:
@@ -400,12 +422,12 @@ class RawPdfFile(models.Model):
400
422
  delete_source=True,
401
423
  ):
402
424
  """
403
- Creates or retrieves a RawPdfFile instance from a given PDF file path and center name.
425
+ Creates or retrieves a RawPdfFile instance from a given report file path and center name.
404
426
 
405
- If a RawPdfFile with the same PDF hash already exists, verifies the file exists in storage and restores it if missing. Otherwise, creates a new RawPdfFile, assigns the file, and saves it to storage. Optionally deletes the source file after processing.
427
+ If a RawPdfFile with the same report hash already exists, verifies the file exists in storage and restores it if missing. Otherwise, creates a new RawPdfFile, assigns the file, and saves it to storage. Optionally deletes the source file after processing.
406
428
 
407
429
  Parameters:
408
- file_path (Path): Path to the source PDF file.
430
+ file_path (Path): Path to the source report file.
409
431
  center_name (str): Name of the center to associate with the file.
410
432
  save (bool, optional): Deprecated; saving occurs internally.
411
433
  delete_source (bool, optional): Whether to delete the source file after processing (default True).
@@ -416,7 +438,7 @@ class RawPdfFile(models.Model):
416
438
  Raises:
417
439
  FileNotFoundError: If the source file does not exist.
418
440
  Center.DoesNotExist: If the specified center is not found.
419
- ValueError: If the PDF hash cannot be calculated.
441
+ ValueError: If the report hash cannot be calculated.
420
442
  IOError: If the file fails to save to storage.
421
443
  """
422
444
  from endoreg_db.models.administration import Center
@@ -460,7 +482,7 @@ class RawPdfFile(models.Model):
460
482
  django_file = File(
461
483
  f, name=Path(_file.name).name
462
484
  ) # Use existing name if possible
463
- existing_pdf_file.file = django_file # type: ignore
485
+ existing_pdf_file.file = django_file
464
486
  existing_pdf_file.save(
465
487
  update_fields=["file"]
466
488
  ) # Only update file field
@@ -554,64 +576,42 @@ class RawPdfFile(models.Model):
554
576
  # Ensure hash is calculated before the first save if possible and not already set
555
577
  # This is primarily a fallback if instance created manually without using create_from_file
556
578
  """
557
- Saves the RawPdfFile instance, ensuring the PDF hash is set and related fields are derived from metadata.
579
+ Saves the RawPdfFile instance, ensuring the report hash is set and related fields are derived from metadata.
558
580
 
559
- If the PDF hash is missing, attempts to calculate it from the file before saving. Validates that the file has a `.pdf` extension. If related fields such as patient, examination, center, or examiner are unset but available in the associated sensitive metadata, they are populated accordingly before saving.
581
+ If the report hash is missing, attempts to calculate it from the file before saving. Validates that the file has a `.pdf` extension. If related fields such as patient, examination, center, or examiner are unset but available in the associated sensitive metadata, they are populated accordingly before saving.
560
582
  """
561
583
  if not self.pk and not self.pdf_hash and self.file:
562
584
  try:
563
- file_path = Path(self.file.path).resolve()
564
- if not file_path.exists():
565
- raise FileNotFoundError(f"File path does not exist: {file_path}")
566
- # Read from the file object before it's saved by storage
567
- self.file.open("rb") # Ensure file is open
568
- self.file.seek(0) # Go to beginning
569
- self.pdf_hash = get_pdf_hash(
570
- file_path
571
- ) # Assuming get_pdf_hash can handle file obj
572
- self.file.seek(0) # Reset position
573
- self.file.close() # Close after reading
574
- logger.info(f"Calculated hash during pre-save for {self.file.name}")
575
- except Exception as e:
585
+ with ensure_local_file(self.file) as local_path:
586
+ self.pdf_hash = get_pdf_hash(local_path)
587
+ logger.info(
588
+ "Calculated hash during pre-save for %s", self.file.name
589
+ )
590
+ except Exception as exc:
576
591
  logger.warning(
577
592
  "Could not calculate hash before initial save for %s: %s",
578
593
  self.file.name,
579
- e,
594
+ exc,
580
595
  )
581
- # Ensure file is closed if opened
582
- if hasattr(self.file, "closed") and not self.file.closed:
583
- self.file.close()
584
596
 
585
597
  if self.file and not self.file.name.endswith(".pdf"):
586
- raise ValidationError("Only PDF files are allowed")
598
+ raise ValidationError("Only report files are allowed")
587
599
 
588
600
  # If hash is still missing after potential creation logic (e.g., direct instantiation)
589
601
  # and the file exists in storage, try calculating it from storage path.
590
602
  # This is less ideal as it requires the file to be saved first.
591
- if (
592
- not self.pdf_hash
593
- and self.pk
594
- and self.file
595
- and self.file.storage.exists(self.file.name)
596
- ):
603
+ if not self.pdf_hash and self.pk and self.file and file_exists(self.file):
597
604
  try:
598
- file_path = Path(self.file.path).resolve()
599
- if not file_path.exists():
600
- raise FileNotFoundError(f"File path does not exist: {file_path}")
601
- logger.warning(
602
- f"Hash missing for saved file {self.file.name}. Recalculating."
603
- )
604
- with self.file.storage.open(self.file.name, "rb") as f:
605
- self.pdf_hash = get_pdf_hash(
606
- file_path
607
- ) # Assuming get_pdf_hash handles file obj
608
- # No need to save again just for hash unless update_fields is used carefully
609
- # Let the main super().save() handle saving the hash if it changed
610
- except Exception as e:
605
+ with ensure_local_file(self.file) as local_path:
606
+ logger.warning(
607
+ "Hash missing for saved file %s. Recalculating.", self.file.name
608
+ )
609
+ self.pdf_hash = get_pdf_hash(local_path)
610
+ except Exception as exc:
611
611
  logger.error(
612
612
  "Could not calculate hash during save for existing file %s: %s",
613
613
  self.file.name,
614
- e,
614
+ exc,
615
615
  )
616
616
 
617
617
  # Derive related fields from sensitive_meta if available
@@ -651,7 +651,7 @@ class RawPdfFile(models.Model):
651
651
  # This method might still be useful if called explicitly, but create_from_file now handles restoration
652
652
  # Ensure fallback_file is a Path object.
653
653
  """
654
- Checks if the stored PDF file exists in storage and attempts to restore it from a fallback file path if missing.
654
+ Checks if the stored report file exists in storage and attempts to restore it from a fallback file path if missing.
655
655
 
656
656
  Parameters:
657
657
  fallback_file: Path or string representing the fallback file location to restore from if the stored file is missing.
@@ -750,8 +750,8 @@ class RawPdfFile(models.Model):
750
750
  return settings_dict
751
751
 
752
752
  @staticmethod
753
- def get_pdf_by_id(pdf_id: int) -> "RawPdfFile":
753
+ def get_pdf_by_pk(pk: int) -> "RawPdfFile":
754
754
  try:
755
- return RawPdfFile.objects.get(pk=pdf_id)
755
+ return RawPdfFile.objects.get(pk=pk)
756
756
  except RawPdfFile.DoesNotExist:
757
- raise ValueError(f"PDF with ID {pdf_id} does not exist.")
757
+ raise ValueError(f"report with ID {pdf_id} does not exist.")