endoreg-db 0.8.9.2__py3-none-any.whl → 0.8.9.10__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 (450) hide show
  1. endoreg_db/admin.py +10 -5
  2. endoreg_db/apps.py +4 -7
  3. endoreg_db/authz/auth.py +1 -0
  4. endoreg_db/authz/backends.py +1 -1
  5. endoreg_db/authz/management/commands/list_routes.py +2 -0
  6. endoreg_db/authz/middleware.py +8 -7
  7. endoreg_db/authz/permissions.py +21 -10
  8. endoreg_db/authz/policy.py +14 -19
  9. endoreg_db/authz/views_auth.py +14 -10
  10. endoreg_db/codemods/rename_datetime_fields.py +8 -1
  11. endoreg_db/exceptions.py +5 -2
  12. endoreg_db/forms/__init__.py +0 -1
  13. endoreg_db/forms/examination_form.py +4 -3
  14. endoreg_db/forms/patient_finding_intervention_form.py +30 -8
  15. endoreg_db/forms/patient_form.py +9 -13
  16. endoreg_db/forms/questionnaires/__init__.py +1 -1
  17. endoreg_db/forms/settings/__init__.py +4 -1
  18. endoreg_db/forms/unit.py +2 -1
  19. endoreg_db/helpers/count_db.py +17 -14
  20. endoreg_db/helpers/default_objects.py +2 -1
  21. endoreg_db/helpers/download_segmentation_model.py +4 -3
  22. endoreg_db/helpers/interact.py +0 -5
  23. endoreg_db/helpers/test_video_helper.py +33 -25
  24. endoreg_db/import_files/__init__.py +1 -1
  25. endoreg_db/import_files/context/__init__.py +1 -1
  26. endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
  27. endoreg_db/import_files/context/ensure_center.py +4 -4
  28. endoreg_db/import_files/context/file_lock.py +3 -3
  29. endoreg_db/import_files/context/import_context.py +11 -12
  30. endoreg_db/import_files/context/validate_directories.py +1 -0
  31. endoreg_db/import_files/file_storage/create_report_file.py +57 -34
  32. endoreg_db/import_files/file_storage/create_video_file.py +64 -35
  33. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
  34. endoreg_db/import_files/file_storage/state_management.py +89 -122
  35. endoreg_db/import_files/file_storage/storage.py +5 -1
  36. endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
  37. endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
  38. endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
  39. endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
  40. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
  41. endoreg_db/import_files/report_import_service.py +36 -30
  42. endoreg_db/import_files/video_import_service.py +27 -23
  43. endoreg_db/logger_conf.py +56 -40
  44. endoreg_db/management/__init__.py +1 -1
  45. endoreg_db/management/commands/__init__.py +1 -1
  46. endoreg_db/management/commands/check_auth.py +45 -38
  47. endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
  48. endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
  49. endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
  50. endoreg_db/management/commands/fix_video_paths.py +75 -54
  51. endoreg_db/management/commands/import_report.py +1 -3
  52. endoreg_db/management/commands/list_routes.py +2 -0
  53. endoreg_db/management/commands/load_ai_model_data.py +8 -2
  54. endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
  55. endoreg_db/management/commands/load_center_data.py +3 -3
  56. endoreg_db/management/commands/load_distribution_data.py +35 -38
  57. endoreg_db/management/commands/load_endoscope_data.py +0 -3
  58. endoreg_db/management/commands/load_examination_data.py +20 -4
  59. endoreg_db/management/commands/load_finding_data.py +18 -3
  60. endoreg_db/management/commands/load_gender_data.py +17 -24
  61. endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
  62. endoreg_db/management/commands/load_information_source.py +0 -3
  63. endoreg_db/management/commands/load_lab_value_data.py +14 -3
  64. endoreg_db/management/commands/load_legacy_data.py +303 -0
  65. endoreg_db/management/commands/load_name_data.py +1 -2
  66. endoreg_db/management/commands/load_pdf_type_data.py +4 -8
  67. endoreg_db/management/commands/load_profession_data.py +0 -1
  68. endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
  69. endoreg_db/management/commands/load_requirement_data.py +6 -2
  70. endoreg_db/management/commands/load_unit_data.py +0 -4
  71. endoreg_db/management/commands/load_user_groups.py +5 -7
  72. endoreg_db/management/commands/model_input.py +169 -0
  73. endoreg_db/management/commands/register_ai_model.py +22 -16
  74. endoreg_db/management/commands/setup_endoreg_db.py +110 -32
  75. endoreg_db/management/commands/storage_management.py +14 -8
  76. endoreg_db/management/commands/summarize_db_content.py +154 -63
  77. endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
  78. endoreg_db/management/commands/validate_video_files.py +82 -50
  79. endoreg_db/management/commands/video_validation.py +4 -6
  80. endoreg_db/migrations/0001_initial.py +112 -63
  81. endoreg_db/models/__init__.py +8 -0
  82. endoreg_db/models/administration/ai/active_model.py +5 -5
  83. endoreg_db/models/administration/ai/ai_model.py +41 -18
  84. endoreg_db/models/administration/ai/model_type.py +1 -0
  85. endoreg_db/models/administration/case/case.py +22 -22
  86. endoreg_db/models/administration/center/__init__.py +5 -5
  87. endoreg_db/models/administration/center/center.py +6 -2
  88. endoreg_db/models/administration/center/center_resource.py +18 -4
  89. endoreg_db/models/administration/center/center_shift.py +3 -1
  90. endoreg_db/models/administration/center/center_waste.py +6 -2
  91. endoreg_db/models/administration/person/__init__.py +1 -1
  92. endoreg_db/models/administration/person/employee/__init__.py +1 -1
  93. endoreg_db/models/administration/person/employee/employee_type.py +3 -1
  94. endoreg_db/models/administration/person/examiner/__init__.py +1 -1
  95. endoreg_db/models/administration/person/examiner/examiner.py +10 -2
  96. endoreg_db/models/administration/person/names/first_name.py +6 -4
  97. endoreg_db/models/administration/person/names/last_name.py +4 -3
  98. endoreg_db/models/administration/person/patient/__init__.py +1 -1
  99. endoreg_db/models/administration/person/patient/patient.py +0 -1
  100. endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
  101. endoreg_db/models/administration/person/person.py +1 -1
  102. endoreg_db/models/administration/product/__init__.py +7 -6
  103. endoreg_db/models/administration/product/product.py +6 -2
  104. endoreg_db/models/administration/product/product_group.py +9 -7
  105. endoreg_db/models/administration/product/product_material.py +9 -2
  106. endoreg_db/models/administration/product/reference_product.py +64 -15
  107. endoreg_db/models/administration/qualification/qualification.py +3 -1
  108. endoreg_db/models/administration/shift/shift.py +3 -1
  109. endoreg_db/models/administration/shift/shift_type.py +12 -4
  110. endoreg_db/models/aidataset/__init__.py +5 -0
  111. endoreg_db/models/aidataset/aidataset.py +193 -0
  112. endoreg_db/models/label/__init__.py +1 -1
  113. endoreg_db/models/label/label.py +10 -2
  114. endoreg_db/models/label/label_set.py +3 -1
  115. endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
  116. endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
  117. endoreg_db/models/media/__init__.py +12 -5
  118. endoreg_db/models/media/frame/__init__.py +1 -1
  119. endoreg_db/models/media/frame/frame.py +34 -8
  120. endoreg_db/models/media/pdf/__init__.py +2 -1
  121. endoreg_db/models/media/pdf/raw_pdf.py +11 -4
  122. endoreg_db/models/media/pdf/report_file.py +6 -2
  123. endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
  124. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
  125. endoreg_db/models/media/video/create_from_file.py +20 -41
  126. endoreg_db/models/media/video/pipe_1.py +75 -30
  127. endoreg_db/models/media/video/pipe_2.py +37 -12
  128. endoreg_db/models/media/video/video_file.py +36 -24
  129. endoreg_db/models/media/video/video_file_ai.py +235 -70
  130. endoreg_db/models/media/video/video_file_anonymize.py +240 -65
  131. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
  132. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
  133. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
  134. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
  135. endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
  136. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
  137. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
  138. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
  139. endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
  140. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
  141. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
  142. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
  143. endoreg_db/models/media/video/video_file_io.py +85 -33
  144. endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
  145. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
  146. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
  147. endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
  148. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
  149. endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
  150. endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
  151. endoreg_db/models/media/video/video_file_segments.py +118 -27
  152. endoreg_db/models/media/video/video_metadata.py +25 -6
  153. endoreg_db/models/media/video/video_processing.py +54 -15
  154. endoreg_db/models/medical/__init__.py +3 -13
  155. endoreg_db/models/medical/contraindication/__init__.py +3 -1
  156. endoreg_db/models/medical/disease.py +18 -6
  157. endoreg_db/models/medical/event.py +6 -2
  158. endoreg_db/models/medical/examination/__init__.py +5 -1
  159. endoreg_db/models/medical/examination/examination.py +22 -6
  160. endoreg_db/models/medical/examination/examination_indication.py +23 -7
  161. endoreg_db/models/medical/examination/examination_time.py +6 -2
  162. endoreg_db/models/medical/finding/__init__.py +3 -1
  163. endoreg_db/models/medical/finding/finding.py +37 -12
  164. endoreg_db/models/medical/finding/finding_classification.py +27 -8
  165. endoreg_db/models/medical/finding/finding_intervention.py +19 -6
  166. endoreg_db/models/medical/finding/finding_type.py +3 -1
  167. endoreg_db/models/medical/hardware/__init__.py +1 -1
  168. endoreg_db/models/medical/hardware/endoscope.py +14 -2
  169. endoreg_db/models/medical/laboratory/__init__.py +1 -1
  170. endoreg_db/models/medical/laboratory/lab_value.py +139 -39
  171. endoreg_db/models/medical/medication/__init__.py +7 -3
  172. endoreg_db/models/medical/medication/medication.py +3 -1
  173. endoreg_db/models/medical/medication/medication_indication.py +3 -1
  174. endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
  175. endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
  176. endoreg_db/models/medical/medication/medication_schedule.py +3 -1
  177. endoreg_db/models/medical/patient/__init__.py +2 -10
  178. endoreg_db/models/medical/patient/medication_examples.py +3 -14
  179. endoreg_db/models/medical/patient/patient_disease.py +17 -5
  180. endoreg_db/models/medical/patient/patient_event.py +12 -4
  181. endoreg_db/models/medical/patient/patient_examination.py +52 -15
  182. endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
  183. endoreg_db/models/medical/patient/patient_finding.py +105 -29
  184. endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
  185. endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
  186. endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
  187. endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
  188. endoreg_db/models/medical/patient/patient_medication.py +25 -7
  189. endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
  190. endoreg_db/models/metadata/model_meta.py +40 -12
  191. endoreg_db/models/metadata/model_meta_logic.py +51 -16
  192. endoreg_db/models/metadata/sensitive_meta.py +65 -28
  193. endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
  194. endoreg_db/models/metadata/video_meta.py +146 -39
  195. endoreg_db/models/metadata/video_prediction_logic.py +70 -21
  196. endoreg_db/models/metadata/video_prediction_meta.py +80 -27
  197. endoreg_db/models/operation_log.py +63 -0
  198. endoreg_db/models/other/__init__.py +10 -10
  199. endoreg_db/models/other/distribution/__init__.py +9 -7
  200. endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
  201. endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
  202. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
  203. endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
  204. endoreg_db/models/other/emission/__init__.py +1 -1
  205. endoreg_db/models/other/emission/emission_factor.py +9 -3
  206. endoreg_db/models/other/information_source.py +15 -5
  207. endoreg_db/models/other/material.py +3 -1
  208. endoreg_db/models/other/transport_route.py +3 -1
  209. endoreg_db/models/other/unit.py +6 -2
  210. endoreg_db/models/report/report.py +0 -1
  211. endoreg_db/models/requirement/requirement.py +84 -27
  212. endoreg_db/models/requirement/requirement_error.py +5 -6
  213. endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
  214. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
  215. endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
  216. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
  217. endoreg_db/models/requirement/requirement_operator.py +28 -8
  218. endoreg_db/models/requirement/requirement_set.py +34 -11
  219. endoreg_db/models/state/__init__.py +1 -0
  220. endoreg_db/models/state/audit_ledger.py +9 -2
  221. endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
  222. endoreg_db/models/state/processing_history/processing_history.py +136 -0
  223. endoreg_db/models/state/raw_pdf.py +0 -1
  224. endoreg_db/models/state/video.py +2 -4
  225. endoreg_db/models/utils.py +4 -2
  226. endoreg_db/queries/__init__.py +2 -6
  227. endoreg_db/queries/annotations/__init__.py +1 -3
  228. endoreg_db/queries/annotations/legacy.py +37 -26
  229. endoreg_db/root_urls.py +3 -4
  230. endoreg_db/schemas/examination_evaluation.py +3 -0
  231. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
  232. endoreg_db/serializers/__init__.py +2 -8
  233. endoreg_db/serializers/administration/__init__.py +1 -2
  234. endoreg_db/serializers/administration/ai/__init__.py +0 -1
  235. endoreg_db/serializers/administration/ai/active_model.py +3 -1
  236. endoreg_db/serializers/administration/ai/ai_model.py +5 -3
  237. endoreg_db/serializers/administration/ai/model_type.py +3 -1
  238. endoreg_db/serializers/administration/center.py +7 -2
  239. endoreg_db/serializers/administration/gender.py +4 -2
  240. endoreg_db/serializers/anonymization.py +13 -13
  241. endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
  242. endoreg_db/serializers/examination/__init__.py +1 -1
  243. endoreg_db/serializers/examination/base.py +12 -13
  244. endoreg_db/serializers/examination/dropdown.py +6 -7
  245. endoreg_db/serializers/examination_serializer.py +3 -6
  246. endoreg_db/serializers/finding/__init__.py +1 -1
  247. endoreg_db/serializers/finding/finding.py +14 -7
  248. endoreg_db/serializers/finding_classification/__init__.py +3 -3
  249. endoreg_db/serializers/finding_classification/choice.py +3 -3
  250. endoreg_db/serializers/finding_classification/classification.py +2 -4
  251. endoreg_db/serializers/label_video_segment/__init__.py +5 -3
  252. endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
  253. endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
  254. endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
  255. endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
  256. endoreg_db/serializers/meta/__init__.py +1 -2
  257. endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
  258. endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
  259. endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
  260. endoreg_db/serializers/misc/__init__.py +2 -2
  261. endoreg_db/serializers/misc/file_overview.py +11 -7
  262. endoreg_db/serializers/misc/stats.py +10 -8
  263. endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
  264. endoreg_db/serializers/misc/upload_job.py +32 -29
  265. endoreg_db/serializers/patient/__init__.py +2 -1
  266. endoreg_db/serializers/patient/patient.py +32 -15
  267. endoreg_db/serializers/patient/patient_dropdown.py +11 -3
  268. endoreg_db/serializers/patient_examination/__init__.py +1 -1
  269. endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
  270. endoreg_db/serializers/patient_finding/__init__.py +1 -1
  271. endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
  272. endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
  273. endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
  274. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
  275. endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
  276. endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
  277. endoreg_db/serializers/pdf/__init__.py +1 -3
  278. endoreg_db/serializers/requirements/requirement_schema.py +1 -6
  279. endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
  280. endoreg_db/serializers/video/__init__.py +2 -2
  281. endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
  282. endoreg_db/serializers/video/video_file_brief.py +6 -2
  283. endoreg_db/serializers/video/video_file_detail.py +36 -23
  284. endoreg_db/serializers/video/video_file_list.py +4 -2
  285. endoreg_db/serializers/video/video_processing_history.py +54 -50
  286. endoreg_db/services/__init__.py +1 -1
  287. endoreg_db/services/anonymization.py +2 -2
  288. endoreg_db/services/examination_evaluation.py +40 -17
  289. endoreg_db/services/model_meta_from_hf.py +76 -0
  290. endoreg_db/services/polling_coordinator.py +101 -70
  291. endoreg_db/services/pseudonym_service.py +27 -22
  292. endoreg_db/services/report_import.py +6 -3
  293. endoreg_db/services/segment_sync.py +75 -59
  294. endoreg_db/services/video_import.py +6 -7
  295. endoreg_db/urls/__init__.py +2 -2
  296. endoreg_db/urls/ai.py +7 -25
  297. endoreg_db/urls/anonymization.py +61 -15
  298. endoreg_db/urls/auth.py +4 -4
  299. endoreg_db/urls/classification.py +4 -9
  300. endoreg_db/urls/examination.py +27 -18
  301. endoreg_db/urls/media.py +27 -34
  302. endoreg_db/urls/patient.py +11 -7
  303. endoreg_db/urls/requirements.py +3 -1
  304. endoreg_db/urls/root_urls.py +2 -3
  305. endoreg_db/urls/stats.py +24 -16
  306. endoreg_db/urls/upload.py +3 -11
  307. endoreg_db/utils/__init__.py +14 -15
  308. endoreg_db/utils/ai/__init__.py +1 -1
  309. endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
  310. endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
  311. endoreg_db/utils/ai/get.py +2 -1
  312. endoreg_db/utils/ai/inference_dataset.py +14 -15
  313. endoreg_db/utils/ai/model_training/config.py +117 -0
  314. endoreg_db/utils/ai/model_training/dataset.py +74 -0
  315. endoreg_db/utils/ai/model_training/losses.py +68 -0
  316. endoreg_db/utils/ai/model_training/metrics.py +78 -0
  317. endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
  318. endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
  319. endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
  320. endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
  321. endoreg_db/utils/ai/predict.py +4 -4
  322. endoreg_db/utils/ai/preprocess.py +19 -11
  323. endoreg_db/utils/calc_duration_seconds.py +4 -4
  324. endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
  325. endoreg_db/utils/check_video_files.py +74 -47
  326. endoreg_db/utils/cropping.py +10 -9
  327. endoreg_db/utils/dataloader.py +11 -3
  328. endoreg_db/utils/dates.py +3 -4
  329. endoreg_db/utils/defaults/set_default_center.py +7 -6
  330. endoreg_db/utils/env.py +6 -2
  331. endoreg_db/utils/extract_specific_frames.py +24 -9
  332. endoreg_db/utils/file_operations.py +30 -18
  333. endoreg_db/utils/fix_video_path_direct.py +57 -41
  334. endoreg_db/utils/frame_anonymization_utils.py +157 -157
  335. endoreg_db/utils/hashs.py +3 -18
  336. endoreg_db/utils/links/requirement_link.py +96 -52
  337. endoreg_db/utils/ocr.py +30 -25
  338. endoreg_db/utils/operation_log.py +61 -0
  339. endoreg_db/utils/parse_and_generate_yaml.py +12 -13
  340. endoreg_db/utils/paths.py +6 -6
  341. endoreg_db/utils/permissions.py +40 -24
  342. endoreg_db/utils/pipelines/process_video_dir.py +50 -26
  343. endoreg_db/utils/product/sum_emissions.py +5 -3
  344. endoreg_db/utils/product/sum_weights.py +4 -2
  345. endoreg_db/utils/pydantic_models/__init__.py +3 -4
  346. endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
  347. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
  348. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
  349. endoreg_db/utils/setup_config.py +21 -5
  350. endoreg_db/utils/storage.py +3 -1
  351. endoreg_db/utils/translation.py +19 -15
  352. endoreg_db/utils/uuid.py +1 -0
  353. endoreg_db/utils/validate_endo_roi.py +12 -4
  354. endoreg_db/utils/validate_subcategory_dict.py +26 -24
  355. endoreg_db/utils/validate_video_detailed.py +207 -149
  356. endoreg_db/utils/video/__init__.py +7 -3
  357. endoreg_db/utils/video/extract_frames.py +30 -18
  358. endoreg_db/utils/video/names.py +11 -6
  359. endoreg_db/utils/video/streaming_processor.py +175 -101
  360. endoreg_db/utils/video/video_splitter.py +30 -19
  361. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
  362. endoreg_db/views/__init__.py +0 -20
  363. endoreg_db/views/anonymization/__init__.py +6 -2
  364. endoreg_db/views/anonymization/media_management.py +2 -6
  365. endoreg_db/views/anonymization/overview.py +34 -1
  366. endoreg_db/views/anonymization/validate.py +79 -18
  367. endoreg_db/views/auth/__init__.py +1 -1
  368. endoreg_db/views/auth/keycloak.py +16 -14
  369. endoreg_db/views/examination/__init__.py +12 -15
  370. endoreg_db/views/examination/examination.py +5 -5
  371. endoreg_db/views/examination/examination_manifest_cache.py +5 -5
  372. endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
  373. endoreg_db/views/examination/get_finding_classifications.py +9 -7
  374. endoreg_db/views/examination/get_findings.py +8 -10
  375. endoreg_db/views/examination/get_instruments.py +3 -2
  376. endoreg_db/views/examination/get_interventions.py +1 -1
  377. endoreg_db/views/finding/__init__.py +2 -2
  378. endoreg_db/views/finding/finding.py +58 -54
  379. endoreg_db/views/finding/get_classifications.py +1 -1
  380. endoreg_db/views/finding/get_interventions.py +1 -1
  381. endoreg_db/views/finding_classification/__init__.py +5 -5
  382. endoreg_db/views/finding_classification/finding_classification.py +5 -6
  383. endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
  384. endoreg_db/views/media/__init__.py +13 -13
  385. endoreg_db/views/media/pdf_media.py +9 -9
  386. endoreg_db/views/media/sensitive_metadata.py +10 -7
  387. endoreg_db/views/media/video_media.py +4 -4
  388. endoreg_db/views/meta/__init__.py +1 -1
  389. endoreg_db/views/meta/sensitive_meta_list.py +20 -22
  390. endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
  391. endoreg_db/views/misc/__init__.py +6 -34
  392. endoreg_db/views/misc/center.py +2 -1
  393. endoreg_db/views/misc/csrf.py +2 -1
  394. endoreg_db/views/misc/gender.py +2 -1
  395. endoreg_db/views/misc/stats.py +141 -106
  396. endoreg_db/views/patient/__init__.py +1 -3
  397. endoreg_db/views/patient/patient.py +141 -99
  398. endoreg_db/views/patient_examination/__init__.py +5 -5
  399. endoreg_db/views/patient_examination/patient_examination.py +43 -42
  400. endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
  401. endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
  402. endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
  403. endoreg_db/views/patient_examination/video.py +114 -80
  404. endoreg_db/views/patient_finding/__init__.py +1 -1
  405. endoreg_db/views/patient_finding/patient_finding.py +17 -10
  406. endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
  407. endoreg_db/views/patient_finding_classification/__init__.py +1 -1
  408. endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
  409. endoreg_db/views/report/reimport.py +1 -1
  410. endoreg_db/views/report/report_stream.py +5 -8
  411. endoreg_db/views/requirement/__init__.py +2 -1
  412. endoreg_db/views/requirement/evaluate.py +7 -9
  413. endoreg_db/views/requirement/lookup.py +2 -3
  414. endoreg_db/views/requirement/lookup_store.py +0 -1
  415. endoreg_db/views/requirement/requirement_utils.py +2 -4
  416. endoreg_db/views/stats/__init__.py +4 -4
  417. endoreg_db/views/stats/stats_views.py +152 -115
  418. endoreg_db/views/video/__init__.py +18 -27
  419. endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
  420. endoreg_db/views/{ai → video/ai}/label.py +20 -16
  421. endoreg_db/views/video/correction.py +5 -6
  422. endoreg_db/views/video/reimport.py +134 -99
  423. endoreg_db/views/video/segments_crud.py +134 -44
  424. endoreg_db/views/video/video_apply_mask.py +13 -12
  425. endoreg_db/views/video/video_correction.py +2 -1
  426. endoreg_db/views/video/video_download_processed.py +15 -15
  427. endoreg_db/views/video/video_meta_stats.py +7 -6
  428. endoreg_db/views/video/video_processing_history.py +3 -2
  429. endoreg_db/views/video/video_remove_frames.py +13 -12
  430. endoreg_db/views/video/video_stream.py +110 -82
  431. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
  432. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +434 -431
  433. endoreg_db/management/commands/import_fallback_video.py +0 -203
  434. endoreg_db/management/commands/import_video.py +0 -422
  435. endoreg_db/management/commands/import_video_with_classification.py +0 -367
  436. endoreg_db/models/media/processing_history/processing_history.py +0 -96
  437. endoreg_db/serializers/label/__init__.py +0 -7
  438. endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
  439. endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
  440. endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
  441. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
  442. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
  443. endoreg_db/services/__old/pdf_import.py +0 -1487
  444. endoreg_db/services/__old/video_import.py +0 -1306
  445. endoreg_db/tasks/upload_tasks.py +0 -216
  446. endoreg_db/tasks/video_ingest.py +0 -161
  447. endoreg_db/tasks/video_processing_tasks.py +0 -327
  448. endoreg_db/views/misc/translation.py +0 -182
  449. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
  450. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
