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

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

Potentially problematic release.


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

Files changed (503) hide show
  1. endoreg_db/authz/auth.py +74 -0
  2. endoreg_db/authz/backends.py +168 -0
  3. endoreg_db/authz/management/commands/list_routes.py +18 -0
  4. endoreg_db/authz/middleware.py +83 -0
  5. endoreg_db/authz/permissions.py +127 -0
  6. endoreg_db/authz/policy.py +218 -0
  7. endoreg_db/authz/views_auth.py +66 -0
  8. endoreg_db/config/env.py +13 -8
  9. endoreg_db/data/__init__.py +2 -11
  10. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +3 -3
  11. endoreg_db/data/event_classification/data.yaml +4 -0
  12. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  13. endoreg_db/data/examination/examinations/data.yaml +114 -14
  14. endoreg_db/data/examination/time-type/data.yaml +0 -3
  15. endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
  16. endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
  17. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
  18. endoreg_db/data/finding/00_generic.yaml +35 -0
  19. endoreg_db/data/finding/00_generic_complication.yaml +9 -0
  20. endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
  21. endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
  22. endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
  23. endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
  24. endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
  25. endoreg_db/data/finding_classification/00_generic.yaml +44 -0
  26. endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
  27. endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
  28. endoreg_db/data/finding_classification/02_colonoscopy_baseline.yaml +83 -0
  29. endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
  30. endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
  31. endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
  32. endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
  33. endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
  34. endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
  35. endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
  36. endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
  37. endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
  38. endoreg_db/data/finding_classification_choice/{colonoscopy_not_complete_reason.yaml → 02_colonoscopy_generic.yaml} +1 -1
  39. endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
  40. endoreg_db/data/finding_classification_choice/{colonoscopy_location.yaml → 02_colonoscopy_location.yaml} +23 -4
  41. endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
  42. endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
  43. endoreg_db/data/finding_classification_choice/{colon_lesion_paris.yaml → 02_colonoscopy_polyp_morphology.yaml} +26 -8
  44. endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
  45. endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
  46. endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
  47. endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
  48. endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
  49. endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
  50. endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
  51. endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
  52. endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
  53. endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
  54. endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
  55. endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
  56. endoreg_db/data/finding_type/data.yaml +8 -12
  57. endoreg_db/data/requirement/01_patient_data.yaml +93 -0
  58. endoreg_db/data/requirement/old/colon_polyp_intervention.yaml +49 -0
  59. endoreg_db/data/requirement/old/coloreg_colon_polyp.yaml +49 -0
  60. endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
  61. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +29 -12
  62. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  63. endoreg_db/data/requirement_set/{endoscopy_bleeding_risk.yaml → 02_endoscopy_bleeding_risk.yaml} +0 -6
  64. endoreg_db/data/requirement_set/90_coloreg.yaml +190 -0
  65. endoreg_db/data/requirement_set/_old_ +109 -0
  66. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  67. endoreg_db/data/setup_config.yaml +4 -4
  68. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  69. endoreg_db/exceptions.py +4 -2
  70. endoreg_db/forms/examination_form.py +1 -1
  71. endoreg_db/helpers/data_loader.py +125 -53
  72. endoreg_db/helpers/default_objects.py +116 -81
  73. endoreg_db/import_files/__init__.py +27 -0
  74. endoreg_db/import_files/context/__init__.py +7 -0
  75. endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
  76. endoreg_db/import_files/context/ensure_center.py +17 -0
  77. endoreg_db/import_files/context/file_lock.py +66 -0
  78. endoreg_db/import_files/context/import_context.py +43 -0
  79. endoreg_db/import_files/context/validate_directories.py +56 -0
  80. endoreg_db/import_files/file_storage/__init__.py +15 -0
  81. endoreg_db/import_files/file_storage/create_report_file.py +76 -0
  82. endoreg_db/import_files/file_storage/create_video_file.py +75 -0
  83. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
  84. endoreg_db/import_files/file_storage/state_management.py +400 -0
  85. endoreg_db/import_files/file_storage/storage.py +36 -0
  86. endoreg_db/import_files/import_service.md +26 -0
  87. endoreg_db/import_files/processing/__init__.py +11 -0
  88. endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
  89. endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
  90. endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
  91. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
  92. endoreg_db/import_files/pseudonymization/fake.py +52 -0
  93. endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
  94. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
  95. endoreg_db/import_files/report_import_service.py +141 -0
  96. endoreg_db/import_files/video_import_service.py +150 -0
  97. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  98. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  99. endoreg_db/management/commands/import_report.py +130 -65
  100. endoreg_db/management/commands/import_video.py +9 -10
  101. endoreg_db/management/commands/import_video_with_classification.py +2 -2
  102. endoreg_db/management/commands/list_routes.py +18 -0
  103. endoreg_db/management/commands/load_ai_model_data.py +5 -5
  104. endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
  105. endoreg_db/management/commands/load_base_db_data.py +5 -134
  106. endoreg_db/management/commands/load_center_data.py +12 -12
  107. endoreg_db/management/commands/load_contraindication_data.py +14 -16
  108. endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
  109. endoreg_db/management/commands/load_disease_classification_data.py +15 -18
  110. endoreg_db/management/commands/load_disease_data.py +25 -28
  111. endoreg_db/management/commands/load_endoscope_data.py +20 -27
  112. endoreg_db/management/commands/load_event_data.py +14 -16
  113. endoreg_db/management/commands/load_examination_data.py +31 -44
  114. endoreg_db/management/commands/load_examination_indication_data.py +20 -21
  115. endoreg_db/management/commands/load_finding_data.py +52 -80
  116. endoreg_db/management/commands/load_information_source.py +21 -23
  117. endoreg_db/management/commands/load_lab_value_data.py +17 -26
  118. endoreg_db/management/commands/load_medication_data.py +13 -12
  119. endoreg_db/management/commands/load_organ_data.py +15 -19
  120. endoreg_db/management/commands/load_pdf_type_data.py +19 -18
  121. endoreg_db/management/commands/load_profession_data.py +14 -17
  122. endoreg_db/management/commands/load_qualification_data.py +20 -23
  123. endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
  124. endoreg_db/management/commands/load_requirement_data.py +62 -39
  125. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  126. endoreg_db/management/commands/load_risk_data.py +7 -6
  127. endoreg_db/management/commands/load_shift_data.py +20 -23
  128. endoreg_db/management/commands/load_tag_data.py +8 -11
  129. endoreg_db/management/commands/load_unit_data.py +17 -19
  130. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  131. endoreg_db/management/commands/start_filewatcher.py +46 -37
  132. endoreg_db/management/commands/storage_management.py +271 -203
  133. endoreg_db/management/commands/validate_video_files.py +1 -5
  134. endoreg_db/migrations/0001_initial.py +297 -250
  135. endoreg_db/models/__init__.py +78 -123
  136. endoreg_db/models/administration/__init__.py +21 -42
  137. endoreg_db/models/administration/ai/active_model.py +2 -2
  138. endoreg_db/models/administration/ai/ai_model.py +7 -6
  139. endoreg_db/models/administration/case/__init__.py +1 -15
  140. endoreg_db/models/administration/case/case.py +3 -3
  141. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  142. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  143. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  144. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  145. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  146. endoreg_db/models/administration/center/center.py +33 -19
  147. endoreg_db/models/administration/center/center_product.py +12 -9
  148. endoreg_db/models/administration/center/center_resource.py +25 -19
  149. endoreg_db/models/administration/center/center_shift.py +21 -17
  150. endoreg_db/models/administration/center/center_waste.py +16 -8
  151. endoreg_db/models/administration/person/__init__.py +2 -0
  152. endoreg_db/models/administration/person/employee/employee.py +10 -5
  153. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  154. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  155. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  156. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  157. endoreg_db/models/administration/person/patient/patient.py +129 -100
  158. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  159. endoreg_db/models/administration/person/person.py +4 -0
  160. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  161. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  162. endoreg_db/models/administration/product/product.py +20 -15
  163. endoreg_db/models/administration/product/product_material.py +17 -18
  164. endoreg_db/models/administration/product/product_weight.py +12 -8
  165. endoreg_db/models/administration/product/reference_product.py +23 -55
  166. endoreg_db/models/administration/qualification/qualification.py +7 -3
  167. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  168. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  169. endoreg_db/models/administration/shift/shift.py +16 -12
  170. endoreg_db/models/administration/shift/shift_type.py +23 -31
  171. endoreg_db/models/label/__init__.py +8 -9
  172. endoreg_db/models/label/annotation/image_classification.py +10 -9
  173. endoreg_db/models/label/annotation/video_segmentation_annotation.py +23 -28
  174. endoreg_db/models/label/label.py +15 -15
  175. endoreg_db/models/label/label_set.py +19 -6
  176. endoreg_db/models/label/label_type.py +1 -1
  177. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  178. endoreg_db/models/label/label_video_segment/label_video_segment.py +98 -102
  179. endoreg_db/models/label/video_segmentation_label.py +4 -0
  180. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  181. endoreg_db/models/media/frame/frame.py +22 -22
  182. endoreg_db/models/media/pdf/raw_pdf.py +194 -194
  183. endoreg_db/models/media/pdf/report_file.py +25 -29
  184. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +55 -47
  185. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  186. endoreg_db/models/media/processing_history/__init__.py +5 -0
  187. endoreg_db/models/media/processing_history/processing_history.py +96 -0
  188. endoreg_db/models/media/video/__init__.py +1 -0
  189. endoreg_db/models/media/video/create_from_file.py +139 -77
  190. endoreg_db/models/media/video/pipe_2.py +8 -9
  191. endoreg_db/models/media/video/video_file.py +174 -112
  192. endoreg_db/models/media/video/video_file_ai.py +288 -74
  193. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  194. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  195. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  196. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  197. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  198. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  199. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  200. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  201. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  202. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  203. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  204. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  205. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  206. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  207. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  208. endoreg_db/models/media/video/video_file_io.py +113 -61
  209. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  210. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  211. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  212. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  213. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  214. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  215. endoreg_db/models/media/video/video_file_segments.py +24 -17
  216. endoreg_db/models/media/video/video_metadata.py +19 -35
  217. endoreg_db/models/media/video/video_processing.py +96 -95
  218. endoreg_db/models/medical/contraindication/README.md +1 -0
  219. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  220. endoreg_db/models/medical/disease.py +22 -16
  221. endoreg_db/models/medical/event.py +31 -18
  222. endoreg_db/models/medical/examination/__init__.py +13 -6
  223. endoreg_db/models/medical/examination/examination.py +39 -20
  224. endoreg_db/models/medical/examination/examination_indication.py +30 -95
  225. endoreg_db/models/medical/examination/examination_time.py +23 -8
  226. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  227. endoreg_db/models/medical/examination/examination_type.py +3 -4
  228. endoreg_db/models/medical/finding/finding.py +32 -40
  229. endoreg_db/models/medical/finding/finding_classification.py +42 -72
  230. endoreg_db/models/medical/finding/finding_intervention.py +25 -22
  231. endoreg_db/models/medical/finding/finding_type.py +13 -12
  232. endoreg_db/models/medical/hardware/endoscope.py +26 -26
  233. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  234. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  235. endoreg_db/models/medical/medication/medication.py +22 -10
  236. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  237. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  238. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  239. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  240. endoreg_db/models/medical/organ/__init__.py +15 -12
  241. endoreg_db/models/medical/patient/medication_examples.py +6 -6
  242. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  243. endoreg_db/models/medical/patient/patient_event.py +19 -22
  244. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  245. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  246. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  247. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  248. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  249. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  250. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  251. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  252. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  253. endoreg_db/models/medical/risk/risk.py +7 -6
  254. endoreg_db/models/medical/risk/risk_type.py +8 -5
  255. endoreg_db/models/metadata/model_meta.py +60 -29
  256. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  257. endoreg_db/models/metadata/pdf_meta.py +31 -24
  258. endoreg_db/models/metadata/sensitive_meta.py +105 -85
  259. endoreg_db/models/metadata/sensitive_meta_logic.py +198 -103
  260. endoreg_db/models/metadata/video_meta.py +51 -31
  261. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  262. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  263. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  264. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  265. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  266. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  267. endoreg_db/models/other/emission/emission_factor.py +18 -8
  268. endoreg_db/models/other/gender.py +10 -5
  269. endoreg_db/models/other/information_source.py +50 -29
  270. endoreg_db/models/other/material.py +9 -5
  271. endoreg_db/models/other/resource.py +6 -4
  272. endoreg_db/models/other/tag.py +10 -5
  273. endoreg_db/models/other/transport_route.py +13 -8
  274. endoreg_db/models/other/unit.py +10 -6
  275. endoreg_db/models/other/waste.py +6 -5
  276. endoreg_db/models/report/report.py +6 -0
  277. endoreg_db/models/requirement/requirement.py +329 -361
  278. endoreg_db/models/requirement/requirement_error.py +85 -0
  279. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  280. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  281. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  282. endoreg_db/models/requirement/requirement_operator.py +103 -112
  283. endoreg_db/models/requirement/requirement_set.py +74 -57
  284. endoreg_db/models/state/__init__.py +4 -4
  285. endoreg_db/models/state/abstract.py +2 -2
  286. endoreg_db/models/state/anonymization.py +12 -0
  287. endoreg_db/models/state/audit_ledger.py +49 -51
  288. endoreg_db/models/state/label_video_segment.py +9 -0
  289. endoreg_db/models/state/raw_pdf.py +101 -68
  290. endoreg_db/models/state/sensitive_meta.py +6 -2
  291. endoreg_db/models/state/video.py +110 -90
  292. endoreg_db/models/upload_job.py +35 -34
  293. endoreg_db/models/utils.py +28 -25
  294. endoreg_db/queries/__init__.py +3 -1
  295. endoreg_db/root_urls.py +21 -2
  296. endoreg_db/schemas/examination_evaluation.py +1 -1
  297. endoreg_db/serializers/__init__.py +2 -10
  298. endoreg_db/serializers/anonymization.py +18 -10
  299. endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
  300. endoreg_db/serializers/meta/__init__.py +1 -6
  301. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  302. endoreg_db/serializers/misc/file_overview.py +11 -99
  303. endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
  304. endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
  305. endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
  306. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  307. endoreg_db/serializers/video/segmentation.py +2 -1
  308. endoreg_db/serializers/video/video_file_list.py +65 -34
  309. endoreg_db/serializers/video/video_processing_history.py +20 -5
  310. endoreg_db/services/__old/pdf_import.py +1487 -0
  311. endoreg_db/services/__old/video_import.py +1306 -0
  312. endoreg_db/services/anonymization.py +128 -89
  313. endoreg_db/services/lookup_service.py +65 -52
  314. endoreg_db/services/lookup_store.py +2 -2
  315. endoreg_db/services/pdf_import.py +0 -1382
  316. endoreg_db/services/report_import.py +10 -0
  317. endoreg_db/services/video_import.py +6 -1255
  318. endoreg_db/tasks/upload_tasks.py +79 -70
  319. endoreg_db/tasks/video_ingest.py +8 -4
  320. endoreg_db/urls/__init__.py +5 -32
  321. endoreg_db/urls/ai.py +32 -0
  322. endoreg_db/urls/media.py +121 -83
  323. endoreg_db/urls/root_urls.py +29 -0
  324. endoreg_db/utils/__init__.py +15 -5
  325. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  326. endoreg_db/utils/case_generator/__init__.py +3 -0
  327. endoreg_db/utils/dataloader.py +142 -40
  328. endoreg_db/utils/defaults/set_default_center.py +32 -0
  329. endoreg_db/utils/names.py +22 -16
  330. endoreg_db/utils/paths.py +110 -46
  331. endoreg_db/utils/permissions.py +2 -1
  332. endoreg_db/utils/pipelines/Readme.md +1 -1
  333. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  334. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +655 -0
  335. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
  336. endoreg_db/utils/setup_config.py +8 -5
  337. endoreg_db/utils/storage.py +115 -0
  338. endoreg_db/utils/validate_endo_roi.py +8 -2
  339. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  340. endoreg_db/views/__init__.py +85 -183
  341. endoreg_db/views/ai/__init__.py +8 -0
  342. endoreg_db/views/ai/label.py +155 -0
  343. endoreg_db/views/anonymization/media_management.py +202 -166
  344. endoreg_db/views/anonymization/overview.py +99 -67
  345. endoreg_db/views/anonymization/validate.py +182 -44
  346. endoreg_db/views/media/__init__.py +7 -20
  347. endoreg_db/views/media/pdf_media.py +197 -174
  348. endoreg_db/views/media/sensitive_metadata.py +193 -138
  349. endoreg_db/views/media/video_media.py +89 -82
  350. endoreg_db/views/meta/__init__.py +0 -8
  351. endoreg_db/views/misc/__init__.py +1 -7
  352. endoreg_db/views/misc/upload_views.py +94 -93
  353. endoreg_db/views/patient/patient.py +5 -4
  354. endoreg_db/views/report/__init__.py +5 -7
  355. endoreg_db/views/{pdf → report}/reimport.py +22 -22
  356. endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +46 -39
  357. endoreg_db/views/requirement/evaluate.py +188 -187
  358. endoreg_db/views/requirement/lookup.py +17 -3
  359. endoreg_db/views/requirement/lookup_store.py +22 -90
  360. endoreg_db/views/requirement/requirement_utils.py +89 -0
  361. endoreg_db/views/video/__init__.py +23 -24
  362. endoreg_db/views/video/correction.py +201 -172
  363. endoreg_db/views/video/reimport.py +1 -1
  364. endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +77 -40
  365. endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
  366. endoreg_db/views/video/video_stream.py +7 -8
  367. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +7 -3
  368. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +391 -413
  369. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
  370. endoreg_db/data/finding/anatomy_colon.yaml +0 -128
  371. endoreg_db/data/finding/colonoscopy.yaml +0 -40
  372. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
  373. endoreg_db/data/finding/complication.yaml +0 -16
  374. endoreg_db/data/finding/data.yaml +0 -105
  375. endoreg_db/data/finding/examination_setting.yaml +0 -16
  376. endoreg_db/data/finding/medication_related.yaml +0 -18
  377. endoreg_db/data/finding/outcome.yaml +0 -12
  378. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +0 -95
  379. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
  380. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
  381. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  382. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  383. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -68
  384. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  385. endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -80
  386. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
  387. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
  388. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
  389. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
  390. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
  391. endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
  392. endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
  393. endoreg_db/data/finding_classification/histology_colo.yaml +0 -51
  394. endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
  395. endoreg_db/data/finding_classification/medication_related.yaml +0 -23
  396. endoreg_db/data/finding_classification/visualized.yaml +0 -33
  397. endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  398. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  399. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  400. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  401. endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  402. endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  403. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  404. endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  405. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
  406. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  407. endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
  408. endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
  409. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  410. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
  411. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
  412. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  413. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  414. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
  415. endoreg_db/data/requirement/age.yaml +0 -26
  416. endoreg_db/data/requirement/gender.yaml +0 -25
  417. endoreg_db/management/commands/init_default_ai_model.py +0 -112
  418. endoreg_db/management/commands/reset_celery_schedule.py +0 -9
  419. endoreg_db/management/commands/validate_video.py +0 -204
  420. endoreg_db/migrations/0002_add_video_correction_models.py +0 -52
  421. endoreg_db/migrations/0003_add_center_display_name.py +0 -30
  422. endoreg_db/models/administration/permissions/__init__.py +0 -44
  423. endoreg_db/models/rule/__init__.py +0 -13
  424. endoreg_db/models/rule/rule.py +0 -27
  425. endoreg_db/models/rule/rule_applicator.py +0 -224
  426. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  427. endoreg_db/models/rule/rule_type.py +0 -20
  428. endoreg_db/models/rule/ruleset.py +0 -17
  429. endoreg_db/renames.yml +0 -8
  430. endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
  431. endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
  432. endoreg_db/serializers/_old/video.py +0 -71
  433. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
  434. endoreg_db/serializers/meta/report_meta.py +0 -53
  435. endoreg_db/serializers/report/__init__.py +0 -9
  436. endoreg_db/serializers/report/mixins.py +0 -45
  437. endoreg_db/serializers/report/report.py +0 -105
  438. endoreg_db/serializers/report/report_list.py +0 -22
  439. endoreg_db/serializers/report/secure_file_url.py +0 -26
  440. endoreg_db/serializers/video/video_metadata.py +0 -105
  441. endoreg_db/services/requirements_object.py +0 -147
  442. endoreg_db/services/storage_aware_video_processor.py +0 -344
  443. endoreg_db/urls/files.py +0 -6
  444. endoreg_db/urls/label_video_segment_validate.py +0 -33
  445. endoreg_db/urls/label_video_segments.py +0 -46
  446. endoreg_db/urls/report.py +0 -48
  447. endoreg_db/urls/video.py +0 -61
  448. endoreg_db/utils/case_generator/case_generator.py +0 -159
  449. endoreg_db/utils/case_generator/utils.py +0 -30
  450. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +0 -368
  451. endoreg_db/views/label/__init__.py +0 -5
  452. endoreg_db/views/label/label.py +0 -15
  453. endoreg_db/views/label_video_segment/__init__.py +0 -16
  454. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
  455. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
  456. endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
  457. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
  458. endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
  459. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
  460. endoreg_db/views/label_video_segment/validate.py +0 -226
  461. endoreg_db/views/media/segments.py +0 -71
  462. endoreg_db/views/meta/available_files_list.py +0 -146
  463. endoreg_db/views/meta/report_meta.py +0 -53
  464. endoreg_db/views/meta/sensitive_meta_detail.py +0 -148
  465. endoreg_db/views/misc/secure_file_serving_view.py +0 -80
  466. endoreg_db/views/misc/secure_file_url_view.py +0 -84
  467. endoreg_db/views/misc/secure_url_validate.py +0 -79
  468. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
  469. endoreg_db/views/patient_finding_location/__init__.py +0 -5
  470. endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
  471. endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
  472. endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
  473. endoreg_db/views/pdf/__init__.py +0 -8
  474. endoreg_db/views/report/report_list.py +0 -112
  475. endoreg_db/views/report/report_with_secure_url.py +0 -28
  476. endoreg_db/views/report/start_examination.py +0 -7
  477. endoreg_db/views/video/segmentation.py +0 -274
  478. endoreg_db/views/video/task_status.py +0 -49
  479. endoreg_db/views/video/timeline.py +0 -46
  480. endoreg_db/views/video/video_analyze.py +0 -52
  481. endoreg_db/views.py +0 -0
  482. /endoreg_db/data/requirement/{colonoscopy_baseline_austria.yaml → old/colonoscopy_baseline_austria.yaml} +0 -0
  483. /endoreg_db/data/requirement/{disease_cardiovascular.yaml → old/disease_cardiovascular.yaml} +0 -0
  484. /endoreg_db/data/requirement/{disease_classification_choice_cardiovascular.yaml → old/disease_classification_choice_cardiovascular.yaml} +0 -0
  485. /endoreg_db/data/requirement/{disease_hepatology.yaml → old/disease_hepatology.yaml} +0 -0
  486. /endoreg_db/data/requirement/{disease_misc.yaml → old/disease_misc.yaml} +0 -0
  487. /endoreg_db/data/requirement/{disease_renal.yaml → old/disease_renal.yaml} +0 -0
  488. /endoreg_db/data/requirement/{endoscopy_bleeding_risk.yaml → old/endoscopy_bleeding_risk.yaml} +0 -0
  489. /endoreg_db/data/requirement/{event_cardiology.yaml → old/event_cardiology.yaml} +0 -0
  490. /endoreg_db/data/requirement/{event_requirements.yaml → old/event_requirements.yaml} +0 -0
  491. /endoreg_db/data/requirement/{finding_colon_polyp.yaml → old/finding_colon_polyp.yaml} +0 -0
  492. /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
  493. /endoreg_db/data/requirement/{lab_value.yaml → old/lab_value.yaml} +0 -0
  494. /endoreg_db/data/requirement/{medication.yaml → old/medication.yaml} +0 -0
  495. /endoreg_db/data/requirement_operator/{age.yaml → _old/age.yaml} +0 -0
  496. /endoreg_db/data/requirement_operator/{lab_operators.yaml → _old/lab_operators.yaml} +0 -0
  497. /endoreg_db/data/requirement_operator/{model_operators.yaml → _old/model_operators.yaml} +0 -0
  498. /endoreg_db/{models/media/video/refactor_plan.md → import_files/pseudonymization/__init__.py} +0 -0
  499. /endoreg_db/{models/media/video/video_file_frames.py → import_files/pseudonymization/pseudonymize.py} +0 -0
  500. /endoreg_db/models/{metadata/frame_ocr_result.py → report/__init__.py} +0 -0
  501. /endoreg_db/{urls/sensitive_meta.py → models/report/images.py} +0 -0
  502. /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
  503. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,8 +1,38 @@
