endoreg-db 0.8.8.9__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 (453) 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 +146 -83
  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/migrations/__init__.py +0 -0
  82. endoreg_db/models/__init__.py +8 -0
  83. endoreg_db/models/administration/ai/active_model.py +5 -5
  84. endoreg_db/models/administration/ai/ai_model.py +41 -18
  85. endoreg_db/models/administration/ai/model_type.py +1 -0
  86. endoreg_db/models/administration/case/case.py +22 -22
  87. endoreg_db/models/administration/center/__init__.py +5 -5
  88. endoreg_db/models/administration/center/center.py +6 -2
  89. endoreg_db/models/administration/center/center_resource.py +18 -4
  90. endoreg_db/models/administration/center/center_shift.py +3 -1
  91. endoreg_db/models/administration/center/center_waste.py +6 -2
  92. endoreg_db/models/administration/person/__init__.py +1 -1
  93. endoreg_db/models/administration/person/employee/__init__.py +1 -1
  94. endoreg_db/models/administration/person/employee/employee_type.py +3 -1
  95. endoreg_db/models/administration/person/examiner/__init__.py +1 -1
  96. endoreg_db/models/administration/person/examiner/examiner.py +10 -2
  97. endoreg_db/models/administration/person/names/first_name.py +6 -4
  98. endoreg_db/models/administration/person/names/last_name.py +4 -3
  99. endoreg_db/models/administration/person/patient/__init__.py +1 -1
  100. endoreg_db/models/administration/person/patient/patient.py +0 -1
  101. endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
  102. endoreg_db/models/administration/person/person.py +1 -1
  103. endoreg_db/models/administration/product/__init__.py +7 -6
  104. endoreg_db/models/administration/product/product.py +6 -2
  105. endoreg_db/models/administration/product/product_group.py +9 -7
  106. endoreg_db/models/administration/product/product_material.py +9 -2
  107. endoreg_db/models/administration/product/reference_product.py +64 -15
  108. endoreg_db/models/administration/qualification/qualification.py +3 -1
  109. endoreg_db/models/administration/shift/shift.py +3 -1
  110. endoreg_db/models/administration/shift/shift_type.py +12 -4
  111. endoreg_db/models/aidataset/__init__.py +5 -0
  112. endoreg_db/models/aidataset/aidataset.py +193 -0
  113. endoreg_db/models/label/__init__.py +1 -1
  114. endoreg_db/models/label/label.py +10 -2
  115. endoreg_db/models/label/label_set.py +3 -1
  116. endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
  117. endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
  118. endoreg_db/models/media/__init__.py +12 -5
  119. endoreg_db/models/media/frame/__init__.py +1 -1
  120. endoreg_db/models/media/frame/frame.py +34 -8
  121. endoreg_db/models/media/pdf/__init__.py +2 -1
  122. endoreg_db/models/media/pdf/raw_pdf.py +11 -4
  123. endoreg_db/models/media/pdf/report_file.py +6 -2
  124. endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
  125. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
  126. endoreg_db/models/media/video/create_from_file.py +20 -41
  127. endoreg_db/models/media/video/pipe_1.py +75 -30
  128. endoreg_db/models/media/video/pipe_2.py +37 -12
  129. endoreg_db/models/media/video/video_file.py +36 -24
  130. endoreg_db/models/media/video/video_file_ai.py +235 -70
  131. endoreg_db/models/media/video/video_file_anonymize.py +240 -65
  132. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
  133. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
  134. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
  135. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
  136. endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
  137. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
  138. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
  139. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
  140. endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
  141. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
  142. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
  143. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
  144. endoreg_db/models/media/video/video_file_io.py +85 -33
  145. endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
  146. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
  147. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
  148. endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
  149. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
  150. endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
  151. endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
  152. endoreg_db/models/media/video/video_file_segments.py +118 -27
  153. endoreg_db/models/media/video/video_metadata.py +25 -6
  154. endoreg_db/models/media/video/video_processing.py +54 -15
  155. endoreg_db/models/medical/__init__.py +3 -13
  156. endoreg_db/models/medical/contraindication/__init__.py +3 -1
  157. endoreg_db/models/medical/disease.py +18 -6
  158. endoreg_db/models/medical/event.py +6 -2
  159. endoreg_db/models/medical/examination/__init__.py +5 -1
  160. endoreg_db/models/medical/examination/examination.py +22 -6
  161. endoreg_db/models/medical/examination/examination_indication.py +23 -7
  162. endoreg_db/models/medical/examination/examination_time.py +6 -2
  163. endoreg_db/models/medical/finding/__init__.py +3 -1
  164. endoreg_db/models/medical/finding/finding.py +37 -12
  165. endoreg_db/models/medical/finding/finding_classification.py +27 -8
  166. endoreg_db/models/medical/finding/finding_intervention.py +19 -6
  167. endoreg_db/models/medical/finding/finding_type.py +3 -1
  168. endoreg_db/models/medical/hardware/__init__.py +1 -1
  169. endoreg_db/models/medical/hardware/endoscope.py +14 -2
  170. endoreg_db/models/medical/laboratory/__init__.py +1 -1
  171. endoreg_db/models/medical/laboratory/lab_value.py +139 -39
  172. endoreg_db/models/medical/medication/__init__.py +7 -3
  173. endoreg_db/models/medical/medication/medication.py +3 -1
  174. endoreg_db/models/medical/medication/medication_indication.py +3 -1
  175. endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
  176. endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
  177. endoreg_db/models/medical/medication/medication_schedule.py +3 -1
  178. endoreg_db/models/medical/patient/__init__.py +2 -10
  179. endoreg_db/models/medical/patient/medication_examples.py +3 -14
  180. endoreg_db/models/medical/patient/patient_disease.py +17 -5
  181. endoreg_db/models/medical/patient/patient_event.py +12 -4
  182. endoreg_db/models/medical/patient/patient_examination.py +52 -15
  183. endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
  184. endoreg_db/models/medical/patient/patient_finding.py +105 -29
  185. endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
  186. endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
  187. endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
  188. endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
  189. endoreg_db/models/medical/patient/patient_medication.py +25 -7
  190. endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
  191. endoreg_db/models/metadata/model_meta.py +40 -12
  192. endoreg_db/models/metadata/model_meta_logic.py +51 -16
  193. endoreg_db/models/metadata/sensitive_meta.py +65 -28
  194. endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
  195. endoreg_db/models/metadata/video_meta.py +146 -39
  196. endoreg_db/models/metadata/video_prediction_logic.py +70 -21
  197. endoreg_db/models/metadata/video_prediction_meta.py +80 -27
  198. endoreg_db/models/operation_log.py +63 -0
  199. endoreg_db/models/other/__init__.py +10 -10
  200. endoreg_db/models/other/distribution/__init__.py +9 -7
  201. endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
  202. endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
  203. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
  204. endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
  205. endoreg_db/models/other/emission/__init__.py +1 -1
  206. endoreg_db/models/other/emission/emission_factor.py +9 -3
  207. endoreg_db/models/other/information_source.py +15 -5
  208. endoreg_db/models/other/material.py +3 -1
  209. endoreg_db/models/other/transport_route.py +3 -1
  210. endoreg_db/models/other/unit.py +6 -2
  211. endoreg_db/models/report/report.py +0 -1
  212. endoreg_db/models/requirement/requirement.py +84 -27
  213. endoreg_db/models/requirement/requirement_error.py +5 -6
  214. endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
  215. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
  216. endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
  217. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
  218. endoreg_db/models/requirement/requirement_operator.py +28 -8
  219. endoreg_db/models/requirement/requirement_set.py +34 -11
  220. endoreg_db/models/state/__init__.py +1 -0
  221. endoreg_db/models/state/audit_ledger.py +9 -2
  222. endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
  223. endoreg_db/models/state/processing_history/processing_history.py +136 -0
  224. endoreg_db/models/state/raw_pdf.py +0 -1
  225. endoreg_db/models/state/video.py +2 -3
  226. endoreg_db/models/utils.py +4 -2
  227. endoreg_db/queries/__init__.py +2 -6
  228. endoreg_db/queries/annotations/__init__.py +1 -3
  229. endoreg_db/queries/annotations/legacy.py +37 -26
  230. endoreg_db/root_urls.py +3 -4
  231. endoreg_db/schemas/examination_evaluation.py +3 -0
  232. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
  233. endoreg_db/serializers/__init__.py +2 -8
  234. endoreg_db/serializers/administration/__init__.py +1 -2
  235. endoreg_db/serializers/administration/ai/__init__.py +0 -1
  236. endoreg_db/serializers/administration/ai/active_model.py +3 -1
  237. endoreg_db/serializers/administration/ai/ai_model.py +5 -3
  238. endoreg_db/serializers/administration/ai/model_type.py +3 -1
  239. endoreg_db/serializers/administration/center.py +7 -2
  240. endoreg_db/serializers/administration/gender.py +4 -2
  241. endoreg_db/serializers/anonymization.py +13 -13
  242. endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
  243. endoreg_db/serializers/examination/__init__.py +1 -1
  244. endoreg_db/serializers/examination/base.py +12 -13
  245. endoreg_db/serializers/examination/dropdown.py +6 -7
  246. endoreg_db/serializers/examination_serializer.py +3 -6
  247. endoreg_db/serializers/finding/__init__.py +1 -1
  248. endoreg_db/serializers/finding/finding.py +14 -7
  249. endoreg_db/serializers/finding_classification/__init__.py +3 -3
  250. endoreg_db/serializers/finding_classification/choice.py +3 -3
  251. endoreg_db/serializers/finding_classification/classification.py +2 -4
  252. endoreg_db/serializers/label_video_segment/__init__.py +5 -3
  253. endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
  254. endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
  255. endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
  256. endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
  257. endoreg_db/serializers/meta/__init__.py +1 -2
  258. endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
  259. endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
  260. endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
  261. endoreg_db/serializers/misc/__init__.py +2 -2
  262. endoreg_db/serializers/misc/file_overview.py +11 -7
  263. endoreg_db/serializers/misc/stats.py +10 -8
  264. endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
  265. endoreg_db/serializers/misc/upload_job.py +32 -29
  266. endoreg_db/serializers/patient/__init__.py +2 -1
  267. endoreg_db/serializers/patient/patient.py +32 -15
  268. endoreg_db/serializers/patient/patient_dropdown.py +11 -3
  269. endoreg_db/serializers/patient_examination/__init__.py +1 -1
  270. endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
  271. endoreg_db/serializers/patient_finding/__init__.py +1 -1
  272. endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
  273. endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
  274. endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
  275. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
  276. endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
  277. endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
  278. endoreg_db/serializers/pdf/__init__.py +1 -3
  279. endoreg_db/serializers/requirements/requirement_schema.py +1 -6
  280. endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
  281. endoreg_db/serializers/video/__init__.py +2 -2
  282. endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
  283. endoreg_db/serializers/video/video_file_brief.py +6 -2
  284. endoreg_db/serializers/video/video_file_detail.py +36 -23
  285. endoreg_db/serializers/video/video_file_list.py +4 -2
  286. endoreg_db/serializers/video/video_processing_history.py +54 -50
  287. endoreg_db/services/__init__.py +1 -1
  288. endoreg_db/services/anonymization.py +2 -2
  289. endoreg_db/services/examination_evaluation.py +40 -17
  290. endoreg_db/services/model_meta_from_hf.py +76 -0
  291. endoreg_db/services/polling_coordinator.py +101 -70
  292. endoreg_db/services/pseudonym_service.py +27 -22
  293. endoreg_db/services/report_import.py +6 -3
  294. endoreg_db/services/segment_sync.py +75 -59
  295. endoreg_db/services/video_import.py +6 -7
  296. endoreg_db/urls/__init__.py +2 -2
  297. endoreg_db/urls/ai.py +7 -25
  298. endoreg_db/urls/anonymization.py +61 -15
  299. endoreg_db/urls/auth.py +4 -4
  300. endoreg_db/urls/classification.py +4 -9
  301. endoreg_db/urls/examination.py +27 -18
  302. endoreg_db/urls/media.py +27 -34
  303. endoreg_db/urls/patient.py +11 -7
  304. endoreg_db/urls/requirements.py +3 -1
  305. endoreg_db/urls/root_urls.py +2 -3
  306. endoreg_db/urls/stats.py +24 -16
  307. endoreg_db/urls/upload.py +3 -11
  308. endoreg_db/utils/__init__.py +14 -15
  309. endoreg_db/utils/ai/__init__.py +1 -1
  310. endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
  311. endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
  312. endoreg_db/utils/ai/get.py +2 -1
  313. endoreg_db/utils/ai/inference_dataset.py +14 -15
  314. endoreg_db/utils/ai/model_training/config.py +117 -0
  315. endoreg_db/utils/ai/model_training/dataset.py +74 -0
  316. endoreg_db/utils/ai/model_training/losses.py +68 -0
  317. endoreg_db/utils/ai/model_training/metrics.py +78 -0
  318. endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
  319. endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
  320. endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
  321. endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
  322. endoreg_db/utils/ai/predict.py +4 -4
  323. endoreg_db/utils/ai/preprocess.py +19 -11
  324. endoreg_db/utils/calc_duration_seconds.py +4 -4
  325. endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
  326. endoreg_db/utils/check_video_files.py +74 -47
  327. endoreg_db/utils/cropping.py +10 -9
  328. endoreg_db/utils/dataloader.py +11 -3
  329. endoreg_db/utils/dates.py +3 -4
  330. endoreg_db/utils/defaults/set_default_center.py +7 -6
  331. endoreg_db/utils/env.py +6 -2
  332. endoreg_db/utils/extract_specific_frames.py +24 -9
  333. endoreg_db/utils/file_operations.py +30 -18
  334. endoreg_db/utils/fix_video_path_direct.py +57 -41
  335. endoreg_db/utils/frame_anonymization_utils.py +157 -157
  336. endoreg_db/utils/hashs.py +3 -18
  337. endoreg_db/utils/links/requirement_link.py +96 -52
  338. endoreg_db/utils/ocr.py +30 -25
  339. endoreg_db/utils/operation_log.py +61 -0
  340. endoreg_db/utils/parse_and_generate_yaml.py +12 -13
  341. endoreg_db/utils/paths.py +6 -6
  342. endoreg_db/utils/permissions.py +40 -24
  343. endoreg_db/utils/pipelines/process_video_dir.py +50 -26
  344. endoreg_db/utils/product/sum_emissions.py +5 -3
  345. endoreg_db/utils/product/sum_weights.py +4 -2
  346. endoreg_db/utils/pydantic_models/__init__.py +3 -4
  347. endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
  348. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
  349. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
  350. endoreg_db/utils/setup_config.py +21 -5
  351. endoreg_db/utils/storage.py +3 -1
  352. endoreg_db/utils/translation.py +19 -15
  353. endoreg_db/utils/uuid.py +1 -0
  354. endoreg_db/utils/validate_endo_roi.py +12 -4
  355. endoreg_db/utils/validate_subcategory_dict.py +26 -24
  356. endoreg_db/utils/validate_video_detailed.py +207 -149
  357. endoreg_db/utils/video/__init__.py +7 -3
  358. endoreg_db/utils/video/extract_frames.py +30 -18
  359. endoreg_db/utils/video/ffmpeg_wrapper.py +217 -52
  360. endoreg_db/utils/video/names.py +11 -6
  361. endoreg_db/utils/video/streaming_processor.py +175 -101
  362. endoreg_db/utils/video/video_splitter.py +30 -19
  363. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
  364. endoreg_db/views/__init__.py +0 -20
  365. endoreg_db/views/anonymization/__init__.py +6 -2
  366. endoreg_db/views/anonymization/media_management.py +2 -6
  367. endoreg_db/views/anonymization/overview.py +34 -1
  368. endoreg_db/views/anonymization/validate.py +79 -18
  369. endoreg_db/views/auth/__init__.py +1 -1
  370. endoreg_db/views/auth/keycloak.py +16 -14
  371. endoreg_db/views/examination/__init__.py +12 -15
  372. endoreg_db/views/examination/examination.py +5 -5
  373. endoreg_db/views/examination/examination_manifest_cache.py +5 -5
  374. endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
  375. endoreg_db/views/examination/get_finding_classifications.py +9 -7
  376. endoreg_db/views/examination/get_findings.py +8 -10
  377. endoreg_db/views/examination/get_instruments.py +3 -2
  378. endoreg_db/views/examination/get_interventions.py +1 -1
  379. endoreg_db/views/finding/__init__.py +2 -2
  380. endoreg_db/views/finding/finding.py +58 -54
  381. endoreg_db/views/finding/get_classifications.py +1 -1
  382. endoreg_db/views/finding/get_interventions.py +1 -1
  383. endoreg_db/views/finding_classification/__init__.py +5 -5
  384. endoreg_db/views/finding_classification/finding_classification.py +5 -6
  385. endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
  386. endoreg_db/views/media/__init__.py +13 -13
  387. endoreg_db/views/media/pdf_media.py +9 -9
  388. endoreg_db/views/media/sensitive_metadata.py +10 -7
  389. endoreg_db/views/media/video_media.py +4 -4
  390. endoreg_db/views/meta/__init__.py +1 -1
  391. endoreg_db/views/meta/sensitive_meta_list.py +20 -22
  392. endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
  393. endoreg_db/views/misc/__init__.py +6 -34
  394. endoreg_db/views/misc/center.py +2 -1
  395. endoreg_db/views/misc/csrf.py +2 -1
  396. endoreg_db/views/misc/gender.py +2 -1
  397. endoreg_db/views/misc/stats.py +141 -106
  398. endoreg_db/views/patient/__init__.py +1 -3
  399. endoreg_db/views/patient/patient.py +141 -99
  400. endoreg_db/views/patient_examination/__init__.py +5 -5
  401. endoreg_db/views/patient_examination/patient_examination.py +43 -42
  402. endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
  403. endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
  404. endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
  405. endoreg_db/views/patient_examination/video.py +114 -80
  406. endoreg_db/views/patient_finding/__init__.py +1 -1
  407. endoreg_db/views/patient_finding/patient_finding.py +17 -10
  408. endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
  409. endoreg_db/views/patient_finding_classification/__init__.py +1 -1
  410. endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
  411. endoreg_db/views/report/reimport.py +1 -1
  412. endoreg_db/views/report/report_stream.py +5 -8
  413. endoreg_db/views/requirement/__init__.py +2 -1
  414. endoreg_db/views/requirement/evaluate.py +7 -9
  415. endoreg_db/views/requirement/lookup.py +2 -3
  416. endoreg_db/views/requirement/lookup_store.py +0 -1
  417. endoreg_db/views/requirement/requirement_utils.py +2 -4
  418. endoreg_db/views/stats/__init__.py +4 -4
  419. endoreg_db/views/stats/stats_views.py +152 -115
  420. endoreg_db/views/video/__init__.py +18 -27
  421. endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
  422. endoreg_db/views/{ai → video/ai}/label.py +20 -16
  423. endoreg_db/views/video/correction.py +5 -6
  424. endoreg_db/views/video/reimport.py +134 -99
  425. endoreg_db/views/video/segments_crud.py +134 -44
  426. endoreg_db/views/video/video_apply_mask.py +13 -12
  427. endoreg_db/views/video/video_correction.py +2 -1
  428. endoreg_db/views/video/video_download_processed.py +15 -15
  429. endoreg_db/views/video/video_meta_stats.py +7 -6
  430. endoreg_db/views/video/video_processing_history.py +3 -2
  431. endoreg_db/views/video/video_remove_frames.py +13 -12
  432. endoreg_db/views/video/video_stream.py +110 -82
  433. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
  434. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +436 -433
  435. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +0 -119
  436. endoreg_db/management/commands/import_fallback_video.py +0 -203
  437. endoreg_db/management/commands/import_video.py +0 -422
  438. endoreg_db/management/commands/import_video_with_classification.py +0 -367
  439. endoreg_db/models/media/processing_history/processing_history.py +0 -96
  440. endoreg_db/serializers/label/__init__.py +0 -7
  441. endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
  442. endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
  443. endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
  444. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
  445. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
  446. endoreg_db/services/__old/pdf_import.py +0 -1487
  447. endoreg_db/services/__old/video_import.py +0 -1306
  448. endoreg_db/tasks/upload_tasks.py +0 -216
  449. endoreg_db/tasks/video_ingest.py +0 -161
  450. endoreg_db/tasks/video_processing_tasks.py +0 -327
  451. endoreg_db/views/misc/translation.py +0 -182
  452. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
  453. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