@@ -1,203 +0,0 @@
1
- from pathlib import Path
2
- from django.core.management.base import BaseCommand
3
- from django.db import transaction
4
- from endoreg_db.models import (
5
- VideoFile,
6
- Center,
7
- Label,
8
- VideoSegmentationLabelSet,
9
- VideoSegmentationLabel,
10
- EndoscopyProcessor
11
- )
12
-
13
- class Command(BaseCommand):
14
- help = 'Import fallback test video and create default labels'
15
-
16
- def _ensure_default_objects(self):
17
- """
18
- Ensures that default Center and EndoscopyProcessor objects exist, creating them if necessary.
19
-
20
- Returns:
21
- tuple: A tuple containing the Center and EndoscopyProcessor objects.
22
- """
23
- center, created = Center.objects.get_or_create(
24
- name="Default Center",
25
- defaults={
26
- 'description': 'Fallback center for test videos',
27
- 'location': 'Test Location'
28
- }
29
- )
30
- if created:
31
- self.stdout.write(f"Created center: {center.name}")
32
-
33
- processor, created = EndoscopyProcessor.objects.get_or_create(
34
- name="Default Processor",
35
- defaults={
36
- 'description': 'Fallback processor for test videos'
37
- }
38
- )
39
- if created:
40
- self.stdout.write(f"Created processor: {processor.name}")
41
-
42
- return center, processor
43
-
44
- def add_arguments(self, parser):
45
- """
46
- Adds the --video-path argument to specify the path of the test video file.
47
-
48
- This optional argument allows users to provide a custom path to the test video file to be imported. If not specified, a default path is used.
49
- """
50
- parser.add_argument(
51
- '--video-path',
52
- type=str,
53
- default='~/test-data/video/lux-gastro-video.mp4',
54
- help='Path to the test video file'
55
- )
56
-
57
- def handle(self, *args, **options):
58
- """
59
- Handles the import of a test video and setup of default labels for frontend testing.
60
-
61
- Checks for the existence of the specified video file. If found, ensures default Center and EndoscopyProcessor objects exist, creates default annotation labels, and imports the video into the database. If the video file is missing or import fails, creates fallback database entries to enable frontend testing.
62
- """
63
- video_path_str = options['video_path']
64
- video_path = Path(video_path_str).expanduser()
65
-
66
- self.stdout.write(f"Looking for video at: {video_path}")
67
-
68
- if not video_path.exists():
69
- self.stdout.write(
70
- self.style.WARNING(f"Video file not found at {video_path}")
71
- )
72
- # Create a fallback entry anyway for frontend testing
73
- self.create_fallback_entries()
74
- return
75
-
76
- # Create or get default center and processor
77
- center, processor = self._ensure_default_objects()
78
-
79
- # Create default labels
80
- self.create_default_labels()
81
-
82
- # Import the video if it exists
83
- try:
84
- video_file = VideoFile.create_from_file_initialized(
85
- file_path=video_path,
86
- center_name=center.name,
87
- processor_name=processor.name
88
- )
89
- self.stdout.write(
90
- self.style.SUCCESS(f"Successfully imported video: {video_file.uuid}")
91
- )
92
- except Exception as e:
93
- self.stdout.write(
94
- self.style.ERROR(f"Failed to import video: {e}")
95
- )
96
- # Create fallback entries for frontend testing
97
- self.create_fallback_entries()
98
-
99
- def create_default_labels(self):
100
- """
101
- Creates a default set of video segmentation labels and associates them with a label set.
102
-
103
- This method ensures that a predefined group of labels, each with multilingual names, colors, and order priorities, exists in the database for endoscopy video annotation. It creates or retrieves both specialized segmentation labels and general labels for compatibility, and links the segmentation labels to a default label set named "Default Endoscopy Labels."
104
- """
105
-
106
- # Create default labelset
107
- labelset, created = VideoSegmentationLabelSet.objects.get_or_create(
108
- name="Default Endoscopy Labels",
109
- defaults={
110
- 'description': 'Default labels for endoscopy video annotation'
111
- }
112
- )
113
- if created:
114
- self.stdout.write(f"Created labelset: {labelset.name}")
115
-
116
- # Fetch existing labels in the labelset to avoid N+1 queries
117
- existing_labels_in_set = set(labelset.labels.values_list('pk', flat=True))
118
-
119
- # Default labels for endoscopy
120
- default_labels = [
121
- {'name': 'outside', 'name_de': 'Außerhalb', 'name_en': 'Outside', 'color': '#00bcd4', 'order_priority': 1},
122
- {'name': 'appendix', 'name_de': 'Appendix', 'name_en': 'Appendix', 'color': '#ff9800', 'order_priority': 2},
123
- {'name': 'blood', 'name_de': 'Blut', 'name_en': 'Blood', 'color': '#f44336', 'order_priority': 3},
124
- {'name': 'diverticule', 'name_de': 'Divertikel', 'name_en': 'Diverticule', 'color': '#9c27b0', 'order_priority': 4},
125
- {'name': 'grasper', 'name_de': 'Greifer', 'name_en': 'Grasper', 'color': '#CBEDCA', 'order_priority': 5},
126
- {'name': 'ileocaecalvalve', 'name_de': 'Ileozäkalklappe', 'name_en': 'Ileocaecal Valve', 'color': '#3f51b5', 'order_priority': 6},
127
- {'name': 'ileum', 'name_de': 'Ileum', 'name_en': 'Ileum', 'color': '#2196f3', 'order_priority': 7},
128
- {'name': 'low_quality', 'name_de': 'Niedrige Bildqualität', 'name_en': 'Low Quality', 'color': '#9e9e9e', 'order_priority': 8},
129
- {'name': 'nbi', 'name_de': 'Narrow Band Imaging', 'name_en': 'NBI', 'color': '#795548', 'order_priority': 9},
130
- {'name': 'needle', 'name_de': 'Nadel', 'name_en': 'Needle', 'color': '#e91e63', 'order_priority': 10},
131
- {'name': 'polyp', 'name_de': 'Polyp', 'name_en': 'Polyp', 'color': '#8bc34a', 'order_priority': 11},
132
- {'name': 'snare', 'name_de': 'Snare', 'name_en': 'Snare', 'color': '#ff5722', 'order_priority': 12},
133
- {'name': 'water_jet', 'name_de': 'Wasserstrahl', 'name_en': 'Water Jet', 'color': '#03a9f4', 'order_priority': 13},
134
- {'name': 'wound', 'name_de': 'Wunde', 'name_en': 'Wound', 'color': '#607d8b', 'order_priority': 14},
135
- ]
136
-
137
- for label_data in default_labels:
138
- # Create VideoSegmentationLabel
139
- vs_label, created = VideoSegmentationLabel.objects.get_or_create(
140
- name=label_data['name'],
141
- defaults={
142
- 'name_de': label_data['name_de'],
143
- 'name_en': label_data['name_en'],
144
- 'color': label_data['color'],
145
- 'order_priority': label_data['order_priority'],
146
- 'description': f"Default {label_data['name_en']} label"
147
- }
148
- )
149
-
150
- # Create general Label (for compatibility)
151
- general_label, created = Label.objects.get_or_create(
152
- name=label_data['name'],
153
- defaults={
154
- 'description': f"Default {label_data['name_en']} label"
155
- }
156
- )
157
-
158
- if created:
159
- self.stdout.write(f"Created label: {label_data['name']}")
160
-
161
- # Link to labelset
162
- if vs_label.pk not in existing_labels_in_set:
163
- labelset.labels.add(vs_label)
164
- existing_labels_in_set.add(vs_label.pk) # Keep the set in sync
165
-
166
- labelset.save()
167
- self.stdout.write(f"Labelset configured with {labelset.labels.count()} labels")
168
-
169
- def create_fallback_entries(self):
170
- """
171
- Creates fallback database entries for testing when the video file is unavailable.
172
-
173
- This method ensures that default annotation labels, a default center, and a default endoscopy processor exist. If a fallback video entry named "lux-gastro-video.mp4" does not already exist, it creates a minimal `VideoFile` record with preset metadata and initializes its state and frame directory for frontend testing.
174
- """
175
- with transaction.atomic():
176
- # Create default labels anyway
177
- self.create_default_labels()
178
-
179
- # Create a placeholder VideoFile entry for frontend testing
180
- center, processor = self._ensure_default_objects()
181
-
182
- # Check if we already have a fallback video
183
- if not VideoFile.objects.filter(original_file_name="lux-gastro-video.mp4").exists():
184
- # Create a minimal VideoFile entry for frontend testing
185
- video_file = VideoFile.objects.create(
186
- original_file_name="lux-gastro-video.mp4",
187
- video_hash="fallback_hash_12345",
188
- center=center,
189
- processor=processor,
190
- fps=30.0,
191
- duration=120.0, # 2 minutes fallback
192
- frame_count=3600,
193
- width=1920,
194
- height=1080
195
- )
196
-
197
- # Initialize the video file
198
- video_file.get_or_create_state()
199
- video_file.set_frame_dir()
200
-
201
- self.stdout.write(
202
- self.style.SUCCESS(f"Created fallback video entry: {video_file.uuid}")
203
- )
@@ -1,422 +0,0 @@
1
- # See Pipe 1 video file function.
2
- #
3
-
4
- """
5
- Management command to import a video file into the database.
6
- This command is designed to be run from the command line and takes various arguments
7
- to specify the video file, center name, and other options.
8
- """
9
-
10
- from django.core.management import BaseCommand
11
- from django.core.files.base import ContentFile
12
- from django.db import connection
13
- from pathlib import Path
14
- from endoreg_db.models import VideoFile
15
- from endoreg_db.models.administration.center import Center
16
- from endoreg_db.models.medical.hardware import EndoscopyProcessor
17
- # #FIXME
18
- # from endoreg_db.management.commands import validate_video
19
-
20
- from endoreg_db.utils.video.ffmpeg_wrapper import check_ffmpeg_availability # ADDED
21
-
22
- import logging
23
- logger = logging.getLogger(__name__)
24
-
25
- # Import frame cleaning functionality - simplified approach
26
- FRAME_CLEANING_AVAILABLE = False
27
-
28
- # Try to import lx_anonymizer using the existing working import method from create_from_file
29
- try:
30
- # Check if we can find the lx-anonymizer directory
31
- current_file = Path(__file__)
32
- endoreg_db_root = current_file.parent.parent.parent.parent
33
- lx_anonymizer_path = endoreg_db_root / "lx-anonymizer"
34
-
35
- if lx_anonymizer_path.exists():
36
- # Add to Python path temporarily
37
- import sys
38
- if str(lx_anonymizer_path) not in sys.path:
39
- sys.path.insert(0, str(lx_anonymizer_path))
40
-
41
- # Try simple import
42
- from lx_anonymizer import FrameCleaner, ReportReader
43
-
44
- FRAME_CLEANING_AVAILABLE = True
45
- logger.debug("Successfully imported lx_anonymizer modules")
46
-
47
- # Remove from path to avoid conflicts
48
- if str(lx_anonymizer_path) in sys.path:
49
- sys.path.remove(str(lx_anonymizer_path))
50
-
51
- except Exception as e:
52
- logger.debug(f"Frame cleaning not available: {e}")
53
- FRAME_CLEANING_AVAILABLE = False
54
-
55
- IMPORT_MODELS = [
56
- VideoFile.__name__,
57
- ]
58
-
59
- IMPORT_METADATA = {
60
- VideoFile.__name__: {
61
- "uuid": VideoFile.uuid,
62
- "raw_file": VideoFile.raw_file,
63
- "processed_file": VideoFile.processed_file,
64
- "foreign_keys": [],
65
- "foreign_key_models": [],
66
- },
67
- }
68
-
69
- class Command(BaseCommand):
70
- help = """
71
- Creates a new VideoFile object in the database.
72
- 1. Validates the existence of the specified center and processor
73
- 2. Checks that the video file is saved and anonymized
74
- 3. Creates or updates a ModelMeta entry with the specified parameters
75
- """
76
-
77
- def add_arguments(self, parser):
78
-
79
- """
80
- Adds command-line arguments for the video import management command.
81
-
82
- Defines options for specifying the video file path, associated center and processor names, directory roots for frames and videos, deletion and saving behavior, model path, segmentation usage, and verbosity.
83
- """
84
- parser.add_argument(
85
- "--verbose",
86
- action="store_true",
87
- help="Display verbose output",
88
- )
89
- parser.add_argument(
90
- "--",
91
- type=str,
92
- default="university_hospital_wuerzburg",
93
- help="Name of the center to associate with video",
94
- )
95
-
96
- # add the path to the video file
97
- parser.add_argument(
98
- "video_file",
99
- type=Path,
100
- help="Path to the video file to import",
101
- )
102
-
103
- # frame dir parent
104
- parser.add_argument(
105
- "--frame_dir_root",
106
- type=str,
107
- default="~/test-data/raw_frame_dir",
108
- help="Path to the frame directory",
109
- )
110
-
111
- # video dir
112
- parser.add_argument(
113
- "--video_dir_root",
114
- type=str,
115
- default="~/test-data/raw_video_dir",
116
- help="Path to the video directory",
117
- )
118
-
119
- # delete source
120
- parser.add_argument(
121
- "--delete_source",
122
- action="store_true",
123
- default=False,
124
- help="Delete the source video file after importing",
125
- )
126
-
127
- # save video file
128
- parser.add_argument(
129
- "--save_video_file",
130
- action="store_true",
131
- default=True,
132
- help="Save the video file to the video directory",
133
- )
134
-
135
- # model_path
136
- parser.add_argument(
137
- "--model_name",
138
- type=str,
139
- default="image_multilabel_classification_colonoscopy_default",
140
- help="AiModel Name",
141
- )
142
-
143
- #
144
- parser.add_argument(
145
- "--segmentation",
146
- action="store_true",
147
- help="Whether to use segmentation",
148
- )
149
-
150
- parser.add_argument(
151
- "--processor_name",
152
- type=str,
153
- default="olympus_cv_1500",
154
- help="Name of the processor to associate with video",
155
- )
156
-
157
- def handle(self, *args, **options):
158
-
159
- """
160
- Imports a video file into the database, associating it with a specified medical center and endoscopy processor, and optionally applies AI-based segmentation.
161
-
162
- Checks for required dependencies, loads reference data, validates the existence of the specified center and processor, and processes the video file. If segmentation is enabled, retrieves the latest segmentation model metadata. Handles interactive processor selection if multiple are available, creates a new `VideoFile` entry, and invokes the processing pipeline. Can optionally delete the source file or save the video to a specified directory. Reports the outcome of the import and processing steps.
163
- """
164
- try: # ADDED
165
- check_ffmpeg_availability() # ADDED
166
- self.stdout.write(self.style.SUCCESS("FFMPEG is available")) # ADDED
167
- except FileNotFoundError as e: # ADDED
168
- self.stderr.write(self.style.ERROR(str(e))) # ADDED
169
- # Decide if the command should exit or if FFMPEG is optional for some operations
170
- # For this command, it seems FFMPEG is critical for VideoFile.pipe_1 and VideoFile.create_from_file
171
- return # ADDED
172
-
173
- self.stdout.write(f"Current database: {connection.alias}")
174
- self.stdout.write(self.style.SUCCESS("Starting video import..."))
175
-
176
- # Should not be invoked here but in a previous db setup step
177
- # load_gender_data()
178
- # load_disease_data()
179
- # load_event_data()
180
- # load_information_source()
181
- # load_examination_data()
182
- # load_center_data()
183
- # load_endoscope_data()
184
-
185
- segmentation = options["segmentation"]
186
-
187
- verbose = options["verbose"]
188
- center_name = options["center_name"]
189
- video_file = options["video_file"]
190
- frame_dir_root = options["frame_dir_root"]
191
- delete_source = options["delete_source"]
192
- save_video_file = options["save_video_file"]
193
- model_name = options["model_name"]
194
- processor_name = options["processor_name"]
195
- video_file = Path(video_file).expanduser()
196
-
197
-
198
- assert isinstance(delete_source, bool), "delete_source must be a boolean"
199
- assert isinstance(save_video_file, bool), "save_video_file must be a boolean"
200
- assert isinstance(verbose, bool), "verbose must be a boolean"
201
- assert isinstance(center_name, str), "center_name must be a string"
202
- assert isinstance(video_file, Path), "video_file must be a Path"
203
- assert isinstance(frame_dir_root, str), "frame_dir_root must be a string"
204
- # Assert Center exists -> Does not exist methods are deprecated
205
- try:
206
- center = Center.objects.get(name=center_name)
207
- self.stdout.write(self.style.SUCCESS(f"Using center: {center.name}"))
208
- except Center.DoesNotExist:
209
- self.stdout.write(self.style.ERROR(f"Center not found: {center_name}"))
210
- return
211
-
212
- # Assert Processor Exists
213
- if processor_name is None:
214
- processors_qs = center.endoscopy_processors.all()
215
- proc_count = processors_qs.count()
216
- if proc_count == 0:
217
- raise AssertionError(
218
- f"No processors linked to '{center.name}' and no processor called '{processor_name}' exists. "
219
- "Fallback from default Processor applied."
220
- )
221
- elif proc_count == 1:
222
- processor = processors_qs.first()
223
- else:
224
-
225
- processor = self._choose_processor_interactively(processors_qs)
226
- self.stdout.write(self.style.SUCCESS(f"Using processor: {processor.name}"))
227
-
228
- else:
229
- processor = EndoscopyProcessor.objects.get(name=processor_name)
230
-
231
- cns = processor.centers.values_list("name", flat=True)
232
- if center_name not in cns:
233
- self.stdout.write(
234
- self.style.ERROR(
235
- f"Processor '{processor_name}' is not linked to center '{center_name}'."
236
- )
237
- )
238
- return
239
-
240
- if not Path(video_file).exists():
241
- self.stdout.write(self.style.ERROR(f"Video file not found: {video_file} saving unsuccessful."))
242
- return AssertionError(f"Video file not found: {video_file}")
243
-
244
- # Create VideoFile instance first
245
- video_file_obj = VideoFile.create_from_file_initialized(
246
- file_path=video_file,
247
- center_name=center_name,
248
- processor_name=processor_name,
249
- delete_source=delete_source,
250
- save_video_file=save_video_file, # Add this line
251
- )
252
-
253
- if not video_file_obj:
254
- self.stdout.write(self.style.ERROR("Failed to create VideoFile instance"))
255
- return
256
-
257
- self.stdout.write(self.style.SUCCESS(f"Created VideoFile with UUID: {video_file_obj.uuid}"))
258
-
259
- # Initialize video specs and frames
260
- video_file_obj.initialize_video_specs()
261
- video_file_obj.initialize_frames()
262
-
263
- # Run Pipe 1 for OCR and AI processing
264
- self.stdout.write(self.style.SUCCESS("Starting Pipe 1 processing (OCR + AI)..."))
265
- success = video_file_obj.pipe_1(
266
- model_name=model_name,
267
- delete_frames_after=True,
268
- ocr_frame_fraction=0.01,
269
- ocr_cap=5
270
- )
271
-
272
- if not success:
273
- self.stdout.write(self.style.ERROR("Pipe 1 processing failed"))
274
- return
275
-
276
- self.stdout.write(self.style.SUCCESS("Pipe 1 processing completed"))
277
-
278
- # Ensure minimum patient data is available
279
- self.stdout.write(self.style.SUCCESS("Ensuring minimum patient data..."))
280
- self._ensure_default_patient_data(video_file_obj)
281
-
282
- # Frame-level anonymization integration
283
- if FRAME_CLEANING_AVAILABLE and video_file_obj.raw_file:
284
- try:
285
- self.stdout.write(self.style.SUCCESS("Starting frame-level anonymization..."))
286
- # Properly instantiate FrameCleaner and ReportReader with correct arguments
287
- frame_cleaner = FrameCleaner()
288
- report_reader = ReportReader(
289
- report_root_path=str(video_file_obj.raw_file.path),
290
- locale="de_DE", # Default German locale for medical data
291
- text_date_format="%d.%m.%Y" # Common German date format
292
- )
293
-
294
- # Updated to handle new return signature (path, metadata)
295
- cleaned_video_path, extracted_metadata = frame_cleaner.clean_video(
296
- video_path=Path(video_file_obj.raw_file.path),
297
- endoscope_image_roi=video_file_obj.processor.get_roi_endoscope_image() if video_file_obj.processor else None,
298
- endoscope_data_roi_nested=video_file_obj.processor.get_rois() if video_file_obj.processor else None,
299
- output_path=video_file_obj.get_processed_file_path(),
300
- technique="mask_overlay" # Use mask overlay technique as default, if not set this will be inferred.
301
- )
302
-
303
- # Save the cleaned video using Django's FileField
304
- with open(cleaned_video_path, 'rb') as f:
305
- video_file_obj.raw_file.save(cleaned_video_path.name, ContentFile(f.read()))
306
- video_file_obj.save()
307
-
308
- self.stdout.write(self.style.SUCCESS(f"Frame cleaning completed: {cleaned_video_path.name}"))
309
- self.stdout.write(self.style.SUCCESS(f"Extracted metadata: {extracted_metadata}"))
310
-
311
- except Exception as e:
312
- self.stdout.write(self.style.WARNING(f"Frame cleaning failed, continuing with original video: {e}"))
313
- elif not FRAME_CLEANING_AVAILABLE:
314
- self.stdout.write(self.style.WARNING("Frame cleaning not available (lx_anonymizer not found)"))
315
-
316
- # Now call pipe_1 on the VideoFile instance
317
- if segmentation:
318
- success = video_file_obj.pipe_1(model_name=model_name, model_meta_version = None)
319
-
320
- if success:
321
- self.stdout.write(self.style.SUCCESS("Pipeline 1 completed successfully"))
322
- else:
323
- self.stdout.write(self.style.ERROR("Pipeline 1 failed"))
324
-
325
- def _ensure_default_patient_data(self, video_file):
326
- """
327
- Ensure video has minimum required patient data in SensitiveMeta.
328
- Creates default values if data is missing after OCR processing.
329
- """
330
- from endoreg_db.models import SensitiveMeta
331
- from datetime import date
332
-
333
- if not video_file.sensitive_meta:
334
- self.stdout.write(self.style.WARNING(f"No SensitiveMeta found for video {video_file.uuid}, creating default"))
335
-
336
- # Create default SensitiveMeta with placeholder data
337
- default_data = {
338
- "patient_first_name": None,
339
- "patient_last_name": None,
340
- "patient_dob": None,
341
- "examination_date": None,
342
- "center_name": video_file.center.name if video_file.center else "unknown",
343
- }
344
-
345
- try:
346
- sensitive_meta = SensitiveMeta.create_from_dict(default_data)
347
- video_file.sensitive_meta = sensitive_meta
348
- video_file.save(update_fields=['sensitive_meta'])
349
- self.stdout.write(self.style.SUCCESS(f"Created default SensitiveMeta for video {video_file.uuid}"))
350
- except Exception as e:
351
- self.stdout.write(self.style.ERROR(f"Failed to create default SensitiveMeta for video {video_file.uuid}: {e}"))
352
-
353
- else:
354
- # Update existing SensitiveMeta with missing fields
355
- update_needed = False
356
- update_data = {}
357
-
358
- if not video_file.sensitive_meta.patient_first_name:
359
- update_data["patient_first_name"] = "Patient"
360
- update_needed = True
361
-
362
- if not video_file.sensitive_meta.patient_last_name:
363
- update_data["patient_last_name"] = "Unknown"
364
- update_needed = True
365
-
366
- if not video_file.sensitive_meta.patient_dob:
367
- update_data["patient_dob"] = date(1990, 1, 1)
368
- update_needed = True
369
-
370
- if not video_file.sensitive_meta.examination_date:
371
- update_data["examination_date"] = date.today()
372
- update_needed = True
373
-
374
- if update_needed:
375
- try:
376
- video_file.sensitive_meta.update_from_dict(update_data)
377
- self.stdout.write(self.style.SUCCESS(f"Updated missing SensitiveMeta fields for video {video_file.uuid}: {list(update_data.keys())}"))
378
- except Exception as e:
379
- self.stdout.write(self.style.ERROR(f"Failed to update SensitiveMeta for video {video_file.uuid}: {e}"))
380
- else:
381
- self.stdout.write(self.style.SUCCESS(f"SensitiveMeta for video {video_file.uuid} already has all required fields"))
382
-
383
- def _choose_processor_interactively(
384
- self, processors_qs
385
- ) -> EndoscopyProcessor:
386
- """
387
- Interactively prompts the user to select an endoscopy processor from a list.
388
-
389
- Displays all available processors and requests user input until a valid selection is made. Aborts the process if the user interrupts input.
390
-
391
- Args:
392
- processors_qs: Queryset of EndoscopyProcessor objects to present for selection.
393
-
394
- Returns:
395
- The EndoscopyProcessor object chosen by the user.
396
- """
397
- # turn the QS into a concrete list so we can index it later
398
- processors = list(processors_qs) # -> [EndoscopyProcessor, …]
399
-
400
- self.stdout.write(self.style.ERROR(
401
- f"\nThe centre has {len(processors)} endoscopy processors.\n"
402
- "Choose one for this import:\n"
403
- ))
404
- for idx, proc in enumerate(processors, 1):
405
- self.stdout.write(f" [{idx}] {proc.name}")
406
-
407
- while True: # keep prompting until valid
408
- try:
409
- choice = input("Processor number › ").strip()
410
- except (EOFError, KeyboardInterrupt):
411
- self.stderr.write("\nAborted.")
412
- raise SystemExit(1)
413
-
414
- try:
415
- index = int(choice) - 1
416
- if not 0 <= index < len(processors):
417
- raise ValueError
418
- except ValueError:
419
- self.stderr.write("❌ Please enter a number in the list above.\n")
420
- continue
421
-
422
- return processors[index] # ← the chosen object