1
1
  import os
2
+ from datetime import UTC, datetime
3
+
2
4
  import yaml
3
5
  from django.core.exceptions import ObjectDoesNotExist
4
6
  from django.db import OperationalError, transaction
5
7
 
8
+ from endoreg_db.utils.paths import STORAGE_DIR
9
+
10
+ _WARNING_LOG_PATH = None
11
+
12
+
13
+ def _get_warning_log_path():
14
+ """Return the path used for warning logs, creating it on first access."""
15
+ global _WARNING_LOG_PATH
16
+ if _WARNING_LOG_PATH is None:
17
+ log_dir = STORAGE_DIR / "logs"
18
+ log_dir.mkdir(parents=True, exist_ok=True)
19
+ timestamp = datetime.now(UTC).strftime("%Y%m%d-%H%M%S")
20
+ _WARNING_LOG_PATH = log_dir / f"dataloader_warnings_{timestamp}.log"
21
+ return _WARNING_LOG_PATH
22
+
23
+
24
+ def _record_warning(command, message, verbose, context):
25
+ """Write a warning to stdout (when verbose) and append it to the log file."""
26
+ prefix = f"[{context}] " if context else ""
27
+ full_message = f"{prefix}{message}"
28
+
29
+ if verbose:
30
+ command.stdout.write(command.style.WARNING(full_message))
31
+
32
+ log_path = _get_warning_log_path()
33
+ with open(log_path, "a", encoding="utf-8") as log_file:
34
+ log_file.write(f"{datetime.now(UTC).isoformat()}Z {full_message}\n")
35
+
6
36
 