@@ -122,7 +122,9 @@ def _infer_output_classes(weights_path: Path) -> Optional[int]:
122
122
  return None
123
123
 
124
124
 
125
- def _build_label_mapping(source_labels: List[str], target_labels: List[str]) -> Dict[str, List[str]]:
125
+ def _build_label_mapping(
126
+ source_labels: List[str], target_labels: List[str]
127
+ ) -> Dict[str, List[str]]:
126
128
  if source_labels == target_labels:
127
129
  return {label: [label] for label in target_labels}
128
130
 
@@ -142,7 +144,9 @@ def _build_label_mapping(source_labels: List[str], target_labels: List[str]) ->
142
144
  return mapping
143
145
 
144
146
 
145
- def _remap_prediction_dict(predictions: Dict[str, Any], mapping: Dict[str, List[str]]) -> Dict[str, Any]:
147
+ def _remap_prediction_dict(
148
+ predictions: Dict[str, Any], mapping: Dict[str, List[str]]
149
+ ) -> Dict[str, Any]:
146
150
  remapped: Dict[str, Any] = {}
147
151
  for target, sources in mapping.items():
148
152
  values: List[Any] = []
@@ -167,7 +171,9 @@ def _remap_prediction_dict(predictions: Dict[str, Any], mapping: Dict[str, List[
167
171
  return remapped
168
172
 
169
173
 
170
- def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float = 0.001, cap: int = 15) -> Optional[Dict[str, str]]:
174
+ def _extract_text_from_video_frames(
175
+ video: "VideoFile", frame_fraction: float = 0.001, cap: int = 15
176
+ ) -> Optional[Dict[str, str]]:
171
177
  """
172
178
  Extracts text from a sample of video frames using OCR based on processor ROIs.
173
179
  Requires frames to be extracted. Raises ValueError on pre-condition failure.
@@ -178,31 +184,47 @@ def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float =
178
184
  - Post-condition: No state changes.
179
185
  """
180
186
  from endoreg_db.utils.ocr import (
181
- extract_text_from_rois,
182
- ) # Local import for dependency isolation
187
+ extract_text_from_rois, # Local import for dependency isolation
188
+ )
183
189
 
184
190
  state = video.get_or_create_state() # Use State helper
185
191
  # --- Pre-condition Check ---
186
192
  if not state.frames_extracted:
187
193
  # Raise exception
188
- raise ValueError(f"Frames not extracted for video {video.uuid}. Cannot extract text.")
194
+ raise ValueError(
195
+ f"Frames not extracted for video {video.video_hash}. Cannot extract text."
196
+ )
189
197
  # --- End Pre-condition Check ---
190
198
 
191
199
  processor: Optional["EndoscopyProcessor"] = video.processor
192
200
  if not processor:
193
201
  # Raise exception
194
- raise ValueError(f"Processor not set for video {video.uuid}. Cannot extract text.")
202
+ raise ValueError(
203
+ f"Processor not set for video {video.video_hash}. Cannot extract text."
204
+ )
195
205
 
196
206
  try:
197
207
  frame_paths = video.get_frame_paths() # Use Frame helper
198
208
  except Exception as e:
199
- logger.error("Error getting frame paths for video %s: %s", video.uuid, e, exc_info=True)
200
- raise RuntimeError(f"Could not get frame paths for video {video.uuid}") from e
209
+ logger.error(
210
+ "Error getting frame paths for video %s: %s",
211
+ video.video_hash,
212
+ e,
213
+ exc_info=True,
214
+ )
215
+ raise RuntimeError(
216
+ f"Could not get frame paths for video {video.video_hash}"
217
+ ) from e
201
218
 
202
219
  n_frames = len(frame_paths)
203
220
  if n_frames == 0:
204
- logger.warning("No frame paths found for video %s during text extraction.", video.uuid)
205
- return None # Return None if no frames, not an error condition for this function
221
+ logger.warning(
222
+ "No frame paths found for video %s during text extraction.",
223
+ video.video_hash,
224
+ )
225
+ return (
226
+ None # Return None if no frames, not an error condition for this function
227
+ )
206
228
 
207
229
  # Determine number of frames to process
208
230
  n_frames_to_process = max(1, int(frame_fraction * n_frames))
@@ -212,7 +234,7 @@ def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float =
212
234
  "Processing %d frames (out of %d) for text extraction from video %s.",
213
235
  n_frames_to_process,
214
236
  n_frames,
215
- video.uuid,
237
+ video.video_hash,
216
238
  )
