endoreg-db 0.6.3__py3-none-any.whl → 0.8.1__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 (798) hide show
  1. endoreg_db/admin.py +26 -26
  2. endoreg_db/api_urls.py +4 -0
  3. endoreg_db/apps.py +12 -0
  4. endoreg_db/assets/dummy_model.ckpt +1 -0
  5. endoreg_db/codemods/readme.md +88 -0
  6. endoreg_db/codemods/rename_datetime_fields.py +92 -0
  7. endoreg_db/config/env.py +101 -0
  8. endoreg_db/data/__init__.py +26 -0
  9. endoreg_db/data/ai_model/data.yaml +1 -1
  10. endoreg_db/data/ai_model_label/label/polyp_classification.yaml +52 -0
  11. endoreg_db/data/ai_model_label/label-set/data.yaml +20 -1
  12. endoreg_db/data/ai_model_label/label-set/polyp_classifications.yaml +25 -0
  13. endoreg_db/data/center/data.yaml +13 -12
  14. endoreg_db/data/center_shift/ukw.yaml +9 -0
  15. endoreg_db/data/db_summary.csv +58 -0
  16. endoreg_db/data/db_summary.xlsx +0 -0
  17. endoreg_db/data/disease/misc.yaml +1 -2
  18. endoreg_db/data/disease_classification/chronic_kidney_disease.yaml +2 -2
  19. endoreg_db/data/disease_classification_choice/chronic_kidney_disease.yaml +6 -6
  20. endoreg_db/data/endoscopy_processor/data.yaml +3 -0
  21. endoreg_db/data/event/cardiology.yaml +0 -13
  22. endoreg_db/data/examination/examinations/data.yaml +34 -28
  23. endoreg_db/data/examination/type/data.yaml +12 -0
  24. endoreg_db/data/examination_indication/endoscopy.yaml +418 -2
  25. endoreg_db/data/examination_indication_classification/endoscopy.yaml +157 -5
  26. endoreg_db/data/examination_requirement_set/colonoscopy.yaml +15 -0
  27. endoreg_db/data/finding/anatomy_colon.yaml +128 -0
  28. endoreg_db/data/finding/colonoscopy.yaml +40 -0
  29. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +56 -0
  30. endoreg_db/data/finding/complication.yaml +16 -0
  31. endoreg_db/data/finding/data.yaml +8 -44
  32. endoreg_db/data/finding/examination_setting.yaml +16 -0
  33. endoreg_db/data/finding/medication_related.yaml +18 -0
  34. endoreg_db/data/finding/outcome.yaml +12 -0
  35. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +95 -0
  36. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +22 -0
  37. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +25 -0
  38. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
  39. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
  40. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +68 -0
  41. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
  42. endoreg_db/data/finding_classification/colonoscopy_location.yaml +80 -0
  43. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +21 -0
  44. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +20 -0
  45. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +26 -0
  46. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +22 -0
  47. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +53 -0
  48. endoreg_db/data/finding_classification/complication_generic.yaml +25 -0
  49. endoreg_db/data/finding_classification/examination_setting_generic.yaml +40 -0
  50. endoreg_db/data/finding_classification/histology_colo.yaml +51 -0
  51. endoreg_db/data/finding_classification/intervention_required.yaml +26 -0
  52. endoreg_db/data/finding_classification/medication_related.yaml +23 -0
  53. endoreg_db/data/finding_classification/visualized.yaml +33 -0
  54. endoreg_db/data/finding_classification_choice/bowel_preparation.yaml +78 -0
  55. endoreg_db/data/{finding_morphology_classification_choice → finding_classification_choice}/colon_lesion_circularity_default.yaml +0 -2
  56. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
  57. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
  58. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +15 -0
  59. endoreg_db/data/{finding_morphology_classification_choice → finding_classification_choice}/colon_lesion_nice.yaml +4 -7
  60. endoreg_db/data/{finding_morphology_classification_choice → finding_classification_choice}/colon_lesion_paris.yaml +0 -8
  61. endoreg_db/data/{finding_morphology_classification_choice → finding_classification_choice}/colon_lesion_planarity_default.yaml +6 -13
  62. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +14 -0
  63. endoreg_db/data/{finding_morphology_classification_choice → finding_classification_choice}/colon_lesion_surface_intact_default.yaml +3 -6
  64. endoreg_db/data/{finding_location_classification_choice/colonoscopy.yaml → finding_classification_choice/colonoscopy_location.yaml} +11 -22
  65. endoreg_db/data/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
  66. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +82 -0
  67. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
  68. endoreg_db/data/finding_classification_choice/complication_generic_types.yaml +15 -0
  69. endoreg_db/data/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
  70. endoreg_db/data/finding_classification_choice/histology.yaml +24 -0
  71. endoreg_db/data/finding_classification_choice/histology_polyp.yaml +20 -0
  72. endoreg_db/data/finding_classification_choice/outcome.yaml +19 -0
  73. endoreg_db/data/finding_classification_choice/yes_no_na.yaml +11 -0
  74. endoreg_db/data/finding_classification_type/colonoscopy_basic.yaml +48 -0
  75. endoreg_db/data/finding_intervention/endoscopy.yaml +26 -121
  76. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
  77. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +128 -0
  78. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +32 -0
  79. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  80. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  81. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +6 -6
  82. endoreg_db/data/finding_type/data.yaml +23 -10
  83. endoreg_db/data/gender/data.yaml +8 -1
  84. endoreg_db/data/information_source/annotation.yaml +6 -0
  85. endoreg_db/data/information_source/endoscopy_guidelines.yaml +7 -0
  86. endoreg_db/data/information_source/prediction.yaml +7 -0
  87. endoreg_db/data/information_source_type/data.yaml +8 -0
  88. endoreg_db/data/lab_value/misc.yaml +43 -0
  89. endoreg_db/data/medication/anticoagulation.yaml +5 -5
  90. endoreg_db/data/medication/tah.yaml +5 -5
  91. endoreg_db/data/medication_indication/anticoagulation.yaml +4 -4
  92. endoreg_db/data/medication_intake_time/base.yaml +4 -4
  93. endoreg_db/data/names_first/first_names.yaml +3 -0
  94. endoreg_db/data/pdf_type/data.yaml +27 -10
  95. endoreg_db/data/qualification/endoscopy.yaml +36 -0
  96. endoreg_db/data/qualification/m2.yaml +39 -0
  97. endoreg_db/data/qualification/outpatient_clinic.yaml +35 -0
  98. endoreg_db/data/qualification/sonography.yaml +36 -0
  99. endoreg_db/data/qualification_type/base.yaml +29 -0
  100. endoreg_db/data/report_reader_flag/rkh-histology-generic.yaml +10 -0
  101. endoreg_db/data/report_reader_flag/ukw-histology-generic.yaml +5 -0
  102. endoreg_db/data/requirement/age.yaml +26 -0
  103. endoreg_db/data/requirement/colonoscopy_baseline_austria.yaml +45 -0
  104. endoreg_db/data/requirement/disease_cardiovascular.yaml +79 -0
  105. endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
  106. endoreg_db/data/requirement/disease_hepatology.yaml +12 -0
  107. endoreg_db/data/requirement/disease_misc.yaml +12 -0
  108. endoreg_db/data/requirement/disease_renal.yaml +96 -0
  109. endoreg_db/data/requirement/endoscopy_bleeding_risk.yaml +59 -0
  110. endoreg_db/data/requirement/event_cardiology.yaml +251 -0
  111. endoreg_db/data/requirement/event_requirements.yaml +145 -0
  112. endoreg_db/data/requirement/finding_colon_polyp.yaml +50 -0
  113. endoreg_db/data/requirement/gender.yaml +25 -0
  114. endoreg_db/data/requirement/lab_value.yaml +441 -0
  115. endoreg_db/data/requirement/medication.yaml +93 -0
  116. endoreg_db/data/requirement_operator/age.yaml +13 -0
  117. endoreg_db/data/requirement_operator/lab_operators.yaml +129 -0
  118. endoreg_db/data/requirement_operator/model_operators.yaml +96 -0
  119. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +48 -0
  120. endoreg_db/data/requirement_set/colonoscopy_austria_screening.yaml +57 -0
  121. endoreg_db/data/requirement_set/endoscopy_bleeding_risk.yaml +52 -0
  122. endoreg_db/data/requirement_set_type/data.yaml +20 -0
  123. endoreg_db/data/requirement_type/requirement_types.yaml +165 -0
  124. endoreg_db/data/risk/bleeding.yaml +26 -0
  125. endoreg_db/data/risk/thrombosis.yaml +37 -0
  126. endoreg_db/data/risk_type/data.yaml +27 -0
  127. endoreg_db/data/shift/endoscopy.yaml +21 -0
  128. endoreg_db/data/shift_type/base.yaml +35 -0
  129. endoreg_db/data/tag/requirement_set_tags.yaml +11 -0
  130. endoreg_db/data/unit/concentration.yaml +23 -0
  131. endoreg_db/data/unit/time.yaml +36 -1
  132. endoreg_db/exceptions.py +19 -0
  133. endoreg_db/forms/patient_finding_intervention_form.py +4 -5
  134. endoreg_db/forms/patient_form.py +7 -6
  135. endoreg_db/forms/questionnaires/__init__.py +1 -1
  136. endoreg_db/forms/questionnaires/tto_questionnaire.py +19 -19
  137. endoreg_db/helpers/count_db.py +45 -0
  138. endoreg_db/helpers/data_loader.py +208 -0
  139. endoreg_db/helpers/default_objects.py +359 -0
  140. endoreg_db/helpers/interact.py +6 -0
  141. endoreg_db/helpers/test_video_helper.py +119 -0
  142. endoreg_db/logger_conf.py +140 -0
  143. endoreg_db/management/__init__.py +1 -0
  144. endoreg_db/management/commands/__init__.py +1 -0
  145. endoreg_db/management/commands/anonymize_video.py +0 -0
  146. endoreg_db/management/commands/check_auth.py +125 -0
  147. endoreg_db/management/commands/create_multilabel_model_meta.py +214 -0
  148. endoreg_db/management/commands/fix_missing_patient_data.py +172 -0
  149. endoreg_db/management/commands/fix_video_paths.py +165 -0
  150. endoreg_db/management/commands/import_fallback_video.py +203 -0
  151. endoreg_db/management/commands/import_report.py +298 -0
  152. endoreg_db/management/commands/import_video.py +422 -0
  153. endoreg_db/management/commands/import_video_with_classification.py +367 -0
  154. endoreg_db/management/commands/init_default_ai_model.py +112 -0
  155. endoreg_db/management/commands/load_ai_model_data.py +2 -7
  156. endoreg_db/management/commands/load_base_db_data.py +15 -1
  157. endoreg_db/management/commands/load_center_data.py +46 -21
  158. endoreg_db/management/commands/load_endoscope_data.py +2 -2
  159. endoreg_db/management/commands/load_examination_indication_data.py +49 -28
  160. endoreg_db/management/commands/load_finding_data.py +49 -92
  161. endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +0 -1
  162. endoreg_db/management/commands/load_information_source.py +13 -7
  163. endoreg_db/management/commands/load_qualification_data.py +59 -0
  164. endoreg_db/management/commands/load_requirement_data.py +180 -0
  165. endoreg_db/management/commands/load_risk_data.py +56 -0
  166. endoreg_db/management/commands/load_shift_data.py +60 -0
  167. endoreg_db/management/commands/load_tag_data.py +57 -0
  168. endoreg_db/management/commands/register_ai_model.py +1 -1
  169. endoreg_db/management/commands/start_filewatcher.py +106 -0
  170. endoreg_db/management/commands/storage_management.py +548 -0
  171. endoreg_db/management/commands/summarize_db_content.py +189 -0
  172. endoreg_db/management/commands/validate_video.py +204 -0
  173. endoreg_db/management/commands/validate_video_files.py +161 -0
  174. endoreg_db/management/commands/video_validation.py +22 -0
  175. endoreg_db/migrations/0001_initial.py +625 -813
  176. endoreg_db/migrations/0002_add_video_correction_models.py +52 -0
  177. endoreg_db/models/__init__.py +274 -291
  178. endoreg_db/models/administration/__init__.py +116 -0
  179. endoreg_db/models/{ai_model → administration/ai}/__init__.py +6 -1
  180. endoreg_db/models/administration/ai/active_model.py +35 -0
  181. endoreg_db/models/administration/ai/ai_model.py +156 -0
  182. endoreg_db/models/{ai_model → administration/ai}/model_type.py +6 -1
  183. endoreg_db/models/administration/case/__init__.py +19 -0
  184. endoreg_db/models/administration/case/case.py +114 -0
  185. endoreg_db/models/{case_template → administration/case/case_template}/case_template.py +3 -3
  186. endoreg_db/models/{case_template → administration/case/case_template}/case_template_rule.py +3 -10
  187. endoreg_db/models/{case_template → administration/case/case_template}/case_template_rule_value.py +2 -4
  188. endoreg_db/models/{case_template → administration/case/case_template}/case_template_type.py +1 -3
  189. endoreg_db/models/{center → administration/center}/__init__.py +3 -1
  190. endoreg_db/models/administration/center/center.py +61 -0
  191. endoreg_db/models/administration/center/center_product.py +64 -0
  192. endoreg_db/models/{center → administration/center}/center_resource.py +19 -3
  193. endoreg_db/models/administration/center/center_shift.py +88 -0
  194. endoreg_db/models/administration/center/center_waste.py +30 -0
  195. endoreg_db/models/administration/permissions/__init__.py +44 -0
  196. endoreg_db/models/administration/person/__init__.py +24 -0
  197. endoreg_db/models/administration/person/employee/__init__.py +3 -0
  198. endoreg_db/models/administration/person/employee/employee.py +35 -0
  199. endoreg_db/models/administration/person/employee/employee_qualification.py +39 -0
  200. endoreg_db/models/administration/person/employee/employee_type.py +42 -0
  201. endoreg_db/models/administration/person/examiner/__init__.py +4 -0
  202. endoreg_db/models/administration/person/examiner/examiner.py +54 -0
  203. endoreg_db/models/administration/person/names/__init__.py +0 -0
  204. endoreg_db/models/{persons → administration/person/names}/first_name.py +1 -1
  205. endoreg_db/models/{persons → administration/person/names}/last_name.py +2 -3
  206. endoreg_db/models/administration/person/patient/__init__.py +5 -0
  207. endoreg_db/models/administration/person/patient/patient.py +460 -0
  208. endoreg_db/models/administration/person/profession/__init__.py +24 -0
  209. endoreg_db/models/administration/person/user/__init__.py +5 -0
  210. endoreg_db/models/administration/person/user/portal_user_information.py +37 -0
  211. endoreg_db/models/administration/product/product.py +97 -0
  212. endoreg_db/models/administration/product/product_group.py +39 -0
  213. endoreg_db/models/administration/product/product_material.py +54 -0
  214. endoreg_db/models/{product → administration/product}/product_weight.py +9 -0
  215. endoreg_db/models/{product → administration/product}/reference_product.py +26 -11
  216. endoreg_db/models/administration/qualification/__init__.py +7 -0
  217. endoreg_db/models/administration/qualification/qualification.py +37 -0
  218. endoreg_db/models/administration/qualification/qualification_type.py +35 -0
  219. endoreg_db/models/administration/shift/__init__.py +9 -0
  220. endoreg_db/models/administration/shift/scheduled_days.py +69 -0
  221. endoreg_db/models/administration/shift/shift.py +51 -0
  222. endoreg_db/models/administration/shift/shift_type.py +108 -0
  223. endoreg_db/models/label/__init__.py +24 -1
  224. endoreg_db/models/label/annotation/__init__.py +12 -0
  225. endoreg_db/models/label/annotation/image_classification.py +84 -0
  226. endoreg_db/models/label/annotation/video_segmentation_annotation.py +66 -0
  227. endoreg_db/models/label/label.py +45 -74
  228. endoreg_db/models/label/label_set.py +53 -0
  229. endoreg_db/models/label/label_type.py +29 -0
  230. endoreg_db/models/label/label_video_segment/__init__.py +3 -0
  231. endoreg_db/models/label/label_video_segment/_create_from_video.py +41 -0
  232. endoreg_db/models/label/label_video_segment/label_video_segment.py +511 -0
  233. endoreg_db/models/label/video_segmentation_label.py +31 -0
  234. endoreg_db/models/{annotation → label}/video_segmentation_labelset.py +7 -0
  235. endoreg_db/models/media/__init__.py +14 -0
  236. endoreg_db/models/media/frame/__init__.py +3 -0
  237. endoreg_db/models/media/frame/frame.py +111 -0
  238. endoreg_db/models/media/pdf/__init__.py +11 -0
  239. endoreg_db/models/media/pdf/raw_pdf.py +608 -0
  240. endoreg_db/models/media/pdf/report_file.py +162 -0
  241. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +77 -0
  242. endoreg_db/models/media/video/__init__.py +4 -0
  243. endoreg_db/models/media/video/create_from_file.py +336 -0
  244. endoreg_db/models/media/video/pipe_1.py +195 -0
  245. endoreg_db/models/media/video/pipe_2.py +105 -0
  246. endoreg_db/models/media/video/refactor_plan.md +0 -0
  247. endoreg_db/models/media/video/video_file.py +680 -0
  248. endoreg_db/models/media/video/video_file_ai.py +443 -0
  249. endoreg_db/models/media/video/video_file_anonymize.py +348 -0
  250. endoreg_db/models/media/video/video_file_frames/__init__.py +47 -0
  251. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +22 -0
  252. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +23 -0
  253. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +104 -0
  254. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +174 -0
  255. endoreg_db/models/media/video/video_file_frames/_get_frame.py +28 -0
  256. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +27 -0
  257. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +20 -0
  258. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +27 -0
  259. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +34 -0
  260. endoreg_db/models/media/video/video_file_frames/_get_frames.py +27 -0
  261. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +129 -0
  262. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +129 -0
  263. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +65 -0
  264. endoreg_db/models/media/video/video_file_frames.py +0 -0
  265. endoreg_db/models/media/video/video_file_io.py +166 -0
  266. endoreg_db/models/media/video/video_file_meta/__init__.py +22 -0
  267. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +45 -0
  268. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +39 -0
  269. endoreg_db/models/media/video/video_file_meta/get_fps.py +147 -0
  270. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +143 -0
  271. endoreg_db/models/media/video/video_file_meta/text_meta.py +134 -0
  272. endoreg_db/models/media/video/video_file_meta/video_meta.py +70 -0
  273. endoreg_db/models/media/video/video_file_meta.py +11 -0
  274. endoreg_db/models/media/video/video_file_segments.py +209 -0
  275. endoreg_db/models/medical/__init__.py +146 -0
  276. endoreg_db/models/{contraindication → medical/contraindication}/__init__.py +1 -5
  277. endoreg_db/models/medical/disease.py +156 -0
  278. endoreg_db/models/medical/event.py +137 -0
  279. endoreg_db/models/{examination → medical/examination}/__init__.py +1 -1
  280. endoreg_db/models/medical/examination/examination.py +148 -0
  281. endoreg_db/models/medical/examination/examination_indication.py +278 -0
  282. endoreg_db/models/{examination → medical/examination}/examination_time.py +0 -4
  283. endoreg_db/models/{examination → medical/examination}/examination_time_type.py +1 -8
  284. endoreg_db/models/{examination → medical/examination}/examination_type.py +18 -10
  285. endoreg_db/models/medical/finding/__init__.py +18 -0
  286. endoreg_db/models/medical/finding/finding.py +96 -0
  287. endoreg_db/models/medical/finding/finding_classification.py +142 -0
  288. endoreg_db/models/{finding → medical/finding}/finding_intervention.py +2 -10
  289. endoreg_db/models/medical/finding/finding_type.py +35 -0
  290. endoreg_db/models/medical/hardware/__init__.py +8 -0
  291. endoreg_db/models/{hardware → medical/hardware}/endoscope.py +28 -23
  292. endoreg_db/models/medical/laboratory/__init__.py +5 -0
  293. endoreg_db/models/medical/laboratory/lab_value.py +419 -0
  294. endoreg_db/models/{medication → medical/medication}/medication.py +1 -3
  295. endoreg_db/models/{medication → medical/medication}/medication_indication_type.py +8 -3
  296. endoreg_db/models/{medication → medical/medication}/medication_intake_time.py +21 -3
  297. endoreg_db/models/{medication → medical/medication}/medication_schedule.py +13 -5
  298. endoreg_db/models/{organ → medical/organ}/__init__.py +3 -6
  299. endoreg_db/models/medical/patient/__init__.py +56 -0
  300. endoreg_db/models/medical/patient/medication_examples.py +38 -0
  301. endoreg_db/models/medical/patient/patient_disease.py +63 -0
  302. endoreg_db/models/medical/patient/patient_event.py +75 -0
  303. endoreg_db/models/medical/patient/patient_examination.py +249 -0
  304. endoreg_db/models/{persons → medical}/patient/patient_examination_indication.py +21 -9
  305. endoreg_db/models/medical/patient/patient_finding.py +357 -0
  306. endoreg_db/models/medical/patient/patient_finding_classification.py +207 -0
  307. endoreg_db/models/{patient → medical/patient}/patient_finding_intervention.py +15 -1
  308. endoreg_db/models/medical/patient/patient_lab_sample.py +148 -0
  309. endoreg_db/models/{persons → medical}/patient/patient_lab_value.py +40 -15
  310. endoreg_db/models/medical/patient/patient_medication.py +104 -0
  311. endoreg_db/models/medical/patient/patient_medication_schedule.py +136 -0
  312. endoreg_db/models/medical/risk/__init__.py +7 -0
  313. endoreg_db/models/medical/risk/risk.py +72 -0
  314. endoreg_db/models/medical/risk/risk_type.py +51 -0
  315. endoreg_db/models/{data_file/metadata → metadata}/__init__.py +6 -0
  316. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  317. endoreg_db/models/metadata/model_meta.py +193 -0
  318. endoreg_db/models/metadata/model_meta_logic.py +236 -0
  319. endoreg_db/models/{data_file/metadata → metadata}/pdf_meta.py +28 -13
  320. endoreg_db/models/metadata/sensitive_meta.py +288 -0
  321. endoreg_db/models/metadata/sensitive_meta_logic.py +643 -0
  322. endoreg_db/models/metadata/video_meta.py +332 -0
  323. endoreg_db/models/metadata/video_prediction_logic.py +190 -0
  324. endoreg_db/models/metadata/video_prediction_meta.py +270 -0
  325. endoreg_db/models/other/__init__.py +17 -0
  326. endoreg_db/models/other/distribution/date_value_distribution.py +0 -2
  327. endoreg_db/models/other/distribution/numeric_value_distribution.py +30 -2
  328. endoreg_db/models/{emission → other/emission}/emission_factor.py +15 -6
  329. endoreg_db/models/{persons → other}/gender.py +8 -3
  330. endoreg_db/models/other/information_source.py +159 -0
  331. endoreg_db/models/other/material.py +10 -2
  332. endoreg_db/models/other/resource.py +6 -2
  333. endoreg_db/models/other/tag.py +27 -0
  334. endoreg_db/models/other/transport_route.py +13 -2
  335. endoreg_db/models/{unit.py → other/unit.py} +16 -6
  336. endoreg_db/models/other/waste.py +10 -3
  337. endoreg_db/models/requirement/__init__.py +11 -0
  338. endoreg_db/models/requirement/requirement.py +767 -0
  339. endoreg_db/models/requirement/requirement_evaluation/__init__.py +6 -0
  340. endoreg_db/models/requirement/requirement_evaluation/get_values.py +40 -0
  341. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +9 -0
  342. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +95 -0
  343. endoreg_db/models/requirement/requirement_operator.py +176 -0
  344. endoreg_db/models/requirement/requirement_set.py +287 -0
  345. endoreg_db/models/rule/__init__.py +13 -0
  346. endoreg_db/models/{rules → rule}/rule.py +6 -3
  347. endoreg_db/models/{rules → rule}/rule_attribute_dtype.py +0 -2
  348. endoreg_db/models/{rules → rule}/rule_type.py +0 -2
  349. endoreg_db/models/{rules → rule}/ruleset.py +0 -2
  350. endoreg_db/models/state/__init__.py +12 -0
  351. endoreg_db/models/state/abstract.py +11 -0
  352. endoreg_db/models/state/audit_ledger.py +150 -0
  353. endoreg_db/models/state/label_video_segment.py +22 -0
  354. endoreg_db/models/state/raw_pdf.py +187 -0
  355. endoreg_db/models/state/sensitive_meta.py +46 -0
  356. endoreg_db/models/state/video.py +232 -0
  357. endoreg_db/models/upload_job.py +99 -0
  358. endoreg_db/models/utils.py +135 -0
  359. endoreg_db/models/video_metadata.py +66 -0
  360. endoreg_db/models/video_processing.py +153 -0
  361. endoreg_db/renames.yml +8 -0
  362. endoreg_db/root_urls.py +9 -0
  363. endoreg_db/schemas/__init__.py +0 -0
  364. endoreg_db/schemas/examination_evaluation.py +27 -0
  365. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +775 -0
  366. endoreg_db/serializers/__init__.py +147 -10
  367. endoreg_db/serializers/{raw_pdf_meta_validation.py → _old/raw_pdf_meta_validation.py} +3 -3
  368. endoreg_db/serializers/{raw_video_meta_validation.py → _old/raw_video_meta_validation.py} +18 -14
  369. endoreg_db/serializers/_old/video.py +71 -0
  370. endoreg_db/serializers/administration/__init__.py +14 -0
  371. endoreg_db/serializers/administration/ai/__init__.py +10 -0
  372. endoreg_db/serializers/administration/ai/active_model.py +10 -0
  373. endoreg_db/serializers/administration/ai/ai_model.py +18 -0
  374. endoreg_db/serializers/administration/ai/model_type.py +10 -0
  375. endoreg_db/serializers/administration/center.py +9 -0
  376. endoreg_db/serializers/administration/gender.py +9 -0
  377. endoreg_db/serializers/anonymization.py +66 -0
  378. endoreg_db/serializers/evaluation/examination_evaluation.py +1 -0
  379. endoreg_db/serializers/examination/__init__.py +10 -0
  380. endoreg_db/serializers/examination/base.py +46 -0
  381. endoreg_db/serializers/examination/dropdown.py +21 -0
  382. endoreg_db/serializers/examination_serializer.py +12 -0
  383. endoreg_db/serializers/finding/__init__.py +5 -0
  384. endoreg_db/serializers/finding/finding.py +54 -0
  385. endoreg_db/serializers/finding_classification/__init__.py +7 -0
  386. endoreg_db/serializers/finding_classification/choice.py +19 -0
  387. endoreg_db/serializers/finding_classification/classification.py +13 -0
  388. endoreg_db/serializers/label/__init__.py +7 -0
  389. endoreg_db/serializers/label/image_classification_annotation.py +62 -0
  390. endoreg_db/serializers/label/label.py +15 -0
  391. endoreg_db/serializers/label_video_segment/__init__.py +7 -0
  392. endoreg_db/serializers/label_video_segment/_lvs_create.py +149 -0
  393. endoreg_db/serializers/label_video_segment/_lvs_update.py +138 -0
  394. endoreg_db/serializers/label_video_segment/_lvs_validate.py +149 -0
  395. endoreg_db/serializers/label_video_segment/label_video_segment.py +344 -0
  396. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +99 -0
  397. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +163 -0
  398. endoreg_db/serializers/meta/__init__.py +19 -0
  399. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +115 -0
  400. endoreg_db/serializers/meta/report_meta.py +53 -0
  401. endoreg_db/serializers/meta/sensitive_meta_detail.py +162 -0
  402. endoreg_db/serializers/meta/sensitive_meta_update.py +148 -0
  403. endoreg_db/serializers/meta/sensitive_meta_verification.py +59 -0
  404. endoreg_db/serializers/meta/video_meta.py +39 -0
  405. endoreg_db/serializers/misc/__init__.py +14 -0
  406. endoreg_db/serializers/misc/file_overview.py +152 -0
  407. endoreg_db/serializers/misc/stats.py +33 -0
  408. endoreg_db/serializers/misc/translatable_field_mix_in.py +44 -0
  409. endoreg_db/serializers/misc/upload_job.py +71 -0
  410. endoreg_db/serializers/misc/vop_patient_data.py +120 -0
  411. endoreg_db/serializers/patient/__init__.py +11 -0
  412. endoreg_db/serializers/patient/patient.py +86 -0
  413. endoreg_db/serializers/patient/patient_dropdown.py +27 -0
  414. endoreg_db/serializers/patient_examination/__init__.py +7 -0
  415. endoreg_db/serializers/patient_examination/patient_examination.py +141 -0
  416. endoreg_db/serializers/patient_finding/__init__.py +15 -0
  417. endoreg_db/serializers/patient_finding/patient_finding.py +31 -0
  418. endoreg_db/serializers/patient_finding/patient_finding_classification.py +39 -0
  419. endoreg_db/serializers/patient_finding/patient_finding_detail.py +53 -0
  420. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +26 -0
  421. endoreg_db/serializers/patient_finding/patient_finding_list.py +41 -0
  422. endoreg_db/serializers/patient_finding/patient_finding_write.py +126 -0
  423. endoreg_db/serializers/pdf/__init__.py +5 -0
  424. endoreg_db/serializers/pdf/anony_text_validation.py +85 -0
  425. endoreg_db/serializers/report/__init__.py +9 -0
  426. endoreg_db/serializers/report/mixins.py +45 -0
  427. endoreg_db/serializers/report/report.py +105 -0
  428. endoreg_db/serializers/report/report_list.py +22 -0
  429. endoreg_db/serializers/report/secure_file_url.py +26 -0
  430. endoreg_db/serializers/requirements/requirement_schema.py +25 -0
  431. endoreg_db/serializers/requirements/requirement_sets.py +29 -0
  432. endoreg_db/serializers/sensitive_meta_serializer.py +282 -0
  433. endoreg_db/serializers/video/__init__.py +7 -0
  434. endoreg_db/serializers/video/segmentation.py +263 -0
  435. endoreg_db/serializers/video/video_file_brief.py +10 -0
  436. endoreg_db/serializers/video/video_file_detail.py +83 -0
  437. endoreg_db/serializers/video/video_file_list.py +67 -0
  438. endoreg_db/serializers/video/video_metadata.py +105 -0
  439. endoreg_db/serializers/video/video_processing_history.py +153 -0
  440. endoreg_db/services/__init__.py +5 -0
  441. endoreg_db/services/anonymization.py +223 -0
  442. endoreg_db/services/examination_evaluation.py +149 -0
  443. endoreg_db/services/finding_description_service.py +0 -0
  444. endoreg_db/services/lookup_service.py +241 -0
  445. endoreg_db/services/lookup_store.py +122 -0
  446. endoreg_db/services/ollama_api_docs.py +1528 -0
  447. endoreg_db/services/pdf_import.py +993 -0
  448. endoreg_db/services/polling_coordinator.py +288 -0
  449. endoreg_db/services/pseudonym_service.py +89 -0
  450. endoreg_db/services/requirements_object.py +147 -0
  451. endoreg_db/services/segment_sync.py +155 -0
  452. endoreg_db/services/storage_aware_video_processor.py +344 -0
  453. endoreg_db/services/video_import.py +915 -0
  454. endoreg_db/tasks/upload_tasks.py +207 -0
  455. endoreg_db/tasks/video_ingest.py +157 -0
  456. endoreg_db/tasks/video_processing_tasks.py +327 -0
  457. endoreg_db/urls/__init__.py +72 -0
  458. endoreg_db/urls/anonymization.py +32 -0
  459. endoreg_db/urls/auth.py +16 -0
  460. endoreg_db/urls/classification.py +39 -0
  461. endoreg_db/urls/examination.py +54 -0
  462. endoreg_db/urls/files.py +6 -0
  463. endoreg_db/urls/label_video_segment_validate.py +33 -0
  464. endoreg_db/urls/label_video_segments.py +44 -0
  465. endoreg_db/urls/media.py +32 -0
  466. endoreg_db/urls/patient.py +19 -0
  467. endoreg_db/urls/pdf.py +0 -0
  468. endoreg_db/urls/report.py +78 -0
  469. endoreg_db/urls/requirements.py +13 -0
  470. endoreg_db/urls/sensitive_meta.py +36 -0
  471. endoreg_db/urls/stats.py +46 -0
  472. endoreg_db/urls/upload.py +20 -0
  473. endoreg_db/urls/video.py +119 -0
  474. endoreg_db/urls.py +6 -269
  475. endoreg_db/utils/__init__.py +68 -16
  476. endoreg_db/utils/ai/__init__.py +9 -0
  477. endoreg_db/{models/ai_model/utils.py → utils/ai/get.py} +1 -4
  478. endoreg_db/{models/ai_model/lightning → utils/ai}/inference_dataset.py +0 -1
  479. endoreg_db/{models/ai_model/lightning → utils/ai}/multilabel_classification_net.py +14 -10
  480. endoreg_db/{models/ai_model/lightning → utils/ai}/postprocess.py +15 -5
  481. endoreg_db/utils/ai/predict.py +291 -0
  482. endoreg_db/{models/ai_model/lightning → utils/ai}/preprocess.py +1 -1
  483. endoreg_db/utils/calc_duration_seconds.py +24 -0
  484. endoreg_db/utils/case_generator/__init__.py +0 -0
  485. endoreg_db/utils/check_video_files.py +148 -0
  486. endoreg_db/utils/dataloader.py +88 -31
  487. endoreg_db/utils/dates.py +21 -0
  488. endoreg_db/utils/env.py +33 -0
  489. endoreg_db/utils/extract_specific_frames.py +72 -0
  490. endoreg_db/utils/file_operations.py +29 -1
  491. endoreg_db/utils/fix_video_path_direct.py +141 -0
  492. endoreg_db/utils/frame_anonymization_utils.py +463 -0
  493. endoreg_db/utils/hashs.py +1 -0
  494. endoreg_db/utils/links/__init__.py +0 -0
  495. endoreg_db/utils/links/requirement_link.py +193 -0
  496. endoreg_db/utils/mime_types.py +0 -0
  497. endoreg_db/utils/names.py +2 -0
  498. endoreg_db/utils/paths.py +104 -0
  499. endoreg_db/utils/permissions.py +143 -0
  500. endoreg_db/utils/pipelines/Readme.md +235 -0
  501. endoreg_db/utils/pipelines/__init__.py +0 -0
  502. endoreg_db/utils/pipelines/process_video_dir.py +120 -0
  503. endoreg_db/utils/product/__init__.py +0 -0
  504. endoreg_db/utils/product/sum_emissions.py +20 -0
  505. endoreg_db/utils/product/sum_weights.py +18 -0
  506. endoreg_db/utils/pydantic_models/db_config.py +1 -1
  507. endoreg_db/utils/requirement_helpers.py +0 -0
  508. endoreg_db/utils/requirement_operator_logic/__init__.py +0 -0
  509. endoreg_db/utils/requirement_operator_logic/lab_value_operators.py +578 -0
  510. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +368 -0
  511. endoreg_db/utils/translation.py +27 -0
  512. endoreg_db/utils/validate_video_detailed.py +357 -0
  513. endoreg_db/utils/video/__init__.py +19 -6
  514. endoreg_db/utils/video/extract_frames.py +37 -70
  515. endoreg_db/utils/video/ffmpeg_wrapper.py +772 -0
  516. endoreg_db/utils/video/names.py +42 -0
  517. endoreg_db/utils/video/streaming_processor.py +312 -0
  518. endoreg_db/utils/video/video_splitter.py +94 -0
  519. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +238 -0
  520. endoreg_db/views/__init__.py +282 -2
  521. endoreg_db/views/anonymization/__init__.py +27 -0
  522. endoreg_db/views/anonymization/media_management.py +454 -0
  523. endoreg_db/views/anonymization/overview.py +216 -0
  524. endoreg_db/views/anonymization/validate.py +63 -0
  525. endoreg_db/views/auth/__init__.py +13 -0
  526. endoreg_db/views/auth/keycloak.py +113 -0
  527. endoreg_db/views/examination/__init__.py +33 -0
  528. endoreg_db/views/examination/examination.py +37 -0
  529. endoreg_db/views/examination/examination_manifest_cache.py +26 -0
  530. endoreg_db/views/examination/get_finding_classification_choices.py +59 -0
  531. endoreg_db/views/examination/get_finding_classifications.py +36 -0
  532. endoreg_db/views/examination/get_findings.py +41 -0
  533. endoreg_db/views/examination/get_instruments.py +18 -0
  534. endoreg_db/views/examination/get_interventions.py +14 -0
  535. endoreg_db/views/finding/__init__.py +9 -0
  536. endoreg_db/views/finding/finding.py +112 -0
  537. endoreg_db/views/finding/get_classifications.py +14 -0
  538. endoreg_db/views/finding/get_interventions.py +17 -0
  539. endoreg_db/views/finding_classification/__init__.py +13 -0
  540. endoreg_db/views/finding_classification/base.py +0 -0
  541. endoreg_db/views/finding_classification/finding_classification.py +42 -0
  542. endoreg_db/views/finding_classification/get_classification_choices.py +55 -0
  543. endoreg_db/views/label/__init__.py +5 -0
  544. endoreg_db/views/label/label.py +15 -0
  545. endoreg_db/views/label_video_segment/__init__.py +16 -0
  546. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +44 -0
  547. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +50 -0
  548. endoreg_db/views/label_video_segment/label_video_segment.py +77 -0
  549. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +174 -0
  550. endoreg_db/views/label_video_segment/label_video_segment_detail.py +73 -0
  551. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +46 -0
  552. endoreg_db/views/label_video_segment/validate.py +226 -0
  553. endoreg_db/views/media/__init__.py +9 -0
  554. endoreg_db/views/media/pdf_media.py +386 -0
  555. endoreg_db/views/media/video_media.py +272 -0
  556. endoreg_db/views/meta/__init__.py +15 -0
  557. endoreg_db/views/meta/available_files_list.py +146 -0
  558. endoreg_db/views/meta/report_meta.py +53 -0
  559. endoreg_db/views/meta/sensitive_meta_detail.py +148 -0
  560. endoreg_db/views/meta/sensitive_meta_list.py +104 -0
  561. endoreg_db/views/meta/sensitive_meta_verification.py +71 -0
  562. endoreg_db/views/misc/__init__.py +63 -0
  563. endoreg_db/views/misc/center.py +13 -0
  564. endoreg_db/views/misc/gender.py +14 -0
  565. endoreg_db/views/misc/secure_file_serving_view.py +80 -0
  566. endoreg_db/views/misc/secure_file_url_view.py +84 -0
  567. endoreg_db/views/misc/secure_url_validate.py +79 -0
  568. endoreg_db/views/misc/stats.py +220 -0
  569. endoreg_db/views/misc/translation.py +182 -0
  570. endoreg_db/views/misc/upload_views.py +240 -0
  571. endoreg_db/views/patient/__init__.py +5 -0
  572. endoreg_db/views/patient/patient.py +210 -0
  573. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +164 -0
  574. endoreg_db/views/patient_examination/__init__.py +11 -0
  575. endoreg_db/views/patient_examination/patient_examination.py +140 -0
  576. endoreg_db/views/patient_examination/patient_examination_create.py +63 -0
  577. endoreg_db/views/patient_examination/patient_examination_detail.py +66 -0
  578. endoreg_db/views/patient_examination/patient_examination_list.py +68 -0
  579. endoreg_db/views/patient_examination/video.py +194 -0
  580. endoreg_db/views/patient_finding/__init__.py +7 -0
  581. endoreg_db/views/patient_finding/base.py +0 -0
  582. endoreg_db/views/patient_finding/patient_finding.py +64 -0
  583. endoreg_db/views/patient_finding/patient_finding_optimized.py +259 -0
  584. endoreg_db/views/patient_finding_classification/__init__.py +5 -0
  585. endoreg_db/views/patient_finding_classification/pfc_create.py +67 -0
  586. endoreg_db/views/patient_finding_location/__init__.py +5 -0
  587. endoreg_db/views/patient_finding_location/pfl_create.py +70 -0
  588. endoreg_db/views/patient_finding_morphology/__init__.py +5 -0
  589. endoreg_db/views/patient_finding_morphology/pfm_create.py +70 -0
  590. endoreg_db/views/pdf/__init__.py +11 -0
  591. endoreg_db/views/pdf/pdf_media.py +239 -0
  592. endoreg_db/views/pdf/pdf_stream_views.py +127 -0
  593. endoreg_db/views/pdf/reimport.py +151 -0
  594. endoreg_db/views/report/__init__.py +9 -0
  595. endoreg_db/views/report/report_list.py +112 -0
  596. endoreg_db/views/report/report_with_secure_url.py +28 -0
  597. endoreg_db/views/report/start_examination.py +7 -0
  598. endoreg_db/views/requirement/__init__.py +10 -0
  599. endoreg_db/views/requirement/evaluate.py +279 -0
  600. endoreg_db/views/requirement/lookup.py +483 -0
  601. endoreg_db/views/requirement/lookup_store.py +252 -0
  602. endoreg_db/views/requirement_lookup/lookup.py +0 -0
  603. endoreg_db/views/requirement_lookup/lookup_store.py +0 -0
  604. endoreg_db/views/stats/__init__.py +13 -0
  605. endoreg_db/views/stats/stats_views.py +229 -0
  606. endoreg_db/views/video/__init__.py +72 -0
  607. endoreg_db/views/video/correction.py +672 -0
  608. endoreg_db/views/video/media/__init__.py +23 -0
  609. endoreg_db/views/video/media/task_status.py +49 -0
  610. endoreg_db/views/video/media/video_analyze.py +52 -0
  611. endoreg_db/views/video/media/video_apply_mask.py +48 -0
  612. endoreg_db/views/video/media/video_correction.py +21 -0
  613. endoreg_db/views/video/media/video_download_processed.py +58 -0
  614. endoreg_db/views/video/media/video_media.py +158 -0
  615. endoreg_db/views/video/media/video_meta.py +29 -0
  616. endoreg_db/views/video/media/video_processing_history.py +24 -0
  617. endoreg_db/views/video/media/video_remove_frames.py +48 -0
  618. endoreg_db/views/video/media/video_reprocess.py +40 -0
  619. endoreg_db/views/video/reimport.py +192 -0
  620. endoreg_db/views/video/segmentation.py +274 -0
  621. endoreg_db/views/{views_for_timeline.py → video/timeline.py} +3 -3
  622. endoreg_db/views/video/video_examination_viewset.py +329 -0
  623. endoreg_db/views/video/video_stream.py +188 -0
  624. endoreg_db-0.8.1.dist-info/METADATA +384 -0
  625. endoreg_db-0.8.1.dist-info/RECORD +789 -0
  626. endoreg_db/data/agl_service/data.yaml +0 -19
  627. endoreg_db/data/finding_location_classification/colonoscopy.yaml +0 -46
  628. endoreg_db/data/finding_morphology_classification/colonoscopy.yaml +0 -48
  629. endoreg_db/data/finding_morphology_classification_choice/colonoscopy_size.yaml +0 -57
  630. endoreg_db/management/commands/_load_model_template.py +0 -41
  631. endoreg_db/management/commands/delete_all.py +0 -18
  632. endoreg_db/management/commands/fetch_legacy_image_dataset.py +0 -32
  633. endoreg_db/management/commands/fix_auth_permission.py +0 -20
  634. endoreg_db/management/commands/load_active_model_data.py +0 -45
  635. endoreg_db/management/commands/load_g_play_data.py +0 -113
  636. endoreg_db/management/commands/load_logging_data.py +0 -39
  637. endoreg_db/management/commands/load_lx_data.py +0 -64
  638. endoreg_db/management/commands/load_medication_indication_data.py +0 -63
  639. endoreg_db/management/commands/load_medication_indication_type_data.py +0 -41
  640. endoreg_db/management/commands/load_medication_intake_time_data.py +0 -41
  641. endoreg_db/management/commands/load_medication_schedule_data.py +0 -55
  642. endoreg_db/management/commands/load_network_data.py +0 -57
  643. endoreg_db/migrations/0002_alter_frame_image_alter_rawframe_image.py +0 -23
  644. endoreg_db/migrations/0003_alter_frame_image_alter_rawframe_image.py +0 -23
  645. endoreg_db/migrations/0004_alter_rawvideofile_file_alter_video_file.py +0 -25
  646. endoreg_db/migrations/0005_rawvideofile_frame_count_and_more.py +0 -33
  647. endoreg_db/migrations/0006_frame_extracted_rawframe_extracted.py +0 -23
  648. endoreg_db/migrations/0007_rename_pseudo_patient_video_patient_and_more.py +0 -24
  649. endoreg_db/migrations/0008_remove_reportfile_patient_examination_and_more.py +0 -48
  650. endoreg_db/models/ai_model/active_model.py +0 -9
  651. endoreg_db/models/ai_model/ai_model.py +0 -103
  652. endoreg_db/models/ai_model/lightning/__init__.py +0 -3
  653. endoreg_db/models/ai_model/lightning/predict.py +0 -172
  654. endoreg_db/models/ai_model/lightning/prediction_visualizer.py +0 -55
  655. endoreg_db/models/ai_model/lightning/run_visualizer.py +0 -21
  656. endoreg_db/models/ai_model/model_meta.py +0 -250
  657. endoreg_db/models/annotation/__init__.py +0 -32
  658. endoreg_db/models/annotation/anonymized_image_annotation.py +0 -115
  659. endoreg_db/models/annotation/binary_classification_annotation_task.py +0 -117
  660. endoreg_db/models/annotation/image_classification.py +0 -86
  661. endoreg_db/models/annotation/video_segmentation_annotation.py +0 -52
  662. endoreg_db/models/case/__init__.py +0 -1
  663. endoreg_db/models/case/case.py +0 -34
  664. endoreg_db/models/center/center.py +0 -51
  665. endoreg_db/models/center/center_product.py +0 -33
  666. endoreg_db/models/center/center_waste.py +0 -16
  667. endoreg_db/models/data_file/__init__.py +0 -39
  668. endoreg_db/models/data_file/base_classes/__init__.py +0 -7
  669. endoreg_db/models/data_file/base_classes/abstract_frame.py +0 -100
  670. endoreg_db/models/data_file/base_classes/abstract_pdf.py +0 -136
  671. endoreg_db/models/data_file/base_classes/abstract_video.py +0 -807
  672. endoreg_db/models/data_file/base_classes/frame_helpers.py +0 -17
  673. endoreg_db/models/data_file/base_classes/prepare_bulk_frames.py +0 -19
  674. endoreg_db/models/data_file/base_classes/utils.py +0 -80
  675. endoreg_db/models/data_file/frame.py +0 -29
  676. endoreg_db/models/data_file/import_classes/__init__.py +0 -18
  677. endoreg_db/models/data_file/import_classes/processing_functions/__init__.py +0 -35
  678. endoreg_db/models/data_file/import_classes/processing_functions/pdf.py +0 -28
  679. endoreg_db/models/data_file/import_classes/processing_functions/video.py +0 -260
  680. endoreg_db/models/data_file/import_classes/raw_pdf.py +0 -260
  681. endoreg_db/models/data_file/import_classes/raw_video.py +0 -288
  682. endoreg_db/models/data_file/metadata/sensitive_meta.py +0 -290
  683. endoreg_db/models/data_file/metadata/video_meta.py +0 -199
  684. endoreg_db/models/data_file/report_file.py +0 -56
  685. endoreg_db/models/data_file/video/__init__.py +0 -11
  686. endoreg_db/models/data_file/video/import_meta.py +0 -25
  687. endoreg_db/models/data_file/video/video.py +0 -196
  688. endoreg_db/models/data_file/video_segment.py +0 -214
  689. endoreg_db/models/disease.py +0 -79
  690. endoreg_db/models/event.py +0 -73
  691. endoreg_db/models/examination/examination.py +0 -67
  692. endoreg_db/models/examination/examination_indication.py +0 -170
  693. endoreg_db/models/finding/__init__.py +0 -11
  694. endoreg_db/models/finding/finding.py +0 -75
  695. endoreg_db/models/finding/finding_location_classification.py +0 -94
  696. endoreg_db/models/finding/finding_morphology_classification.py +0 -89
  697. endoreg_db/models/finding/finding_type.py +0 -22
  698. endoreg_db/models/hardware/__init__.py +0 -2
  699. endoreg_db/models/information_source.py +0 -29
  700. endoreg_db/models/laboratory/__init__.py +0 -1
  701. endoreg_db/models/laboratory/lab_value.py +0 -111
  702. endoreg_db/models/logging/__init__.py +0 -11
  703. endoreg_db/models/logging/agl_service.py +0 -19
  704. endoreg_db/models/logging/base.py +0 -22
  705. endoreg_db/models/logging/log_type.py +0 -23
  706. endoreg_db/models/logging/network_device.py +0 -27
  707. endoreg_db/models/lx/__init__.py +0 -4
  708. endoreg_db/models/lx/client.py +0 -57
  709. endoreg_db/models/lx/identity.py +0 -34
  710. endoreg_db/models/lx/permission.py +0 -18
  711. endoreg_db/models/lx/user.py +0 -16
  712. endoreg_db/models/network/__init__.py +0 -9
  713. endoreg_db/models/network/agl_service.py +0 -38
  714. endoreg_db/models/network/network_device.py +0 -58
  715. endoreg_db/models/network/network_device_type.py +0 -23
  716. endoreg_db/models/other/distribution.py +0 -5
  717. endoreg_db/models/patient/__init__.py +0 -24
  718. endoreg_db/models/patient/patient_examination.py +0 -182
  719. endoreg_db/models/patient/patient_finding.py +0 -143
  720. endoreg_db/models/patient/patient_finding_location.py +0 -120
  721. endoreg_db/models/patient/patient_finding_morphology.py +0 -166
  722. endoreg_db/models/permissions/__init__.py +0 -44
  723. endoreg_db/models/persons/__init__.py +0 -34
  724. endoreg_db/models/persons/examiner/__init__.py +0 -2
  725. endoreg_db/models/persons/examiner/examiner.py +0 -60
  726. endoreg_db/models/persons/examiner/examiner_type.py +0 -2
  727. endoreg_db/models/persons/patient/__init__.py +0 -8
  728. endoreg_db/models/persons/patient/patient.py +0 -389
  729. endoreg_db/models/persons/patient/patient_disease.py +0 -22
  730. endoreg_db/models/persons/patient/patient_event.py +0 -52
  731. endoreg_db/models/persons/patient/patient_lab_sample.py +0 -108
  732. endoreg_db/models/persons/patient/patient_medication.py +0 -59
  733. endoreg_db/models/persons/patient/patient_medication_schedule.py +0 -88
  734. endoreg_db/models/persons/portal_user_information.py +0 -27
  735. endoreg_db/models/prediction/__init__.py +0 -8
  736. endoreg_db/models/prediction/image_classification.py +0 -51
  737. endoreg_db/models/prediction/video_prediction_meta.py +0 -306
  738. endoreg_db/models/product/product.py +0 -110
  739. endoreg_db/models/product/product_group.py +0 -27
  740. endoreg_db/models/product/product_material.py +0 -28
  741. endoreg_db/models/questionnaires/__init__.py +0 -114
  742. endoreg_db/models/quiz/__init__.py +0 -9
  743. endoreg_db/models/quiz/quiz_answer.py +0 -41
  744. endoreg_db/models/quiz/quiz_question.py +0 -54
  745. endoreg_db/models/report_reader/report_reader_config.py +0 -53
  746. endoreg_db/models/rules/__init__.py +0 -5
  747. endoreg_db/queries/get/__init__.py +0 -6
  748. endoreg_db/queries/get/center.py +0 -42
  749. endoreg_db/queries/get/model.py +0 -13
  750. endoreg_db/queries/get/patient.py +0 -14
  751. endoreg_db/queries/get/patient_examination.py +0 -20
  752. endoreg_db/queries/get/report_file.py +0 -33
  753. endoreg_db/queries/get/video.py +0 -31
  754. endoreg_db/serializers/ai_model.py +0 -19
  755. endoreg_db/serializers/annotation.py +0 -14
  756. endoreg_db/serializers/center.py +0 -11
  757. endoreg_db/serializers/examination.py +0 -33
  758. endoreg_db/serializers/frame.py +0 -9
  759. endoreg_db/serializers/hardware.py +0 -21
  760. endoreg_db/serializers/label.py +0 -22
  761. endoreg_db/serializers/patient.py +0 -33
  762. endoreg_db/serializers/prediction.py +0 -10
  763. endoreg_db/serializers/raw_pdf_anony_text_validation.py +0 -137
  764. endoreg_db/serializers/report_file.py +0 -7
  765. endoreg_db/serializers/video.py +0 -20
  766. endoreg_db/serializers/video_segmentation.py +0 -587
  767. endoreg_db/tests.py +0 -3
  768. endoreg_db/utils/legacy_ocr.py +0 -201
  769. endoreg_db/utils/video/transcode_videofile.py +0 -111
  770. endoreg_db/views/patient_views.py +0 -90
  771. endoreg_db/views/raw_pdf_anony_text_validation_views.py +0 -95
  772. endoreg_db/views/raw_pdf_meta_validation_views.py +0 -111
  773. endoreg_db/views/raw_video_meta_validation_views.py +0 -148
  774. endoreg_db/views/report_views.py +0 -96
  775. endoreg_db/views/video_segmentation_views.py +0 -166
  776. endoreg_db-0.6.3.dist-info/METADATA +0 -161
  777. endoreg_db-0.6.3.dist-info/RECORD +0 -435
  778. /endoreg_db/{case_generator/__init__.py → api/serializers/finding_descriptions.py} +0 -0
  779. /endoreg_db/{queries/get/annotation.py → api/views/finding_descriptions.py} +0 -0
  780. /endoreg_db/{queries/get/prediction.py → data/shift/m2.yaml} +0 -0
  781. /endoreg_db/{queries/get/video_import_meta.py → factories/__init__.py} +0 -0
  782. /endoreg_db/{queries/get/video_prediction_meta.py → helpers/__init__.py} +0 -0
  783. /endoreg_db/models/{case_template → administration/case/case_template}/__init__.py +0 -0
  784. /endoreg_db/models/{persons → administration/person}/person.py +0 -0
  785. /endoreg_db/models/{product → administration/product}/__init__.py +0 -0
  786. /endoreg_db/models/{report_reader → media/pdf/report_reader}/__init__.py +0 -0
  787. /endoreg_db/models/{report_reader → media/pdf/report_reader}/report_reader_flag.py +0 -0
  788. /endoreg_db/models/{hardware → medical/hardware}/endoscopy_processor.py +0 -0
  789. /endoreg_db/models/{medication → medical/medication}/__init__.py +0 -0
  790. /endoreg_db/models/{medication → medical/medication}/medication_indication.py +0 -0
  791. /endoreg_db/models/{emission → other/emission}/__init__.py +0 -0
  792. /endoreg_db/models/{rules → rule}/rule_applicator.py +0 -0
  793. /endoreg_db/{case_generator → utils/case_generator}/case_generator.py +0 -0
  794. /endoreg_db/{case_generator → utils/case_generator}/lab_sample_factory.py +0 -0
  795. /endoreg_db/{case_generator → utils/case_generator}/utils.py +0 -0
  796. /endoreg_db/views/{csrf.py → misc/csrf.py} +0 -0
  797. {endoreg_db-0.6.3.dist-info → endoreg_db-0.8.1.dist-info}/WHEEL +0 -0
  798. {endoreg_db-0.6.3.dist-info → endoreg_db-0.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,915 @@
1
+ """
2
+ Video import service module.
3
+
4
+ Provides high-level functions for importing and anonymizing video files,
5
+ combining VideoFile creation with frame-level anonymization.
6
+ """
7
+ from datetime import date
8
+ import logging
9
+ import sys
10
+ import os
11
+ import shutil
12
+ from pathlib import Path
13
+ from typing import TYPE_CHECKING, Union, Dict, Any, Optional
14
+ from django.db import transaction
15
+ from django.core.exceptions import FieldError
16
+ from endoreg_db.models import VideoFile, SensitiveMeta
17
+ from endoreg_db.utils.paths import STORAGE_DIR, RAW_FRAME_DIR, VIDEO_DIR, ANONYM_VIDEO_DIR
18
+ import random
19
+ from lx_anonymizer.ocr import trocr_full_image_ocr
20
+
21
+
22
+ class VideoImportService():
23
+ """
24
+ Service for importing and anonymizing video files.
25
+ Uses a central video instance pattern for cleaner state management.
26
+ """
27
+
28
+ def __init__(self, project_root: Path = None):
29
+
30
+ # Set up project root path
31
+ if project_root:
32
+ self.project_root = Path(project_root)
33
+ else:
34
+ self.project_root = Path(__file__).parent.parent.parent.parent
35
+
36
+ # Track processed files to prevent duplicates
37
+ self.processed_files = set(str(file) for file in os.listdir(ANONYM_VIDEO_DIR))
38
+
39
+ self.STORAGE_DIR = STORAGE_DIR
40
+
41
+ # Central video instance and processing context
42
+ self.current_video = None
43
+ self.processing_context: Dict[str, Any] = {}
44
+
45
+ if TYPE_CHECKING:
46
+ from endoreg_db.models import VideoFile
47
+
48
+ self.logger = logging.getLogger(__name__)
49
+
50
+ def processed(self) -> bool:
51
+ """Indicates if the current file has already been processed."""
52
+ return getattr(self, '_processed', False)
53
+
54
+ def import_and_anonymize(
55
+ self,
56
+ file_path: Union[Path, str],
57
+ center_name: str,
58
+ processor_name: str,
59
+ save_video: bool = True,
60
+ delete_source: bool = True,
61
+ ) -> "VideoFile":
62
+ """
63
+ High-level helper that orchestrates the complete video import and anonymization process.
64
+ Uses the central video instance pattern for improved state management.
65
+
66
+ Args:
67
+ file_path: Path to the video file to import
68
+ center_name: Name of the center to associate with video
69
+ processor_name: Name of the processor to associate with video
70
+ save_video: Whether to save the video file
71
+ delete_source: Whether to delete the source file after import
72
+
73
+ Returns:
74
+ VideoFile instance after import and anonymization
75
+
76
+ Raises:
77
+ Exception: On any failure during import or anonymization
78
+ """
79
+ try:
80
+ # Initialize processing context
81
+ self._initialize_processing_context(file_path, center_name, processor_name,
82
+ save_video, delete_source)
83
+
84
+ # Validate and prepare file
85
+ self._validate_and_prepare_file()
86
+
87
+ # Create or retrieve video instance
88
+ self._create_or_retrieve_video_instance()
89
+
90
+ # Setup processing environment
91
+ self._setup_processing_environment()
92
+
93
+ # Process frames and metadata
94
+ self._process_frames_and_metadata()
95
+
96
+ # Finalize processing
97
+ self._finalize_processing()
98
+
99
+ # Move files and cleanup
100
+ self._cleanup_and_archive()
101
+
102
+ return self.current_video
103
+
104
+ except Exception as e:
105
+ self.logger.error(f"Video import and anonymization failed for {file_path}: {e}")
106
+ self._cleanup_on_error()
107
+ raise
108
+ finally:
109
+ self._cleanup_processing_context()
110
+
111
+ def _initialize_processing_context(self, file_path: Union[Path, str], center_name: str,
112
+ processor_name: str, save_video: bool, delete_source: bool):
113
+ """Initialize the processing context for the current video import."""
114
+ self.processing_context = {
115
+ 'file_path': Path(file_path),
116
+ 'center_name': center_name,
117
+ 'processor_name': processor_name,
118
+ 'save_video': save_video,
119
+ 'delete_source': delete_source,
120
+ 'processing_started': False,
121
+ 'frames_extracted': False,
122
+ 'anonymization_completed': False,
123
+ 'error_reason': None
124
+ }
125
+
126
+ self.logger.info(f"Initialized processing context for: {file_path}")
127
+
128
+ def _validate_and_prepare_file(self):
129
+ """Validate the video file and prepare for processing."""
130
+ file_path = self.processing_context['file_path']
131
+
132
+ # Check if already processed
133
+ if str(file_path) in self.processed_files:
134
+ self.logger.info(f"File {file_path} already processed, skipping")
135
+ self.processed = True
136
+ raise ValueError(f"File already processed: {file_path}")
137
+
138
+ # Check file exists
139
+ if not file_path.exists():
140
+ raise FileNotFoundError(f"Video file not found: {file_path}")
141
+
142
+ self.logger.info(f"File validation completed for: {file_path}")
143
+
144
+ def _create_or_retrieve_video_instance(self):
145
+ """Create or retrieve the VideoFile instance and move to final storage."""
146
+ from endoreg_db.models import VideoFile
147
+
148
+ self.logger.info("Creating VideoFile instance...")
149
+
150
+ self.current_video = VideoFile.create_from_file_initialized(
151
+ file_path=self.processing_context['file_path'],
152
+ center_name=self.processing_context['center_name'],
153
+ processor_name=self.processing_context['processor_name'],
154
+ delete_source=self.processing_context['delete_source'],
155
+ save_video_file=self.processing_context['save_video'],
156
+ )
157
+
158
+ if not self.current_video:
159
+ raise RuntimeError("Failed to create VideoFile instance")
160
+
161
+ # Immediately move to final storage locations
162
+ self._move_to_final_storage()
163
+
164
+ self.logger.info(f"Created VideoFile with UUID: {self.current_video.uuid}")
165
+
166
+ # Get and mark processing state
167
+ state = VideoFile.get_or_create_state(self.current_video)
168
+ if not state:
169
+ raise RuntimeError("Failed to create VideoFile state")
170
+
171
+ state.mark_processing_started(save=True)
172
+ self.processing_context['processing_started'] = True
173
+
174
+ def _move_to_final_storage(self):
175
+ """
176
+ Move video from raw_videos to final storage locations.
177
+ - Raw video → /data/videos (raw_file_path)
178
+ - Processed video will later → /data/anonym_videos (file_path)
179
+ """
180
+ from endoreg_db.utils import data_paths
181
+
182
+ source_path = self.processing_context['file_path']
183
+
184
+ # Define target directories
185
+ videos_dir = data_paths["video"] # /data/videos for raw files
186
+ videos_dir.mkdir(parents=True, exist_ok=True)
187
+
188
+ # Create target path for raw video in /data/videos
189
+ video_filename = f"{self.current_video.uuid}_{Path(source_path).name}"
190
+ raw_target_path = videos_dir / video_filename
191
+
192
+ # Move source file to raw video storage
193
+ try:
194
+ shutil.move(str(source_path), str(raw_target_path))
195
+ self.logger.info(f"Moved raw video to: {raw_target_path}")
196
+ except Exception as e:
197
+ self.logger.error(f"Failed to move video to final storage: {e}")
198
+ raise
199
+
200
+ # Update the raw_file path in database (relative to storage root)
201
+ try:
202
+ storage_root = data_paths["storage"]
203
+ relative_path = raw_target_path.relative_to(storage_root)
204
+ self.current_video.raw_file.name = str(relative_path)
205
+ self.current_video.save(update_fields=['raw_file'])
206
+ self.logger.info(f"Updated raw_file path to: {relative_path}")
207
+ except Exception as e:
208
+ self.logger.error(f"Failed to update raw_file path: {e}")
209
+ # Fallback to simple relative path
210
+ self.current_video.raw_file.name = f"videos/{video_filename}"
211
+ self.current_video.save(update_fields=['raw_file'])
212
+ self.logger.info(f"Updated raw_file path using fallback: videos/{video_filename}")
213
+
214
+
215
+ # Store paths for later processing
216
+ self.processing_context['raw_video_path'] = raw_target_path
217
+ self.processing_context['video_filename'] = video_filename
218
+
219
+ def _setup_processing_environment(self):
220
+ """Setup the processing environment without file movement."""
221
+ # Ensure we have a valid video instance
222
+ if not self.current_video:
223
+ raise RuntimeError("No video instance available for processing environment setup")
224
+
225
+ # Initialize video specifications
226
+ self.current_video.initialize_video_specs()
227
+
228
+ # Initialize frame objects in database
229
+ self.current_video.initialize_frames()
230
+
231
+ # Extract frames BEFORE processing to prevent pipeline 1 conflicts
232
+ self.logger.info("Pre-extracting frames to avoid pipeline conflicts...")
233
+ try:
234
+ frames_extracted = self.current_video.extract_frames(overwrite=False)
235
+ if frames_extracted:
236
+ self.processing_context['frames_extracted'] = True
237
+ self.logger.info("Frame extraction completed successfully")
238
+
239
+ # CRITICAL: Immediately save the frames_extracted state to database
240
+ # to prevent refresh_from_db() in pipeline 1 from overriding it
241
+ state = self.current_video.get_or_create_state()
242
+ if not state.frames_extracted:
243
+ state.frames_extracted = True
244
+ state.save(update_fields=['frames_extracted'])
245
+ self.logger.info("Persisted frames_extracted=True to database")
246
+ else:
247
+ self.logger.warning("Frame extraction failed, but continuing...")
248
+ self.processing_context['frames_extracted'] = False
249
+ except Exception as e:
250
+ self.logger.warning(f"Frame extraction failed during setup: {e}, but continuing...")
251
+ self.processing_context['frames_extracted'] = False
252
+
253
+ # Ensure default patient data
254
+ self._ensure_default_patient_data()
255
+
256
+ self.logger.info("Processing environment setup completed")
257
+
258
+ def _process_frames_and_metadata(self):
259
+ """Process frames and extract metadata with anonymization."""
260
+ # Check frame cleaning availability
261
+ frame_cleaning_available, FrameCleaner, ReportReader = self._ensure_frame_cleaning_available()
262
+
263
+ if not (frame_cleaning_available and self.current_video.raw_file):
264
+ self.logger.warning("Frame cleaning not available or conditions not met, using fallback anonymization.")
265
+ self._fallback_anonymize_video()
266
+ return
267
+
268
+ try:
269
+ self.logger.info("Starting frame-level anonymization with processor ROI masking...")
270
+
271
+ # Get processor ROI information
272
+ processor_roi, endoscope_roi = self._get_processor_roi_info()
273
+
274
+ # Perform frame cleaning
275
+ self._perform_frame_cleaning(FrameCleaner, processor_roi, endoscope_roi)
276
+
277
+ self.processing_context['anonymization_completed'] = True
278
+
279
+ except Exception as e:
280
+ self.logger.warning(f"Frame cleaning failed, continuing with original video: {e}")
281
+ self.processing_context['anonymization_completed'] = False
282
+ self.processing_context['error_reason'] = f"Frame cleaning failed: {e}"
283
+
284
+
285
+ def _fallback_anonymize_video(self):
286
+ """Fallback to create anonymized video if lx_anonymizer is not available."""
287
+ try:
288
+ self.logger.info("Attempting to anonymize video using fallback method.")
289
+ # This requires sensitive meta to be verified.
290
+ if self.current_video.sensitive_meta:
291
+ self.current_video.sensitive_meta.is_verified = True
292
+ self.current_video.sensitive_meta.save()
293
+
294
+ if self.current_video.anonymize_video(delete_original_raw=False):
295
+ self.logger.info("Fallback anonymization successful.")
296
+ self.processing_context['anonymization_completed'] = True
297
+ else:
298
+ self.logger.warning("Fallback anonymization failed.")
299
+ self.processing_context['anonymization_completed'] = False
300
+ self.processing_context['error_reason'] = "Fallback anonymization failed"
301
+ except Exception as e:
302
+ self.logger.error(f"Error during fallback anonymization: {e}", exc_info=True)
303
+ self.processing_context['anonymization_completed'] = False
304
+ self.processing_context['error_reason'] = f"Fallback anonymization failed: {e}"
305
+
306
+ def _finalize_processing(self):
307
+ """Finalize processing and update video state."""
308
+ self.logger.info("Updating video processing state...")
309
+
310
+ with transaction.atomic():
311
+ # Update basic processing states
312
+ # Only mark frames as extracted if they were successfully extracted
313
+ if self.processing_context.get('frames_extracted', False):
314
+ self.current_video.state.frames_extracted = True
315
+ self.logger.info("Marked frames as extracted in state")
316
+ else:
317
+ self.logger.warning("Frames were not extracted, not updating state")
318
+
319
+ self.current_video.state.frames_initialized = True
320
+ self.current_video.state.video_meta_extracted = True
321
+ self.current_video.state.text_meta_extracted = True
322
+
323
+ # Mark sensitive meta as processed
324
+ self.current_video.state.mark_sensitive_meta_processed(save=False)
325
+
326
+ # Update completion status based on anonymization success
327
+ if self.processing_context['anonymization_completed']:
328
+ self.logger.info(f"Video {self.current_video.uuid} successfully anonymized")
329
+ else:
330
+ self.logger.warning(f"Video {self.current_video.uuid} imported but not anonymized")
331
+
332
+ # Save all state changes
333
+ self.current_video.state.save()
334
+ self.current_video.save()
335
+
336
+ # Signal completion
337
+ self._signal_completion()
338
+
339
+ def _cleanup_and_archive(self):
340
+ """Move processed video to anonym_videos and cleanup."""
341
+ from endoreg_db.utils import data_paths
342
+
343
+ # Define target directory for processed videos
344
+ anonym_videos_dir = data_paths["anonym_video"] # /data/anonym_videos
345
+ anonym_videos_dir.mkdir(parents=True, exist_ok=True)
346
+
347
+ # Check if we have a processed/cleaned video
348
+ processed_video_path = None
349
+
350
+ # Look for cleaned video from frame cleaning process
351
+ if 'cleaned_video_path' in self.processing_context:
352
+ processed_video_path = self.processing_context['cleaned_video_path']
353
+ else:
354
+ # If no processing occurred, copy from raw video location
355
+ raw_video_path = self.processing_context.get('raw_video_path')
356
+ if raw_video_path and Path(raw_video_path).exists():
357
+ video_filename = self.processing_context.get('video_filename', Path(raw_video_path).name)
358
+ processed_filename = f"processed_{video_filename}"
359
+ processed_video_path = Path(raw_video_path).parent / processed_filename
360
+
361
+ # Copy raw to processed location (will be moved to anonym_videos)
362
+ try:
363
+ shutil.copy2(str(raw_video_path), str(processed_video_path))
364
+ self.logger.info(f"Copied raw video for processing: {processed_video_path}")
365
+ except Exception as e:
366
+ self.logger.error(f"Failed to copy raw video: {e}")
367
+ processed_video_path = raw_video_path # Use original as fallback
368
+
369
+ # Move processed video to anonym_videos
370
+ if processed_video_path and Path(processed_video_path).exists():
371
+ try:
372
+ anonym_video_filename = f"anonym_{self.processing_context.get('video_filename', Path(processed_video_path).name)}"
373
+ anonym_target_path = anonym_videos_dir / anonym_video_filename
374
+
375
+ shutil.move(str(processed_video_path), str(anonym_target_path))
376
+ self.logger.info(f"Moved processed video to: {anonym_target_path}")
377
+
378
+ storage_root = data_paths["storage"]
379
+ relative_path = anonym_target_path.relative_to(storage_root)
380
+
381
+ # Update the file field in database (for processed video)
382
+ try:
383
+ self.current_video.processed_file = str(relative_path)
384
+ self.current_video.save(update_fields=['processed_file'])
385
+ self.logger.info(f"Updated file path to: {relative_path}")
386
+ except FieldError:
387
+ self.logger.warning("Field 'processed_file' does not exist on VideoFile, skipping update")
388
+ raise FieldError
389
+ except Exception as e:
390
+ self.logger.error(f"Failed to move processed video to anonym_videos: {e}")
391
+
392
+ # Cleanup temporary directories
393
+ try:
394
+ from endoreg_db.utils.paths import RAW_FRAME_DIR
395
+ shutil.rmtree(RAW_FRAME_DIR, ignore_errors=True)
396
+ self.logger.debug(f"Cleaned up temporary frames directory: {RAW_FRAME_DIR}")
397
+ except Exception as e:
398
+ self.logger.warning(f"Failed to remove directory {RAW_FRAME_DIR}: {e}")
399
+
400
+ # Handle source file deletion - this should already be moved, but check raw_videos
401
+ source_path = self.processing_context['file_path']
402
+ if self.processing_context['delete_source'] and Path(source_path).exists():
403
+ try:
404
+ os.remove(source_path)
405
+ self.logger.info(f"Removed remaining source file: {source_path}")
406
+ except Exception as e:
407
+ self.logger.warning(f"Failed to remove source file {source_path}: {e}")
408
+
409
+ # Mark as processed
410
+ self.processed_files.add(str(self.processing_context['file_path']))
411
+
412
+ # Refresh from database and finalize state
413
+ with transaction.atomic():
414
+ self.current_video.refresh_from_db()
415
+ if hasattr(self.current_video, 'state'):
416
+ self.current_video.state.mark_sensitive_meta_processed(save=True)
417
+
418
+ self.logger.info(f"Import and anonymization completed for VideoFile UUID: {self.current_video.uuid}")
419
+ self.logger.info("Raw video stored in: /data/videos")
420
+ self.logger.info("Processed video stored in: /data/anonym_videos")
421
+
422
+ def _create_sensitive_file(self, video_instance: "VideoFile" = None, file_path: Union[Path, str] = None) -> Path:
423
+ """
424
+ Create a sensitive file for the given video file by copying the original file and updating the path.
425
+ Uses the central video instance and processing context if parameters not provided.
426
+
427
+ Args:
428
+ video_instance: Optional video instance, defaults to self.current_video
429
+ file_path: Optional file path, defaults to processing_context['file_path']
430
+
431
+ Returns:
432
+ Path: The path to the created sensitive file.
433
+ """
434
+ video_file = video_instance or self.current_video
435
+ # Always use the currently stored raw file path from the model to avoid deleting external source assets
436
+ source_path = None
437
+ try:
438
+ if video_file and hasattr(video_file, 'raw_file') and video_file.raw_file and hasattr(video_file.raw_file, 'path'):
439
+ source_path = Path(video_file.raw_file.path)
440
+ except Exception:
441
+ source_path = None
442
+ # Fallback only if explicitly provided (do NOT default to processing_context input file)
443
+ if source_path is None and file_path is not None:
444
+ source_path = Path(file_path)
445
+
446
+ if not video_file:
447
+ raise ValueError("No video instance available for creating sensitive file")
448
+ if not source_path:
449
+ raise ValueError("No file path available for creating sensitive file")
450
+
451
+ if not video_file.raw_file:
452
+ raise ValueError("VideoFile must have a raw_file to create a sensitive file")
453
+
454
+ # Ensure the target directory exists
455
+ target_dir = VIDEO_DIR / 'sensitive'
456
+ if not target_dir.exists():
457
+ self.logger.info(f"Creating sensitive file directory: {target_dir}")
458
+ os.makedirs(target_dir, exist_ok=True)
459
+
460
+ # Move the stored raw file into the sensitive directory within storage
461
+ target_file_path = target_dir / source_path.name
462
+ try:
463
+ # Prefer a move within the storage to avoid extra disk usage. This does not touch external input files.
464
+ shutil.move(str(source_path), str(target_file_path))
465
+ self.logger.info(f"Moved raw file to sensitive directory: {target_file_path}")
466
+ except Exception as e:
467
+ # Fallback to copy if move fails (e.g., cross-device or permissions), then remove only the original stored raw file
468
+ self.logger.warning(f"Failed to move raw file to sensitive dir, copying instead: {e}")
469
+ shutil.copy(str(source_path), str(target_file_path))
470
+ try:
471
+ # Remove only the stored raw file copy; never touch external input paths here
472
+ os.remove(source_path)
473
+ except FileNotFoundError:
474
+ pass
475
+
476
+ # Update the model to point to the sensitive file location
477
+ # Use relative path from storage root, like in create_from_file.py
478
+ try:
479
+ from endoreg_db.utils import data_paths
480
+ storage_root = data_paths["storage"]
481
+ relative_path = target_file_path.relative_to(storage_root)
482
+ video_file.raw_file.name = str(relative_path)
483
+ video_file.save(update_fields=['raw_file'])
484
+ self.logger.info(f"Updated video.raw_file to point to sensitive location: {relative_path}")
485
+ except Exception as e:
486
+ # Fallback to absolute path conversion if relative path fails
487
+ self.logger.warning(f"Failed to set relative path, using fallback: {e}")
488
+ video_file.raw_file.name = f"videos/sensitive/{target_file_path.name}"
489
+ video_file.save(update_fields=['raw_file'])
490
+ self.logger.info(f"Updated video.raw_file using fallback method: videos/sensitive/{target_file_path.name}")
491
+
492
+ # Important: Do NOT remove the original input asset passed to the service here.
493
+ # Source file cleanup for external inputs is handled by create_from_file via delete_source flag.
494
+
495
+ self.logger.info(f"Created sensitive file for {video_file.uuid} at {target_file_path}")
496
+ return target_file_path
497
+
498
+
499
+
500
+
501
+ def _ensure_frame_cleaning_available(self):
502
+ """
503
+ Ensure frame cleaning modules are available by adding lx-anonymizer to path.
504
+
505
+ Returns:
506
+ Tuple of (availability_flag, FrameCleaner_class, ReportReader_class)
507
+ """
508
+ try:
509
+ # Check if we can find the lx-anonymizer directory
510
+ from importlib import resources
511
+ lx_anonymizer_path = resources.files("lx_anonymizer")
512
+
513
+ if lx_anonymizer_path.exists():
514
+ # Add to Python path temporarily
515
+ if str(lx_anonymizer_path) not in sys.path:
516
+ sys.path.insert(0, str(lx_anonymizer_path))
517
+
518
+ # Try simple import
519
+ from lx_anonymizer import FrameCleaner, ReportReader
520
+
521
+ self.logger.info("Successfully imported lx_anonymizer modules")
522
+
523
+ # Remove from path to avoid conflicts
524
+ if str(lx_anonymizer_path) in sys.path:
525
+ sys.path.remove(str(lx_anonymizer_path))
526
+
527
+ return True, FrameCleaner, ReportReader
528
+
529
+ else:
530
+ self.logger.warning(f"lx-anonymizer path not found: {lx_anonymizer_path}")
531
+
532
+ except Exception as e:
533
+ self.logger.warning(f"Frame cleaning not available: {e}")
534
+
535
+ return False, None, None
536
+
537
+ def _get_processor_roi_info(self):
538
+ """Get processor ROI information for masking."""
539
+ processor_roi = None
540
+ endoscope_roi = None
541
+
542
+ try:
543
+ if self.current_video.video_meta and self.current_video.video_meta.processor:
544
+ processor = getattr(self.current_video.video_meta, "processor", None)
545
+
546
+ # Get the endoscope ROI for masking
547
+ endoscope_roi = processor.get_roi_endoscope_image()
548
+
549
+ # Get all processor ROIs for comprehensive masking
550
+ processor_roi = {
551
+ 'endoscope_image': endoscope_roi,
552
+ 'patient_first_name': processor.get_roi_patient_first_name(),
553
+ 'patient_last_name': processor.get_roi_patient_last_name(),
554
+ 'patient_dob': processor.get_roi_patient_dob(),
555
+ 'examination_date': processor.get_roi_examination_date(),
556
+ 'examination_time': processor.get_roi_examination_time(),
557
+ 'endoscope_type': processor.get_roi_endoscope_type(),
558
+ 'endoscopy_sn': processor.get_roi_endoscopy_sn(),
559
+ }
560
+
561
+ self.logger.info(f"Retrieved processor ROI information: endoscope_roi={endoscope_roi}")
562
+ else:
563
+ self.logger.warning(f"No processor found for video {self.current_video.uuid}, proceeding without ROI masking")
564
+
565
+ except Exception as e:
566
+ self.logger.error(f"Failed to retrieve processor ROI information: {e}")
567
+ # Continue without ROI - don't fail the entire import process
568
+
569
+ return processor_roi, endoscope_roi
570
+
571
+
572
+ def _ensure_default_patient_data(self, video_instance: "VideoFile" = None) -> None:
573
+ """
574
+ Ensure video has minimum required patient data in SensitiveMeta.
575
+ Creates default values if data is missing after OCR processing.
576
+ Uses the central video instance if parameter not provided.
577
+
578
+ Args:
579
+ video_instance: Optional video instance, defaults to self.current_video
580
+ """
581
+ video_file = video_instance or self.current_video
582
+
583
+ if not video_file:
584
+ raise ValueError("No video instance available for ensuring patient data")
585
+
586
+ if not video_file.sensitive_meta:
587
+ self.logger.info(f"No SensitiveMeta found for video {video_file.uuid}, creating default")
588
+
589
+ # Create default SensitiveMeta with placeholder data
590
+ default_data = {
591
+ "patient_first_name": "Patient",
592
+ "patient_last_name": "Unknown",
593
+ "patient_dob": date(1990, 1, 1), # Default DOB
594
+ "examination_date": date.today(),
595
+ "center_name": video_file.center.name if video_file.center else "university_hospital_wuerzburg"
596
+ }
597
+
598
+ try:
599
+ sensitive_meta = SensitiveMeta.create_from_dict(default_data)
600
+ video_file.sensitive_meta = sensitive_meta
601
+ video_file.save(update_fields=['sensitive_meta'])
602
+
603
+ # Mark sensitive meta as processed after creating default data
604
+ state = video_file.get_or_create_state()
605
+ state.mark_sensitive_meta_processed(save=True)
606
+
607
+ self.logger.info(f"Created default SensitiveMeta for video {video_file.uuid}")
608
+ except Exception as e:
609
+ self.logger.error(f"Failed to create default SensitiveMeta for video {video_file.uuid}: {e}")
610
+ return
611
+
612
+ else:
613
+ # Update existing SensitiveMeta with missing fields
614
+ update_needed = False
615
+ update_data = {}
616
+
617
+ if not video_file.sensitive_meta.patient_first_name:
618
+ update_data["patient_first_name"] = "Patient"
619
+ update_needed = True
620
+
621
+ if not video_file.sensitive_meta.patient_last_name:
622
+ update_data["patient_last_name"] = "Unknown"
623
+ update_needed = True
624
+
625
+ if not video_file.sensitive_meta.patient_dob:
626
+ update_data["patient_dob"] = date(1990, 1, 1)
627
+ update_needed = True
628
+
629
+ if not video_file.sensitive_meta.examination_date:
630
+ update_data["examination_date"] = date.today()
631
+ update_needed = True
632
+
633
+ if update_needed:
634
+ try:
635
+ video_file.sensitive_meta.update_from_dict(update_data)
636
+
637
+ # Mark sensitive meta as processed after updating missing fields
638
+ state = video_file.get_or_create_state()
639
+ state.mark_sensitive_meta_processed(save=True)
640
+
641
+ self.logger.info(f"Updated missing SensitiveMeta fields for video {video_file.uuid}: {list(update_data.keys())}")
642
+ except Exception as e:
643
+ self.logger.error(f"Failed to update SensitiveMeta for video {video_file.uuid}: {e}")
644
+
645
+
646
+ def _ensure_frame_cleaning_available(self):
647
+ """
648
+ Ensure frame cleaning modules are available by adding lx-anonymizer to path.
649
+
650
+ Returns:
651
+ Tuple of (availability_flag, FrameCleaner_class, ReportReader_class)
652
+ """
653
+ try:
654
+ # Check if we can find the lx-anonymizer directory
655
+ from importlib import resources
656
+ lx_anonymizer_path = resources.files("lx_anonymizer")
657
+
658
+ if lx_anonymizer_path.exists():
659
+ # Add to Python path temporarily
660
+ if str(lx_anonymizer_path) not in sys.path:
661
+ sys.path.insert(0, str(lx_anonymizer_path))
662
+
663
+ # Try simple import
664
+ from lx_anonymizer import FrameCleaner, ReportReader
665
+
666
+ self.logger.info("Successfully imported lx_anonymizer modules")
667
+
668
+ # Remove from path to avoid conflicts
669
+ if str(lx_anonymizer_path) in sys.path:
670
+ sys.path.remove(str(lx_anonymizer_path))
671
+
672
+ return True, FrameCleaner, ReportReader
673
+
674
+ else:
675
+ self.logger.warning(f"lx-anonymizer path not found: {lx_anonymizer_path}")
676
+
677
+ except Exception as e:
678
+ self.logger.warning(f"Frame cleaning not available: {e}")
679
+
680
+ return False, None, None
681
+
682
+ def _get_processor_roi_info(self):
683
+ """Get processor ROI information for masking."""
684
+ processor_roi = None
685
+ endoscope_roi = None
686
+
687
+ try:
688
+ if self.current_video.video_meta and self.current_video.video_meta.processor:
689
+ processor = getattr(self.current_video.video_meta, "processor", None)
690
+
691
+ # Get the endoscope ROI for masking
692
+ endoscope_roi = processor.get_roi_endoscope_image()
693
+
694
+ # Get all processor ROIs for comprehensive masking
695
+ processor_roi = {
696
+ 'endoscope_image': endoscope_roi,
697
+ 'patient_first_name': processor.get_roi_patient_first_name(),
698
+ 'patient_last_name': processor.get_roi_patient_last_name(),
699
+ 'patient_dob': processor.get_roi_patient_dob(),
700
+ 'examination_date': processor.get_roi_examination_date(),
701
+ 'examination_time': processor.get_roi_examination_time(),
702
+ 'endoscope_type': processor.get_roi_endoscope_type(),
703
+ 'endoscopy_sn': processor.get_roi_endoscopy_sn(),
704
+ }
705
+
706
+ self.logger.info(f"Retrieved processor ROI information: endoscope_roi={endoscope_roi}")
707
+ else:
708
+ self.logger.warning(f"No processor found for video {self.current_video.uuid}, proceeding without ROI masking")
709
+
710
+ except Exception as e:
711
+ self.logger.error(f"Failed to retrieve processor ROI information: {e}")
712
+ # Continue without ROI - don't fail the entire import process
713
+
714
+ return processor_roi, endoscope_roi
715
+
716
+ def _perform_frame_cleaning(self, FrameCleaner, processor_roi, endoscope_roi):
717
+ """Perform frame cleaning and anonymization."""
718
+ # Instantiate frame cleaner
719
+ frame_cleaner = FrameCleaner()
720
+
721
+ # Prepare parameters for frame cleaning
722
+ raw_video_path = self.processing_context.get('raw_video_path')
723
+
724
+ if not raw_video_path or not Path(raw_video_path).exists():
725
+ raise RuntimeError(f"Raw video path not found: {raw_video_path}")
726
+
727
+ # Get processor name safely
728
+ processor = getattr(self.current_video.video_meta, "processor", None) if self.current_video.video_meta else None
729
+ device_name = processor.name if processor else self.processing_context['processor_name']
730
+
731
+ tmp_dir = RAW_FRAME_DIR
732
+
733
+ # Create temporary output path for cleaned video
734
+ video_filename = self.processing_context.get('video_filename', Path(raw_video_path).name)
735
+ cleaned_filename = f"cleaned_{video_filename}"
736
+ cleaned_video_path = Path(raw_video_path).parent / cleaned_filename
737
+
738
+ # Clean video with ROI masking (heavy I/O operation)
739
+ actual_cleaned_path, extracted_metadata = frame_cleaner.clean_video(
740
+ Path(raw_video_path),
741
+ self.current_video,
742
+ tmp_dir,
743
+ device_name,
744
+ endoscope_roi,
745
+ processor_roi,
746
+ cleaned_video_path
747
+ )
748
+
749
+ # Optional: enrich metadata using TrOCR+LLM on one random extracted frame
750
+ try:
751
+ # Prefer frames belonging to this video (UUID in path), else pick any frame
752
+ frame_candidates = list(RAW_FRAME_DIR.rglob("*.jpg")) + list(RAW_FRAME_DIR.rglob("*.png"))
753
+ video_uuid = str(self.current_video.uuid)
754
+ filtered = [p for p in frame_candidates if video_uuid in str(p)] or frame_candidates
755
+ if filtered:
756
+ sample_frame = random.choice(filtered)
757
+ ocr_text = trocr_full_image_ocr(sample_frame)
758
+ if ocr_text:
759
+ llm_metadata = frame_cleaner.extract_metadata(ocr_text)
760
+ if llm_metadata:
761
+ # Merge with already extracted frame-level metadata
762
+ extracted_metadata = frame_cleaner.frame_metadata_extractor.merge_metadata(
763
+ extracted_metadata or {}, llm_metadata
764
+ )
765
+ self.logger.info("LLM metadata extraction (random frame) successful")
766
+ else:
767
+ self.logger.info("LLM metadata extraction (random frame) found no data")
768
+ else:
769
+ self.logger.info("No text extracted by TrOCR on random frame")
770
+ except Exception as e:
771
+ self.logger.error(f"LLM metadata enrichment step failed: {e}")
772
+
773
+ # Store cleaned video path for later use in _cleanup_and_archive
774
+ self.processing_context['cleaned_video_path'] = actual_cleaned_path
775
+ self.processing_context['extracted_metadata'] = extracted_metadata
776
+
777
+ # Update sensitive metadata with extracted information
778
+ self._update_sensitive_metadata(extracted_metadata)
779
+ self.logger.info(f"Extracted metadata from frame cleaning: {extracted_metadata}")
780
+
781
+ self.logger.info(f"Frame cleaning with ROI masking completed: {actual_cleaned_path}")
782
+ self.logger.info("Cleaned video will be moved to anonym_videos during cleanup")
783
+
784
+ def _update_sensitive_metadata(self, extracted_metadata):
785
+ """
786
+ Update sensitive metadata with extracted information.
787
+
788
+ SAFETY MECHANISM: Only updates fields that are empty, default values, or explicitly marked as safe to overwrite.
789
+ This prevents accidentally overwriting valuable manually entered or previously extracted data.
790
+ """
791
+ if not (self.current_video.sensitive_meta and extracted_metadata):
792
+ return
793
+
794
+ sm = self.current_video.sensitive_meta
795
+ updated_fields = []
796
+
797
+ # Map extracted metadata to SensitiveMeta fields
798
+ metadata_mapping = {
799
+ 'patient_first_name': 'patient_first_name',
800
+ 'patient_last_name': 'patient_last_name',
801
+ 'patient_dob': 'patient_dob',
802
+ 'examination_date': 'examination_date',
803
+ 'endoscope_type': 'endoscope_type'
804
+ }
805
+
806
+ # Define default/placeholder values that are safe to overwrite
807
+ SAFE_TO_OVERWRITE_VALUES = [
808
+ 'Patient', # Default first name
809
+ 'Unknown', # Default last name
810
+ date(1990, 1, 1), # Default DOB
811
+ None, # Empty values
812
+ '', # Empty strings
813
+ 'N/A', # Placeholder values
814
+ 'Unknown Device', # Default device name
815
+ ]
816
+
817
+ for meta_key, sm_field in metadata_mapping.items():
818
+ if extracted_metadata.get(meta_key) and hasattr(sm, sm_field):
819
+ old_value = getattr(sm, sm_field)
820
+ new_value = extracted_metadata[meta_key]
821
+
822
+ # Enhanced safety check: Only update if current value is safe to overwrite
823
+ if new_value and (old_value in SAFE_TO_OVERWRITE_VALUES):
824
+ self.logger.info(f"Updating {sm_field} from '{old_value}' to '{new_value}' for video {self.current_video.uuid}")
825
+ setattr(sm, sm_field, new_value)
826
+ updated_fields.append(sm_field)
827
+ elif new_value and old_value and old_value not in SAFE_TO_OVERWRITE_VALUES:
828
+ self.logger.info(f"Preserving existing {sm_field} value '{old_value}' (not overwriting with '{new_value}') for video {self.current_video.uuid}")
829
+
830
+ if updated_fields:
831
+ sm.save(update_fields=updated_fields)
832
+ self.logger.info(f"Updated SensitiveMeta fields for video {self.current_video.uuid}: {updated_fields}")
833
+
834
+ # Mark sensitive meta as processed after successful update
835
+ self.current_video.state.mark_sensitive_meta_processed(save=True)
836
+ self.logger.info(f"Marked sensitive metadata as processed for video {self.current_video.uuid}")
837
+ else:
838
+ self.logger.info(f"No SensitiveMeta fields updated for video {self.current_video.uuid} - all existing values preserved")
839
+
840
+ def _signal_completion(self):
841
+ """Signal completion to the tracking system."""
842
+ try:
843
+ video_processing_complete = (
844
+ self.current_video.sensitive_meta is not None and
845
+ self.current_video.video_meta is not None and
846
+ self.current_video.raw_file and
847
+ hasattr(self.current_video.raw_file, 'path') and
848
+ Path(self.current_video.raw_file.path).exists()
849
+ )
850
+
851
+ if video_processing_complete:
852
+ self.logger.info(f"Video {self.current_video.uuid} processing completed successfully - ready for validation")
853
+
854
+ # Update completion flags if they exist
855
+ completion_fields = []
856
+ for field_name in ['import_completed', 'processing_complete', 'ready_for_validation']:
857
+ if hasattr(self.current_video, field_name):
858
+ setattr(self.current_video, field_name, True)
859
+ completion_fields.append(field_name)
860
+
861
+ if completion_fields:
862
+ self.current_video.save(update_fields=completion_fields)
863
+ self.logger.info(f"Updated completion flags: {completion_fields}")
864
+ else:
865
+ self.logger.warning(f"Video {self.current_video.uuid} processing incomplete - missing required components")
866
+
867
+ except Exception as e:
868
+ self.logger.warning(f"Failed to signal completion status: {e}")
869
+
870
+ def _cleanup_on_error(self):
871
+ """Cleanup processing context on error."""
872
+ if self.current_video and hasattr(self.current_video, 'state'):
873
+ try:
874
+ if self.processing_context.get('processing_started'):
875
+ self.current_video.state.frames_extracted = False
876
+ self.current_video.state.frames_initialized = False
877
+ self.current_video.state.video_meta_extracted = False
878
+ self.current_video.state.text_meta_extracted = False
879
+ self.current_video.state.save()
880
+ except Exception as e:
881
+ self.logger.warning(f"Error during cleanup: {e}")
882
+
883
+ def _cleanup_processing_context(self):
884
+ """Cleanup processing context."""
885
+ try:
886
+ # Clean up any temporary processing artifacts
887
+ if self.processing_context.get('frames_extracted'):
888
+ # Cleanup handled in _cleanup_and_archive
889
+ pass
890
+ except Exception as e:
891
+ self.logger.warning(f"Error during context cleanup: {e}")
892
+ finally:
893
+ # Reset context
894
+ self.current_video = None
895
+ self.processing_context = {}
896
+
897
+ # Convenience function for callers/tests that expect a module-level import_and_anonymize
898
+ def import_and_anonymize(
899
+ file_path,
900
+ center_name: str,
901
+ processor_name: str,
902
+ save_video: bool = True,
903
+ delete_source: bool = False,
904
+ ) -> "VideoFile":
905
+ """Module-level helper that instantiates VideoImportService and runs import_and_anonymize.
906
+ Kept for backward compatibility with callers that import this function directly.
907
+ """
908
+ service = VideoImportService()
909
+ return service.import_and_anonymize(
910
+ file_path=file_path,
911
+ center_name=center_name,
912
+ processor_name=processor_name,
913
+ save_video=save_video,
914
+ delete_source=delete_source,
915
+ )