7
37
  def load_model_data_from_yaml(command, model_name, metadata, verbose):
8
38
  """
@@ -16,10 +46,15 @@ def load_model_data_from_yaml(command, model_name, metadata, verbose):
16
46
  """
17
47
  if verbose:
18
48
  command.stdout.write(f"Start loading {model_name}")
49
+
50
+ warning_log_path = _get_warning_log_path()
51
+ if verbose:
52
+ command.stdout.write(f"Warning log file: {warning_log_path}")
19
53
  model = metadata["model"]
20
54
  dir_path = metadata["dir"]
21
55
  foreign_keys = metadata["foreign_keys"]
22
56
  foreign_key_models = metadata["foreign_key_models"]
57
+ validators = metadata.get("validators", [])
23
58
 
24
59
  _files = [f for f in os.listdir(dir_path) if f.endswith(".yaml")]
25
60
  # sort
@@ -29,16 +64,30 @@ def load_model_data_from_yaml(command, model_name, metadata, verbose):
29
64
  yaml_data = yaml.safe_load(file)
30
65
 
31
66
  load_data_with_foreign_keys(
32
- command, model, yaml_data, foreign_keys, foreign_key_models, verbose
67
+ command,
68
+ model,
69
+ yaml_data,
70
+ foreign_keys,
71
+ foreign_key_models,
72
+ validators,
73
+ verbose,
74
+ log_context=model_name or model.__name__,
33
75
  )