217
239
 
218
240
  # Select evenly spaced frames
@@ -230,7 +252,13 @@ def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float =
230
252
  rois_texts[roi].append(text)
231
253
  except Exception as e:
232
254
  # Log error but continue processing other frames
233
- logger.error("Error extracting text from frame %s for video %s: %s", frame_path, video.uuid, e, exc_info=True)
255
+ logger.error(
256
+ "Error extracting text from frame %s for video %s: %s",
257
+ frame_path,
258
+ video.video_hash,
259
+ e,
260
+ exc_info=True,
261
+ )
234
262
  errors_encountered = True # Flag that an error occurred
235
263
 
236
264
  # Determine the most frequent text for each ROI
@@ -247,17 +275,24 @@ def _extract_text_from_video_frames(video: "VideoFile", frame_fraction: float =
247
275
  else:
248
276
  most_frequent_texts[roi] = None
249
277
  except Exception as e:
250
- logger.error("Error finding most common text for ROI %s: %s", roi, e, exc_info=True)
278
+ logger.error(
279
+ "Error finding most common text for ROI %s: %s", roi, e, exc_info=True
280
+ )
251
281
  most_frequent_texts[roi] = None
252
282
 
253
283
  if errors_encountered:
254
- logger.warning("Errors occurred during text extraction for some frames of video %s. Results may be incomplete.", video.uuid)
284
+ logger.warning(
285
+ "Errors occurred during text extraction for some frames of video %s. Results may be incomplete.",
286
+ video.video_hash,
287
+ )
255
288
 
256
289
  if not most_frequent_texts:
257
- logger.info("No text extracted for any ROI for video %s.", video.uuid)
290
+ logger.info("No text extracted for any ROI for video %s.", video.video_hash)
258
291
  return None # Return None if no text found
259
292
 
260
- logger.info("Extracted text for video %s: %s", video.uuid, most_frequent_texts)
293
+ logger.info(
294
+ "Extracted text for video %s: %s", video.video_hash, most_frequent_texts
295
+ )
261
296
  return most_frequent_texts
262
297
 
263
298
 
@@ -282,16 +317,26 @@ def _predict_video_pipeline(
282
317
  from ...administration.ai import AiModel
283
318
 
284
319
  try:
285
- from ....utils.ai import Classifier, InferenceDataset, MultiLabelClassificationNet
320
+ from ....utils.ai import (
321
+ Classifier,
322
+ InferenceDataset,
323
+ MultiLabelClassificationNet,
324
+ )
286
325
  from ....utils.ai.postprocess import (
287
326
  concat_pred_dicts,
288
327
  find_true_pred_sequences,
289
328
  make_smooth_preds,
290
329
  )
291
330
  except ImportError as e:
292
- logger.error("Failed to import endo_ai components: %s. Prediction unavailable.", e, exc_info=True)
331
+ logger.error(
332
+ "Failed to import endo_ai components: %s. Prediction unavailable.",
333
+ e,
334
+ exc_info=True,
335
+ )
293
336
  # Raise exception
294
- raise ImportError("Failed to import required AI components for prediction.") from e
337
+ raise ImportError(
338
+ "Failed to import required AI components for prediction."
339
+ ) from e
295
340
 
296
341
  if not test_run and GLOBAL_TEST_RUN:
297
342
  test_run = True
@@ -302,48 +347,72 @@ def _predict_video_pipeline(
302
347
  # --- Pre-condition Check ---
303
348
  if not state.frames_extracted:
304
349
  # Raise exception
305
- raise ValueError(f"Frames not extracted for video {video.uuid}. Prediction aborted.")
350
+ raise ValueError(
351
+ f"Frames not extracted for video {video.video_hash}. Prediction aborted."
352
+ )
306
353
  # --- End Pre-condition Check ---
307
354
 
308
355
  # Frame directory check
309
356
  frame_dir = video.get_frame_dir_path() # Use IO helper
310
357
  if not frame_dir or not frame_dir.exists() or not any(frame_dir.iterdir()):
311
358
  # Raise exception
312
- raise FileNotFoundError(f"Frame directory {frame_dir} is empty or does not exist for video {video.uuid}. Prediction aborted.")
359
+ raise FileNotFoundError(
360
+ f"Frame directory {frame_dir} is empty or does not exist for video {video.video_hash}. Prediction aborted."
361
+ )
313
362
 
314
363
  model: Optional[AiModel] = model_meta.model
315
364
  if not model:
316
365
  # Raise exception
317
- raise ValueError(f"Model not found in ModelMeta {model_meta.name} (Version: {model_meta.version}) for video {video.uuid}. Prediction aborted.")
366
+ raise ValueError(
367
+ f"Model not found in ModelMeta {model_meta.name} (Version: {model_meta.version}) for video {video.video_hash}. Prediction aborted."
368
+ )
318
369
 
319
370
  # Ensure weights file exists
320
371
  try:
321
372
  weights_path = Path(model_meta.weights.path)
322
373
  if not weights_path.exists():
323
374
  # Raise exception
324
- raise FileNotFoundError(f"Model weights file {weights_path} not found for {model_meta.name} (Video: {video.uuid}). Prediction aborted.")
375
+ raise FileNotFoundError(
376
+ f"Model weights file {weights_path} not found for {model_meta.name} (Video: {video.video_hash}). Prediction aborted."
377
+ )
325
378
  except Exception as e:
326
- logger.error("Error accessing model weights path for %s (Video: %s): %s", model_meta.name, video.uuid, e, exc_info=True)
327
- raise RuntimeError(f"Error accessing model weights for {model_meta.name}") from e
379
+ logger.error(
380
+ "Error accessing model weights path for %s (Video: %s): %s",
381
+ model_meta.name,
382
+ video.video_hash,
383
+ e,
384
+ exc_info=True,
385
+ )
386
+ raise RuntimeError(
387
+ f"Error accessing model weights for {model_meta.name}"
388
+ ) from e
328
389
 
329
390
  # Get or create VideoPredictionMeta
330
391
  try:
331
- _video_prediction_meta, created = VideoPredictionMeta.objects.get_or_create(video_file=video, model_meta=model_meta)
392
+ _video_prediction_meta, created = VideoPredictionMeta.objects.get_or_create(
393
+ video_file=video, model_meta=model_meta
394
+ )
332
395
  if created:
333
396
  logger.info(
334
397
  "Created new VideoPredictionMeta for video %s, model %s.",
335
- video.uuid,
398
+ video.video_hash,
336
399
  model_meta.name,
337
400
  )
338
401
  else:
339
402
  logger.info(
340
403
  "Found existing VideoPredictionMeta for video %s, model %s.",
341
- video.uuid,
404
+ video.video_hash,
342
405
  model_meta.name,
343
406
  )
344
407
  # video_prediction_meta.save() # Save is handled by get_or_create
345
408
  except Exception as e:
346
- logger.error("Failed to get or create VideoPredictionMeta for video %s, model %s: %s", video.uuid, model_meta.name, e, exc_info=True)
409
+ logger.error(
410
+ "Failed to get or create VideoPredictionMeta for video %s, model %s: %s",
411
+ video.video_hash,
412
+ model_meta.name,
413
+ e,
414
+ exc_info=True,
415
+ )
347
416
  # Raise exception
348
417
  raise RuntimeError("Failed to get or create VideoPredictionMeta") from e
349
418
 
@@ -351,7 +420,7 @@ def _predict_video_pipeline(
351
420
  logger.info(
352
421
  "Detected stub weights at %s for video %s; skipping model inference and returning empty predictions.",
353
422
  weights_path,
354
- video.uuid,
423
+ video.video_hash,
355
424
  )
356
425
  return {}
357
426
 
@@ -363,33 +432,56 @@ def _predict_video_pipeline(
363
432
  dataset_model_class = datasets.get(dataset_name)
364
433
  if not dataset_model_class:
365
434
  # Raise exception
366
- raise ValueError(f"Dataset class '{dataset_name}' not found for video {video.uuid}. Prediction aborted.")
435
+ raise ValueError(
436
+ f"Dataset class '{dataset_name}' not found for video {video.video_hash}. Prediction aborted."
437
+ )
367
438
 
368
439
  try:
369
440
  paths = video.get_frame_paths() # Use Frame helper
370
441
  if not paths:
371
- raise FileNotFoundError(f"No frame paths returned by get_frame_paths for {frame_dir} (Video: {video.uuid})")
442
+ raise FileNotFoundError(
443
+ f"No frame paths returned by get_frame_paths for {frame_dir} (Video: {video.video_hash})"
444
+ )
372
445
  except Exception as e:
373
- logger.error("Error listing or getting frame files from %s for video %s: %s", frame_dir, video.uuid, e, exc_info=True)
446
+ logger.error(
447
+ "Error listing or getting frame files from %s for video %s: %s",
448
+ frame_dir,
449
+ video.video_hash,
450
+ e,
451
+ exc_info=True,
452
+ )
374
453
  raise RuntimeError(f"Error getting frame paths from {frame_dir}") from e
375
454
 
376
- logger.info("Found %d frame files in %s for video %s.", len(paths), frame_dir, video.uuid)
455
+ logger.info(
456
+ "Found %d frame files in %s for video %s.",
457
+ len(paths),
458
+ frame_dir,
459
+ video.video_hash,
460
+ )
377
461
 
378
462
  crop_template = video.get_crop_template() # Use Meta helper
379
463
  string_paths = [p.as_posix() for p in paths]
380
464
  crops = [crop_template] * len(paths) # Assuming same crop for all frames
381
465
 
382
466
  if test_run:
383
- logger.info("TEST RUN: Using first %d frames for video %s.", n_test_frames, video.uuid)
467
+ logger.info(
468
+ "TEST RUN: Using first %d frames for video %s.",
469
+ n_test_frames,
470
+ video.video_hash,
471
+ )
384
472
  string_paths = string_paths[:n_test_frames]
385
473
  crops = crops[:n_test_frames]
386
474
  if not string_paths:
387
475
  # Raise exception
388
- raise ValueError(f"Not enough frames ({len(paths)}) for test run (required {n_test_frames}) for video {video.uuid}.")
476
+ raise ValueError(
477
+ f"Not enough frames ({len(paths)}) for test run (required {n_test_frames}) for video {video.video_hash}."
478
+ )
389
479
 
390
480
  label_names = _resolve_label_names(model_meta)
391
481
  if not label_names:
392
- raise ValueError(f"Label set '{getattr(model_meta.labelset, 'name', 'unknown')}' has no labels configured.")
482
+ raise ValueError(
483
+ f"Label set '{getattr(model_meta.labelset, 'name', 'unknown')}' has no labels configured."
484
+ )
393
485
 
394
486
  outputs_hint = _infer_output_classes(weights_path)
395
487
 
@@ -428,7 +520,12 @@ def _predict_video_pipeline(
428
520
  try:
429
521
  ds_config = model_meta.get_inference_dataset_config()
430
522
  ds = dataset_model_class(string_paths, crops, config=ds_config)
431
- logger.info("Created dataset '%s' with %d items for video %s.", dataset_name, len(ds), video.uuid)
523
+ logger.info(
524
+ "Created dataset '%s' with %d items for video %s.",
525
+ dataset_name,
526
+ len(ds),
527
+ video.video_hash,
528
+ )
432
529
  if len(ds) > 0:
433
530
  sample = ds[0] # Get a sample for debugging shape
434
531
  logger.debug("Sample shape: %s", getattr(sample, "shape", None))
@@ -451,7 +548,13 @@ def _predict_video_pipeline(
451
548
  "labels": network_labels,
452
549
  }
453
550
  except Exception as e:
454
- logger.error("Failed to create dataset '%s' for video %s: %s", dataset_name, video.uuid, e, exc_info=True)
551
+ logger.error(
552
+ "Failed to create dataset '%s' for video %s: %s",
553
+ dataset_name,
554
+ video.video_hash,
555
+ e,
556
+ exc_info=True,
557
+ )
455
558
  # Raise exception
456
559
  raise RuntimeError(f"Failed to create dataset '{dataset_name}'") from e
457
560
 
@@ -469,11 +572,11 @@ def _predict_video_pipeline(
469
572
  **load_kwargs,
470
573
  )
471
574
  ai_model_instance = ai_model_instance.to(device)
472
- logger.info("Loaded model on GPU for video %s.", video.uuid)
575
+ logger.info("Loaded model on GPU for video %s.", video.video_hash)
473
576
  except RuntimeError as cuda_err:
474
577
  logger.warning(
475
578
  "GPU loading failed for video %s: %s. Falling back to CPU.",
476
- video.uuid,
579
+ video.video_hash,
477
580
  cuda_err,
478
581
  )
479
582
  device = torch.device("cpu")
@@ -483,10 +586,13 @@ def _predict_video_pipeline(
483
586
  **load_kwargs,
484
587
  )
485
588
  ai_model_instance = ai_model_instance.to(device)
486
- logger.info("Loaded model on CPU for video %s.", video.uuid)
589
+ logger.info("Loaded model on CPU for video %s.", video.video_hash)
487
590
  else:
488
591
  # No CUDA available, load directly on CPU
489
- logger.info("CUDA not available. Loading model on CPU for video %s.", video.uuid)
592
+ logger.info(
593
+ "CUDA not available. Loading model on CPU for video %s.",
594
+ video.video_hash,
595
+ )
490
596
  device = torch.device("cpu")
491
597
  ai_model_instance = MultiLabelClassificationNet.load_from_checkpoint(
492
598
  checkpoint_path=weights_path.as_posix(),
@@ -496,28 +602,49 @@ def _predict_video_pipeline(
496
602
  ai_model_instance = ai_model_instance.to(device)
497
603
 
498
604
  _ = ai_model_instance.eval() # Set to evaluation mode
499
- classifier = Classifier(ai_model_instance, config=classifier_config or {}, verbose=True)
500
- logger.info("AI model loaded successfully for video %s from %s.", video.uuid, weights_path)
605
+ classifier = Classifier(
606
+ ai_model_instance, config=classifier_config or {}, verbose=True
607
+ )
608
+ logger.info(
609
+ "AI model loaded successfully for video %s from %s.",
610
+ video.video_hash,
611
+ weights_path,
612
+ )
501
613
  except Exception as e:
502
- logger.error("Failed to load AI model for video %s from %s: %s", video.uuid, weights_path, e, exc_info=True)
614
+ logger.error(
615
+ "Failed to load AI model for video %s from %s: %s",
616
+ video.video_hash,
617
+ weights_path,
618
+ e,
619
+ exc_info=True,
620
+ )
503
621
  # Raise exception
504
622
  raise RuntimeError(f"Failed to load AI model from {weights_path}") from e
505
623
 
506
624
  # --- Inference ---
507
625
  try:
508
- logger.info("Starting inference on %d frames for video %s...", len(string_paths), video.uuid)
626
+ logger.info(
627
+ "Starting inference on %d frames for video %s...",
628
+ len(string_paths),
629
+ video.video_hash,
630
+ )
509
631
  predictions = classifier.pipe(string_paths, crops)
510
- logger.info("Inference completed for video %s.", video.uuid)
632
+ logger.info("Inference completed for video %s.", video.video_hash)
511
633
  except Exception as e:
512
- logger.error("Inference failed for video %s: %s", video.uuid, e, exc_info=True)
634
+ logger.error(
635
+ "Inference failed for video %s: %s", video.video_hash, e, exc_info=True
636
+ )
513
637
  # CUDA-OOM Fallback: Speicher freigeben und CPU versuchen
514
638
  try:
515
639
  import gc
516
640
 
517
641
  import torch
518
642
 
519
- is_oom = isinstance(e, (getattr(torch.cuda, "OutOfMemoryError", RuntimeError), RuntimeError)) and (
520
- "out of memory" in str(e).lower() or "cuda out of memory" in str(e).lower()
643
+ is_oom = isinstance(
644
+ e, (getattr(torch.cuda, "OutOfMemoryError", RuntimeError), RuntimeError)
645
+ ) and (
646
+ "out of memory" in str(e).lower()
647
+ or "cuda out of memory" in str(e).lower()
521
648
  )
522
649
  except Exception:
523
650
  is_oom = False
@@ -526,7 +653,9 @@ def _predict_video_pipeline(
526
653
  import torch # ensure available in this scope
527
654
 
528
655
  if torch.cuda.is_available() and is_oom:
529
- logger.warning("CUDA OOM detected. Freeing CUDA cache and retrying on CPU…")
656
+ logger.warning(
657
+ "CUDA OOM detected. Freeing CUDA cache and retrying on CPU…"
658
+ )
530
659
  try:
531
660
  torch.cuda.empty_cache()
532
661
  gc.collect()
@@ -537,9 +666,17 @@ def _predict_video_pipeline(
537
666
  _ = ai_model_instance.cpu()
538
667
  classifier = Classifier(ai_model_instance, verbose=True)
539
668
  predictions = classifier.pipe(string_paths, crops)
540
- logger.info("Inference completed on CPU after CUDA OOM for video %s.", video.uuid)
669
+ logger.info(
670
+ "Inference completed on CPU after CUDA OOM for video %s.",
671
+ video.video_hash,
672
+ )
541
673
  except Exception as e2:
542
- logger.error("CPU fallback inference failed for video %s: %s", video.uuid, e2, exc_info=True)
674
+ logger.error(
675
+ "CPU fallback inference failed for video %s: %s",
676
+ video.video_hash,
677
+ e2,
678
+ exc_info=True,
679
+ )
543
680
  # Raise exception
544
681
  raise RuntimeError("Inference failed") from e2
545
682
  else:
@@ -554,10 +691,13 @@ def _predict_video_pipeline(
554
691
 
555
692
  # --- Post-processing ---
556
693
  try:
557
- logger.info("Post-processing predictions for video %s...", video.uuid)
694
+ logger.info("Post-processing predictions for video %s...", video.video_hash)
558
695
  readable_predictions = [classifier.readable(p) for p in predictions]
559
696
  if label_mapping:
560
- readable_predictions = [_remap_prediction_dict(prediction, label_mapping) for prediction in readable_predictions]
697
+ readable_predictions = [
698
+ _remap_prediction_dict(prediction, label_mapping)
699
+ for prediction in readable_predictions
700
+ ]
561
701
 
562
702
  merged_predictions = concat_pred_dicts(readable_predictions)
563
703
 
@@ -565,7 +705,7 @@ def _predict_video_pipeline(
565
705
  if not fps:
566
706
  logger.warning(
567
707
  "Video FPS is unknown for %s. Smoothing/sequence calculations might be inaccurate. Using default 30 FPS.",
568
- video.uuid,
708
+ video.video_hash,
569
709
  )
570
710
  fps = 30 # Default FPS if unknown
571
711
 
@@ -580,7 +720,9 @@ def _predict_video_pipeline(
580
720
 
581
721
  binary_smooth_merged_predictions = {}
582
722
  for key in smooth_merged_predictions.keys():
583
- binary_smooth_merged_predictions[key] = smooth_merged_predictions[key] > binarize_threshold
723
+ binary_smooth_merged_predictions[key] = (
724
+ smooth_merged_predictions[key] > binarize_threshold
725
+ )
584
726
 
585
727
  sequences = {}
586
728
  for label, prediction_array in binary_smooth_merged_predictions.items():
@@ -588,13 +730,18 @@ def _predict_video_pipeline(
588
730
 
589
731
  logger.info(
590
732
  "Post-processing completed for video %s. Found sequences for labels: %s",
591
- video.uuid,
733
+ video.video_hash,
592
734
  list(sequences.keys()),
593
735
  )
594
736
  return sequences if sequences is not None else {}
595
737
 
596
738
  except Exception as e:
597
- logger.error("Post-processing failed for video %s: %s", video.uuid, e, exc_info=True)
739
+ logger.error(
740
+ "Post-processing failed for video %s: %s",
741
+ video.video_hash,
742
+ e,
743
+ exc_info=True,
744
+ )
598
745
  # Raise exception
599
746
  raise RuntimeError("Post-processing failed") from e
600
747
 
@@ -617,14 +764,26 @@ def _predict_video_entry(
617
764
  ai_model = AiModel.objects.get(name=model_name)
618
765
  if not model_meta_version:
619
766
  model_meta = ai_model.get_latest_version()
620
- logger.info("Using latest ModelMeta version %s for model %s.", model_meta_version, model_name)
767
+ logger.info(
768
+ "Using latest ModelMeta version %s for model %s.",
769
+ model_meta_version,
770
+ model_name,
771
+ )
621
772
  else:
622
773
  model_meta = ai_model.get_version(model_meta_version)
623
- logger.info("Using specified ModelMeta version %s for model %s.", model_meta_version, model_name)
774
+ logger.info(
775
+ "Using specified ModelMeta version %s for model %s.",
776
+ model_meta_version,
777
+ model_name,
778
+ )
624
779
 
625
- logger.info("Using ModelMeta: %s (Version: %s)", model_meta.name, model_meta.version)
780
+ logger.info(
781
+ "Using ModelMeta: %s (Version: %s)", model_meta.name, model_meta.version
782
+ )
626
783
  except ModelMeta.DoesNotExist:
627
- logger.error("ModelMeta '%s' (Version: %s) not found.", model_name, model_meta_version)
784
+ logger.error(
785
+ "ModelMeta '%s' (Version: %s) not found.", model_name, model_meta_version
786
+ )
628
787
  raise
629
788
 
630
789
  # --- Explicitly pass only the arguments expected by _predict_video_pipeline ---
@@ -643,15 +802,21 @@ def _predict_video_entry(
643
802
  return predicted_sequences, model_meta
644
803
 
645
804
 
646
- def _extract_text_information(video: "VideoFile", frame_fraction: float = 0.001, cap: int = 15) -> Optional[Dict[str, str]]:
805
+ def _extract_text_information(
806
+ video: "VideoFile", frame_fraction: float = 0.001, cap: int = 15
807
+ ) -> Optional[Dict[str, str]]:
647
808
  """Facade function to call the text extraction logic."""
648
- logger.info("Attempting text extraction for video %s.", video.uuid)
809
+ logger.info("Attempting text extraction for video %s.", video.video_hash)
649
810
 
650
- extracted_data = _extract_text_from_video_frames(video=video, frame_fraction=frame_fraction, cap=cap)
811
+ extracted_data = _extract_text_from_video_frames(
812
+ video=video, frame_fraction=frame_fraction, cap=cap
813
+ )
651
814
 
652
815
  if extracted_data is not None:
653
- logger.info("Text extraction successful for video %s.", video.uuid)
816
+ logger.info("Text extraction successful for video %s.", video.video_hash)
654
817
  else:
655
- logger.warning("Text extraction returned no data for video %s.", video.uuid)
818
+ logger.warning(
819
+ "Text extraction returned no data for video %s.", video.video_hash
820
+ )
656
821
 
657
822
  return extracted_data