34
76
 
35
77
 
36
78
  def load_data_with_foreign_keys(
37
- command, model, yaml_data, foreign_keys, foreign_key_models, verbose
79
+ command,
80
+ model,
81
+ yaml_data,
82
+ foreign_keys,
83
+ foreign_key_models,
84
+ validators,
85
+ verbose,
86
+ log_context=None,
38
87
  ):
39
88
  """
40
89
  Load YAML data into Django model instances with FK and M2M support.
41
-
90
+
42
91
  Processes each YAML entry to create or update a model instance. For each entry, the
43
92
  function extracts field data and uses the presence of a 'name' field to decide whether
44
93
  to update an existing instance or create a new one. Foreign key fields listed in
@@ -46,34 +95,49 @@ def load_data_with_foreign_keys(
46
95
  contains a list, it is treated as a many-to-many relationship and the corresponding
47
96
  objects are set after the instance is saved. Missing or unresolved foreign keys trigger
48
97
  warnings if verbose output is enabled.
49
-
98
+
50
99
  Parameters:
51
100
  model: The Django model class representing the data.
52
101
  yaml_data: A list of dictionaries representing YAML entries.
53
102
  foreign_keys: A list of foreign key field names to process from each entry.
54
103
  foreign_key_models: The corresponding Django model classes for each foreign key.
104
+ validators: A sequence of callables invoked before persisting each entry. Each
105
+ validator receives a shallow copy of the entry's field dictionary along with
106
+ the original entry and model for context.
55
107
  verbose: If True, prints detailed output and warnings during processing.
108
+ log_context: Label that identifies the source dataset inside the warning log.
56
109
  """
110
+ context_label = log_context or getattr(model, "__name__", "dataloader")
111
+
57
112
  for entry in yaml_data:
58
- fields = entry.get("fields", {})
113
+ raw_fields = entry.get("fields", {})
114
+
115
+ for validator in validators:
116
+ validator(dict(raw_fields), entry=entry, model=model)
117
+
118
+ fields = dict(raw_fields)
59
119
  name = fields.pop("name", None)
60
120
 
121
+ if getattr(model, "_meta", None) and model._meta.model_name == "requirement":
122
+ requirement_types = fields.get("requirement_types", [])
123
+
124
+ if not requirement_types:
125
+ raise ValueError(f"Requirement '{name}' must define at least one requirement_types entry.")
61
126
 
62
127
  ####################
63
- #TODO REMOVE AFTER TRANSLATION SUPPORT IS ADDED
64
- SKIP_NAMES=[
128
+ # TODO REMOVE AFTER TRANSLATION SUPPORT IS ADDED
129
+ SKIP_NAMES = [
65
130
  "name_de", # German name, not used
66
131
  "name_en", # English name, not used
67
132
  "description_de", # German description
68
133
  "description_en", # English description
69
134
  ]
70
135
 
71
-
72
136
  # Remove fields that are not needed
73
137
  for skip_name in SKIP_NAMES:
74
138
  if skip_name in fields:
75
139
  fields.pop(skip_name)
76
- ########################
140
+ # ########################
77
141
 
78
142
  m2m_relationships = {} # Store many-to-many relationships
79
143
  # print(entry)
@@ -88,12 +152,12 @@ def load_data_with_foreign_keys(
88
152
 
89
153
  # Ensure the foreign key exists
90
154
  if target_keys is None:
91
- if verbose:
92
- command.stdout.write(
93
- command.style.WARNING(
94
- f"Foreign key {fk_field} not found in fields"
95
- )
96
- )
155
+ _record_warning(
156
+ command,
157
+ f"Foreign key {fk_field} not found in fields",
158
+ verbose,
159
+ context_label,
160
+ )
97
161
  continue # Skip if no foreign key provided
98
162
 
99
163
  # Process many-to-many fields or foreign keys
@@ -103,29 +167,69 @@ def load_data_with_foreign_keys(
103
167
  try:
104
168
  obj = fk_model.objects.get_by_natural_key(key)
105
169
  except ObjectDoesNotExist:
106
- if verbose:
107
- command.stdout.write(
108
- command.style.WARNING(
109
- f"{fk_model.__name__} with key {key} not found"
110
- )
111
- )
170
+ _record_warning(
171
+ command,
172
+ f"{fk_model.__name__} with key {key} not found",
173
+ verbose,
174
+ context_label,
175
+ )
112
176
  continue
113
177
  related_objects.append(obj)
114
178
  m2m_relationships[fk_field] = related_objects
115
179
  else: # Single foreign key relationship
116
- try:
117
- obj = fk_model.objects.get_by_natural_key(target_keys)
118
- except ObjectDoesNotExist:
119
- if verbose:
120
- command.stdout.write(
121
- command.style.WARNING(
122
- f"{fk_model.__name__} with key {target_keys} not found"
123
- )
180
+ if model.__name__ == "ModelMeta" and fk_field == "labelset":
181
+ labelset_version = fields.pop("labelset_version", None)
182
+
183
+ if isinstance(target_keys, (tuple, list)):
184
+ labelset_name = target_keys[0] if target_keys else None
185
+ if len(target_keys) > 1 and labelset_version in (None, ""):
186
+ labelset_version = target_keys[1]
187
+ else:
188
+ labelset_name = target_keys
189
+
190
+ if not labelset_name:
191
+ _record_warning(
192
+ command,
193
+ "LabelSet name missing for ModelMeta entry",
194
+ verbose,
195
+ context_label,
196
+ )
197
+ continue
198
+
199
+ queryset = fk_model.objects.filter(name=labelset_name)
200
+ if labelset_version not in (None, "", -1):
201
+ try:
202
+ version_value = int(labelset_version)
203
+ except (TypeError, ValueError):
204
+ version_value = labelset_version
205
+ queryset = queryset.filter(version=version_value)
206
+
207
+ obj = queryset.order_by("-version").first()
208
+ if obj is None:
209
+ _record_warning(
210
+ command,
211
+ f"LabelSet '{labelset_name}' (version={labelset_version}) not found",
212
+ verbose,
213
+ context_label,
124
214
  )
125
- continue
126
- fields[fk_field] = obj
215
+ continue
216
+ fields[fk_field] = obj
217
+ else:
218
+ try:
219
+ obj = fk_model.objects.get_by_natural_key(target_keys)
220
+ except ObjectDoesNotExist:
221
+ _record_warning(
222
+ command,
223
+ f"{fk_model.__name__} with key {target_keys} not found",
224
+ verbose,
225
+ context_label,
226
+ )
227
+ continue
228
+ fields[fk_field] = obj
127
229
 
128
230
  # Create or update the main object (avoid update_or_create to prevent SQLite locks)
231
+ version_value = fields.get("version")
232
+
129
233
  def _save_instance():
130
234
  if name is None:
131
235
  # Try to find an existing object by all provided fields
@@ -136,7 +240,11 @@ def load_data_with_foreign_keys(
136
240
  else:
137
241
  created = False
138
242
  else:
139
- obj = model.objects.filter(name=name).first()
243
+ lookup_kwargs = {"name": name}
244
+ if model.__name__ == "LabelSet" and version_value is not None:
245
+ lookup_kwargs["version"] = version_value
246
+
247
+ obj = model.objects.filter(**lookup_kwargs).first()
140
248
  if obj is None:
141
249
  obj = model.objects.create(name=name, **fields)
142
250
  created = True
@@ -157,9 +265,7 @@ def load_data_with_foreign_keys(
157
265
  obj, created = _save_instance()
158
266
 
159
267
  if created and verbose:
160
- command.stdout.write(
161
- command.style.SUCCESS(f"Created {model.__name__} {name}")
162
- )
268
+ command.stdout.write(command.style.SUCCESS(f"Created {model.__name__} {name}"))
163
269
  elif verbose:
164
270
  pass
165
271
 
@@ -168,8 +274,4 @@ def load_data_with_foreign_keys(
168
274
  if related_objs: # Only set if there are objects to set
169
275
  getattr(obj, field_name).set(related_objs)
170
276
  if verbose:
171
- command.stdout.write(
172
- command.style.SUCCESS(
173
- f"Set {len(related_objs)} {field_name} for {model.__name__} {name}"
174
- )
175
- )
277
+ command.stdout.write(command.style.SUCCESS(f"Set {len(related_objs)} {field_name} for {model.__name__} {name}"))
@@ -0,0 +1,32 @@
1
+ import subprocess
2
+ from endoreg_db.models.administration.center import center
3
+
4
+ # Start process with interactive pipes
5
+ proc = subprocess.Popen(
6
+ ["python3", "-i"], # or your target program
7
+ stdin=subprocess.PIPE,
8
+ stdout=subprocess.PIPE,
9
+ stderr=subprocess.STDOUT,
10
+ text=True,
11
+ bufsize=1
12
+ )
13
+
14
+ proc.stdin.write("print('Trying to extract...')\n")
15
+ proc.stdin.flush()
16
+
17
+ try:
18
+ subprocess.run(["python", "manage.py", "load_center_data"], check=True)
19
+ proc.stdout.write("print('found center')")
20
+ except subprocess.CalledProcessError as e:
21
+ proc.stdout.write("print('Didn't find center. Please add it to endoreg_db luxnix or via export DEFAULT_CENTER")
22
+ # """
23
+ # Future Implementation using dialogue
24
+ #"""
25
+ # # Send commands as if from terminal
26
+ # proc.stdin.write("print('You dont have a default center set up yet. Please enter one here.')\n\nprint('Rule: use_this_format_and_connect_words_with_underscore')")
27
+ # proc.stdin.flush()
28
+
29
+ # # Read responses
30
+ # for _ in range(3):
31
+ # line = proc.stdout.readline()
32
+ # print("Selected >>", line.strip())
endoreg_db/utils/names.py CHANGED
@@ -55,22 +55,28 @@ def create_mock_patient_name(gender: str) -> tuple[str, str]:
55
55
 
56
56
 
57
57
  def guess_name_gender(name: str) -> str:
58
- """
59
- Guesses the gender for a given name by using a gender detector and retrieving the corresponding Gender model instance.
60
- Parameters:
61
- name (str): The name for which the gender is to be determined.
62
- Returns:
63
- Gender: The Gender object corresponding to the detected gender name.
64
- Raises:
65
- Gender.DoesNotExist: If no Gender object matching the detected gender is found.
66
- Exception: For any other exceptions that occur during gender detection or database lookup.
67
- """
58
+ """Return a normalized gender slug (male|female|unknown) for the given name.
68
59
 
69
- from endoreg_db.models import Gender
60
+ Uses :mod:`gender_guesser` to infer gender without touching the database. All
61
+ detector outputs are mapped onto our canonical slugs so callers can perform
62
+ their own model lookups or fall back safely.
63
+ """
70
64
 
71
65
  detector = gender_detector.Detector(case_sensitive=False)
72
- gender_name = detector.get_gender(name)
73
- gender = Gender.objects.get(name=gender_name)
74
- if not gender:
75
- gender = Gender.objects.get(name="unknown")
76
- return gender
66
+ try:
67
+ detected = detector.get_gender(name or "")
68
+ except Exception: # pragma: no cover - defensive, detector is pure-Python
69
+ detected = None
70
+
71
+ if not detected:
72
+ return "unknown"
73
+
74
+ normalized = detected.lower()
75
+ if normalized in {"male", "mostly_male"}:
76
+ return "male"
77
+ if normalized in {"female", "mostly_female"}:
78
+ return "female"
79
+
80
+ # gender-guesser returns "andy" for androgynous names and "unknown" for
81
+ # unrecognised inputs – both should map to our "unknown" slug.
82
+ return "unknown"
endoreg_db/utils/paths.py CHANGED
@@ -7,89 +7,153 @@ It provides a unified dictionary 'data_paths' for accessing all path objects.
7
7
 
8
8
  from logging import getLogger
9
9
 
10
- from sphinx.search import no
11
10
  logger = getLogger(__name__)
12
11
 
13
- from pathlib import Path
14
- from typing import Dict
15
12
  import os
13
+ from pathlib import Path
14
+ from typing import Dict, Union
15
+
16
16
  # Alternative approach using env_path helper, deprecated since monorepo setup. Alright for single install, env is always preferred.
17
- #from endoreg_db.config.env import env_path
17
+ # from endoreg_db.config.env import env_path
18
18
 
19
- STORAGE_DIR = Path(os.getenv("STORAGE_DIR", "storage"))
19
+ STORAGE_DIR = Path(os.getenv("STORAGE_DIR", "internal_storage"))
20
20
 
21
21
  storage_dir_env = os.getenv("STORAGE_DIR")
22
22
  if storage_dir_env is None:
23
23
  raise RuntimeError("STORAGE_DIR environment variable is not set.")
24
- storage_dir_str = Path(storage_dir_env)
25
- STORAGE_DIR = storage_dir_str
24
+ storage_dir = Path(storage_dir_env)
25
+ STORAGE_DIR = storage_dir
26
26
  if not STORAGE_DIR.exists():
27
27
  STORAGE_DIR.mkdir(parents=True, exist_ok=True)
28
28
 
29
+
29
30
  PREFIX_RAW = "raw_"
31
+ io_dir_env = Path(os.getenv("IO_DIR", "data"))
32
+ io_dir = Path(io_dir_env)
33
+ IO_DIR = io_dir
34
+ if not STORAGE_DIR.exists():
35
+ STORAGE_DIR.mkdir(parents=True, exist_ok=True)
36
+
37
+
38
+ # Data dropoff folders - These can be external, determined by IO_DIR (Default: set to desktop root folder of OS)
39
+
30
40
  IMPORT_DIR_NAME = "import"
31
41
  EXPORT_DIR_NAME = "export"
32
42
 
33
- VIDEO_DIR_NAME = "videos"
34
- ANONYM_VIDEO_DIR_NAME = "anonym_videos" # Added for processed videos
43
+ IMPORT_DIR = IO_DIR / IMPORT_DIR_NAME # data/import
44
+ EXPORT_DIR = IO_DIR / EXPORT_DIR_NAME # data/export
45
+
46
+
47
+ IMPORT_VIDEO_DIR_NAME = "video_import"
48
+ REPORT_IMPORT_DIR_NAME = "report_import"
49
+
50
+ VIDEO_EXPORT_DIR_NAME = "video_export"
51
+ REPORT_EXPORT_DIR_NAME = "report_export"
52
+
53
+
54
+ IMPORT_VIDEO_DIR = IMPORT_DIR / IMPORT_VIDEO_DIR_NAME
55
+ IMPORT_REPORT_DIR = IMPORT_DIR / REPORT_IMPORT_DIR_NAME
56
+
57
+
58
+ VIDEO_EXPORT_DIR = EXPORT_DIR / VIDEO_EXPORT_DIR_NAME
59
+ REPORT_EXPORT_DIR = EXPORT_DIR / REPORT_EXPORT_DIR_NAME
60
+
61
+ # Document Dir
62
+
63
+ DOCUMENT_DIR = STORAGE_DIR / "documents"
64
+
65
+ # After initial import, files will remain here.
66
+
67
+ TRANSCODING_DIR = STORAGE_DIR /"temp"
68
+
69
+ SENSITIVE_VIDEO_DIR_NAME = "sensitive_videos"
70
+ SENSITIVE_REPORT_DIR_NAME = "sensitive_reports"
71
+ ANONYM_VIDEO_DIR_NAME = "processed_videos_final"
72
+ ANONYM_REPORT_DIR_NAME = "processed_reports_final"
73
+
74
+ RAW_FRAME_DIR_NAME = f"{PREFIX_RAW}frames"
35
75
  FRAME_DIR_NAME = "frames"
36
- PDF_DIR_NAME = "pdfs" # Changed from reports
37
76
  WEIGHTS_DIR_NAME = "model_weights"
38
77
  EXAMINATION_DIR_NAME = "examinations"
39
78
 
40
- RAW_VIDEO_DIR_NAME = f"{PREFIX_RAW}videos"
41
- RAW_FRAME_DIR_NAME = f"{PREFIX_RAW}frames"
42
- RAW_PDF_DIR_NAME = f"{PREFIX_RAW}pdfs" # Changed from reports
43
-
44
79
  # Define data subdirectories under STORAGE_DIR
45
- VIDEO_DIR = STORAGE_DIR / VIDEO_DIR_NAME
46
- ANONYM_VIDEO_DIR = STORAGE_DIR / ANONYM_VIDEO_DIR_NAME # Added
80
+ ANONYM_VIDEO_DIR = STORAGE_DIR / ANONYM_VIDEO_DIR_NAME
81
+ SENSITIVE_VIDEO_DIR = STORAGE_DIR / SENSITIVE_VIDEO_DIR_NAME
82
+ ANONYM_REPORT_DIR = STORAGE_DIR / ANONYM_REPORT_DIR_NAME
83
+ SENSITIVE_REPORT_DIR = STORAGE_DIR / SENSITIVE_REPORT_DIR_NAME
84
+
47
85
  FRAME_DIR = STORAGE_DIR / FRAME_DIR_NAME
48
- PDF_DIR = STORAGE_DIR / PDF_DIR_NAME # Changed
86
+
87
+
49
88
  WEIGHTS_DIR = STORAGE_DIR / WEIGHTS_DIR_NAME
50
- RAW_VIDEO_DIR = STORAGE_DIR / RAW_VIDEO_DIR_NAME
51
89
  RAW_FRAME_DIR = STORAGE_DIR / RAW_FRAME_DIR_NAME
52
- RAW_PDF_DIR = STORAGE_DIR / RAW_PDF_DIR_NAME # Changed
53
90
 
54
- IMPORT_DIR = STORAGE_DIR / IMPORT_DIR_NAME
55
- VIDEO_IMPORT_DIR = IMPORT_DIR / VIDEO_DIR_NAME
56
- FRAME_IMPORT_DIR = IMPORT_DIR / FRAME_DIR_NAME
57
- PDF_IMPORT_DIR = IMPORT_DIR / PDF_DIR_NAME # Changed
58
91
  WEIGHTS_IMPORT_DIR = IMPORT_DIR / WEIGHTS_DIR_NAME
92
+ WEIGHTS_EXPORT_DIR = EXPORT_DIR / WEIGHTS_DIR_NAME
93
+
94
+ FRAME_IMPORT_DIR = IMPORT_DIR / FRAME_DIR_NAME
59
95
 
60
- EXPORT_DIR = STORAGE_DIR / EXPORT_DIR_NAME
96
+ FRAME_EXPORT_DIR = EXPORT_DIR / FRAME_DIR_NAME
61
97
 
62
- data_paths:Dict[str,Path] = {
98
+
99
+ data_paths: Dict[str, Path] = {
63
100
  "storage": STORAGE_DIR,
64
- "video": VIDEO_DIR,
65
- "anonym_video": ANONYM_VIDEO_DIR, # Added
66
- "frame": FRAME_DIR,
67
- "pdf": PDF_DIR, # Changed
68
101
  "import": IMPORT_DIR,
69
- "video_import": VIDEO_IMPORT_DIR,
70
- "frame_import": FRAME_IMPORT_DIR,
71
- "pdf_import": PDF_IMPORT_DIR, # Changed
72
- "raw_video": RAW_VIDEO_DIR,
102
+ "import_video": IMPORT_VIDEO_DIR,
103
+ "sensitive_video": SENSITIVE_VIDEO_DIR,
104
+ "sensitive_report": SENSITIVE_REPORT_DIR,
105
+ "anonym_video": ANONYM_VIDEO_DIR,
106
+ "anonym_report": ANONYM_REPORT_DIR,
107
+ "import_frame": FRAME_IMPORT_DIR,
108
+ "import_report": IMPORT_REPORT_DIR,
73
109
  "raw_frame": RAW_FRAME_DIR,
74
- "raw_pdf": RAW_PDF_DIR, # Changed
75
110
  "weights": WEIGHTS_DIR,
76
111
  "weights_import": WEIGHTS_IMPORT_DIR,
77
112
  "export": EXPORT_DIR,
78
- "video_export": EXPORT_DIR / VIDEO_DIR_NAME,
79
- "anonym_video_export": EXPORT_DIR / ANONYM_VIDEO_DIR_NAME, # Added
80
- "frame_export": EXPORT_DIR / FRAME_DIR_NAME,
81
- "pdf_export": EXPORT_DIR / PDF_DIR_NAME, # Changed
113
+ "report_export": REPORT_EXPORT_DIR,
114
+ "video_export": VIDEO_EXPORT_DIR,
115
+ "frame_export": FRAME_EXPORT_DIR,
82
116
  "weights_export": EXPORT_DIR / WEIGHTS_DIR_NAME,
83
- "examination_export": EXPORT_DIR / EXAMINATION_DIR_NAME,
84
- "raw_video_export": EXPORT_DIR / RAW_VIDEO_DIR_NAME,
85
- "raw_frame_export": EXPORT_DIR / RAW_FRAME_DIR_NAME,
86
- "raw_pdf_export": EXPORT_DIR / RAW_PDF_DIR_NAME, # Changed
117
+ "transcoding": TRANSCODING_DIR,
118
+ "frame": FRAME_DIR,
119
+ "documents": DOCUMENT_DIR
87
120
  }
88
121
 
89
122
  logger.info(f"Storage directory: {STORAGE_DIR.resolve()}")
90
123
  logger.info(f"Export directory: {EXPORT_DIR.resolve()}")
91
124
 
92
- # for key, path in data_paths.items():
93
- # path.mkdir(parents=True, exist_ok=True)
94
-
95
- # logger.info(f"{key.capitalize()} directory: {path.resolve()}")
125
+ for key, path in data_paths.items():
126
+ path.mkdir(parents=True, exist_ok=True)
127
+
128
+ logger.info(f"{key.capitalize()} directory: {path.resolve()}")
129
+
130
+
131
+ def to_storage_relative(path: Union[str, Path]) -> str:
132
+ """
133
+ Return a path string relative to STORAGE_DIR, suitable for Django FileField.name.
134
+
135
+ - If `path` is inside STORAGE_DIR (absolute or contains STORAGE_DIR as prefix),
136
+ we strip the STORAGE_DIR prefix and return the relative part.
137
+ - If `path` is outside STORAGE_DIR, we return it as a string unchanged
138
+ (caller can decide what to do).
139
+ """
140
+ # Normalize input to Path
141
+ p = Path(path)
142
+
143
+ # Resolve absolute path for comparison
144
+ storage_root = STORAGE_DIR.resolve()
145
+
146
+ if not p.is_absolute():
147
+ # Resolve relative path against current working directory
148
+ # (in tests, cwd should be project root, so this still lands under STORAGE_DIR)
149
+ p = p.resolve()
150
+
151
+ try:
152
+ rel = p.relative_to(storage_root)
153
+ except ValueError:
154
+ # Not under STORAGE_DIR – probably already a relative name or external.
155
+ # In that case, just return the string representation as-is.
156
+ return str(path)
157
+
158
+ # Use POSIX-style separators for Django FileField
159
+ return rel.as_posix()
@@ -7,7 +7,7 @@ the DEBUG setting and other environment configurations.
7
7
 
8
8
  from django.conf import settings
9
9
  from rest_framework.permissions import IsAuthenticated, AllowAny, BasePermission
10
- from rest_framework.decorators import permission_classes as drf_permission_classes
10
+ #from rest_framework.decorators import permission_classes as drf_permission_classes
11
11
  from functools import wraps
12
12
  import logging
13
13
  import os
@@ -50,6 +50,7 @@ def dynamic_permission_classes(force_auth=None):
50
50
  - None: Use environment-based logic (default)
51
51
  """
52
52
  def decorator(view_func):
53
+ from rest_framework.decorators import permission_classes as drf_permission_classes
53
54
  @wraps(view_func)
54
55
  def wrapper(*args, **kwargs):
55
56
  return view_func(*args, **kwargs)
@@ -154,7 +154,7 @@ At this point in the pipeline:
154
154
  `VideoFile.processed_file`
155
155
  - Video hash is calculated and saved in:
156
156
  `VideoFile.processed_video_hash`
157
- - `VideoState.anonymized = True`
157
+ - `VideoState.state.anonymization_status.mark_anonymized()`
158
158
 
159
159
  ---
160
160
 
@@ -20,7 +20,7 @@ DEFAULT_DIR = Path(__file__).resolve().parent.parent.parent.parent / "data/study
20
20
  CENTER_NAME = os.environ.get("CENTER_NAME", "university_hospital_wuerzburg")
21
21
  ENDOSCOPY_PROCESSOR_NAME = os.environ.get("ENDOSCOPY_PROCESSOR_NAME", "olympus_cv_1500")
22
22
  MODEL_NAME = "image_multilabel_classification_colonoscopy_default"
23
- MODEL_WEIGHTS_PATH = "./tests/assets/colo_segmentation_RegNetX800MF_6.ckpt"
23
+ MODEL_WEIGHTS_PATH = "./tests/assets/colo_segmentation_RegNetX800MF_6.safetensors"
24
24
  ic(DEFAULT_DIR)
25
25
 
26
26