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
@@ -30,11 +30,19 @@ class VideoMeta(models.Model):
30
30
  Links to hardware (processor, endoscope), center, import details, and FFmpeg technical specs.
31
31
  """
32
32
 
33
- processor = models.ForeignKey("EndoscopyProcessor", on_delete=models.CASCADE, blank=True, null=True)
34
- endoscope = models.ForeignKey("Endoscope", on_delete=models.CASCADE, blank=True, null=True)
33
+ processor = models.ForeignKey(
34
+ "EndoscopyProcessor", on_delete=models.CASCADE, blank=True, null=True
35
+ )
36
+ endoscope = models.ForeignKey(
37
+ "Endoscope", on_delete=models.CASCADE, blank=True, null=True
38
+ )
35
39
  center = models.ForeignKey("Center", on_delete=models.CASCADE)
36
- import_meta = models.OneToOneField("VideoImportMeta", on_delete=models.CASCADE, blank=True, null=True)
37
- ffmpeg_meta = models.OneToOneField("FFMpegMeta", on_delete=models.CASCADE, blank=True, null=True)
40
+ import_meta = models.OneToOneField(
41
+ "VideoImportMeta", on_delete=models.CASCADE, blank=True, null=True
42
+ )
43
+ ffmpeg_meta = models.OneToOneField(
44
+ "FFMpegMeta", on_delete=models.CASCADE, blank=True, null=True
45
+ )
38
46
 
39
47
  if TYPE_CHECKING:
40
48
  processor: models.ForeignKey["EndoscopyProcessor|None"]
@@ -47,21 +55,27 @@ class VideoMeta(models.Model):
47
55
  def center_safe(self) -> "Center":
48
56
  center = self.center
49
57
  if not center:
50
- raise Center.DoesNotExist("Center does not exist for this VideoMeta instance.")
58
+ raise Center.DoesNotExist(
59
+ "Center does not exist for this VideoMeta instance."
60
+ )
51
61
  return center
52
62
 
53
63
  @property
54
64
  def processor_safe(self) -> "EndoscopyProcessor":
55
65
  processor = self.processor
56
66
  if not processor:
57
- raise EndoscopyProcessor.DoesNotExist("EndoscopyProcessor does not exist for this VideoMeta instance.")
67
+ raise EndoscopyProcessor.DoesNotExist(
68
+ "EndoscopyProcessor does not exist for this VideoMeta instance."
69
+ )
58
70
  return processor
59
71
 
60
72
  @property
61
73
  def ffmpeg_meta_safe(self) -> "FFMpegMeta":
62
74
  ffmpeg_meta = self.ffmpeg_meta
63
75
  if not ffmpeg_meta:
64
- raise FFMpegMeta.DoesNotExist("FFMpegMeta does not exist for this VideoMeta instance.")
76
+ raise FFMpegMeta.DoesNotExist(
77
+ "FFMpegMeta does not exist for this VideoMeta instance."
78
+ )
65
79
  return ffmpeg_meta
66
80
 
67
81
  @classmethod
@@ -88,14 +102,27 @@ class VideoMeta(models.Model):
88
102
  meta.initialize_ffmpeg_meta(video_path)
89
103
  except Exception as e:
90
104
  # Re-raise exceptions from ffmpeg meta initialization
91
- logger.error("Failed during FFMpegMeta initialization within create_from_file for %s: %s", video_path.name, e, exc_info=True)
92
- raise RuntimeError(f"Failed to initialize FFMpeg metadata for {video_path.name}") from e
105
+ logger.error(
106
+ "Failed during FFMpegMeta initialization within create_from_file for %s: %s",
107
+ video_path.name,
108
+ e,
109
+ exc_info=True,
110
+ )
111
+ raise RuntimeError(
112
+ f"Failed to initialize FFMpeg metadata for {video_path.name}"
113
+ ) from e
93
114
 
94
115
  if save_instance:
95
116
  meta.save() # This ensures VideoImportMeta is created too
96
- logger.info("Created and saved VideoMeta instance PK %s from %s", meta.pk, video_path.name)
117
+ logger.info(
118
+ "Created and saved VideoMeta instance PK %s from %s",
119
+ meta.pk,
120
+ video_path.name,
121
+ )
97
122
  else:
98
- logger.info("Instantiated VideoMeta from %s (not saved yet)", video_path.name)
123
+ logger.info(
124
+ "Instantiated VideoMeta from %s (not saved yet)", video_path.name
125
+ )
99
126
 
100
127
  return meta
101
128
 
@@ -129,10 +156,17 @@ class VideoMeta(models.Model):
129
156
  Raises RuntimeError if FFMpegMeta creation fails.
130
157
  """
131
158
  if self.ffmpeg_meta:
132
- logger.debug("FFMpegMeta already exists for VideoMeta PK %s. Skipping initialization.", self.pk)
159
+ logger.debug(
160
+ "FFMpegMeta already exists for VideoMeta PK %s. Skipping initialization.",
161
+ self.pk,
162
+ )
133
163
  return
134
164
 
135
- logger.info("Initializing FFMpegMeta for VideoMeta PK %s from %s", self.pk if self.pk else "(unsaved)", video_path.name)
165
+ logger.info(
166
+ "Initializing FFMpegMeta for VideoMeta PK %s from %s",
167
+ self.pk if self.pk else "(unsaved)",
168
+ video_path.name,
169
+ )
136
170
  try:
137
171
  # FFMpegMeta.create_from_file now raises exceptions on failure
138
172
  ffmpeg_instance = FFMpegMeta.create_from_file(video_path)
@@ -142,27 +176,43 @@ class VideoMeta(models.Model):
142
176
  # Otherwise, the link will be saved when VideoMeta itself is saved.
143
177
  if self.pk:
144
178
  self.save(update_fields=["ffmpeg_meta"])
145
- logger.info("Successfully created and linked FFMpegMeta PK %s", self.ffmpeg_meta_safe.pk)
179
+ logger.info(
180
+ "Successfully created and linked FFMpegMeta PK %s",
181
+ self.ffmpeg_meta_safe.pk,
182
+ )
146
183
 
147
184
  except Exception as e:
148
185
  # Log the error and re-raise it
149
- logger.error("Failed to create or link FFMpegMeta from %s: %s", video_path, e, exc_info=True)
150
- raise RuntimeError(f"Failed to create FFMpeg metadata from {video_path}") from e
186
+ logger.error(
187
+ "Failed to create or link FFMpegMeta from %s: %s",
188
+ video_path,
189
+ e,
190
+ exc_info=True,
191
+ )
192
+ raise RuntimeError(
193
+ f"Failed to create FFMpeg metadata from {video_path}"
194
+ ) from e
151
195
 
152
196
  def update_meta(self, video_path: Path):
153
197
  """
154
198
  Updates the FFMpeg metadata from the file, replacing existing data.
155
199
  Raises RuntimeError if FFMpegMeta creation fails.
156
200
  """
157
- logger.info("Updating FFMpegMeta for VideoMeta PK %s from %s", self.pk, video_path.name)
201
+ logger.info(
202
+ "Updating FFMpegMeta for VideoMeta PK %s from %s", self.pk, video_path.name
203
+ )
158
204
  existing_ffmpeg_pk = None
159
205
  if self.ffmpeg_meta:
160
206
  existing_ffmpeg_pk = self.ffmpeg_meta.pk
161
- logger.debug("Deleting existing FFMpegMeta PK %s before update.", existing_ffmpeg_pk)
207
+ logger.debug(
208
+ "Deleting existing FFMpegMeta PK %s before update.", existing_ffmpeg_pk
209
+ )
162
210
  # Nullify the relation first before deleting the related object
163
211
  self.ffmpeg_meta = None
164
212
  self.save(update_fields=["ffmpeg_meta"]) # Save the null relation
165
- FFMpegMeta.objects.filter(pk=existing_ffmpeg_pk).delete() # Delete the old object
213
+ FFMpegMeta.objects.filter(
214
+ pk=existing_ffmpeg_pk
215
+ ).delete() # Delete the old object
166
216
 
167
217
  # initialize_ffmpeg_meta handles creation, linking, saving the link, and raises exceptions
168
218
  self.initialize_ffmpeg_meta(video_path)
@@ -179,7 +229,9 @@ class VideoMeta(models.Model):
179
229
  def fps(self) -> Optional[float]:
180
230
  """Returns the frame rate (FPS) from the linked FFMpegMeta."""
181
231
  if not self.ffmpeg_meta:
182
- logger.warning("FFMpegMeta not linked for VideoMeta PK %s. Cannot get FPS.", self.pk)
232
+ logger.warning(
233
+ "FFMpegMeta not linked for VideoMeta PK %s. Cannot get FPS.", self.pk
234
+ )
183
235
  return None
184
236
  return self.ffmpeg_meta.fps
185
237
 
@@ -201,7 +253,12 @@ class VideoMeta(models.Model):
201
253
  @property
202
254
  def frame_count(self) -> Optional[int]:
203
255
  """Calculates frame count based on duration and FPS from FFMpegMeta."""
204
- if self.ffmpeg_meta and self.ffmpeg_meta.duration is not None and self.ffmpeg_meta.fps is not None and self.ffmpeg_meta.fps > 0:
256
+ if (
257
+ self.ffmpeg_meta
258
+ and self.ffmpeg_meta.duration is not None
259
+ and self.ffmpeg_meta.fps is not None
260
+ and self.ffmpeg_meta.fps > 0
261
+ ):
205
262
  return int(self.ffmpeg_meta.duration * self.ffmpeg_meta.fps)
206
263
  return None
207
264
 
@@ -214,17 +271,29 @@ class FFMpegMeta(models.Model):
214
271
  width = models.IntegerField(null=True, blank=True)
215
272
  height = models.IntegerField(null=True, blank=True)
216
273
  duration = models.FloatField(null=True, blank=True) # Duration in seconds
217
- frame_rate_num = models.IntegerField(null=True, blank=True) # Numerator for frame rate
218
- frame_rate_den = models.IntegerField(null=True, blank=True) # Denominator for frame rate
274
+ frame_rate_num = models.IntegerField(
275
+ null=True, blank=True
276
+ ) # Numerator for frame rate
277
+ frame_rate_den = models.IntegerField(
278
+ null=True, blank=True
279
+ ) # Denominator for frame rate
219
280
  codec_name = models.CharField(max_length=50, null=True, blank=True)
220
281
  pixel_format = models.CharField(max_length=50, null=True, blank=True)
221
- bit_rate = models.BigIntegerField(null=True, blank=True) # Bit rate in bits per second
222
- raw_probe_data = models.JSONField(null=True, blank=True) # Store the full JSON output for debugging or future use
282
+ bit_rate = models.BigIntegerField(
283
+ null=True, blank=True
284
+ ) # Bit rate in bits per second
285
+ raw_probe_data = models.JSONField(
286
+ null=True, blank=True
287
+ ) # Store the full JSON output for debugging or future use
223
288
 
224
289
  @property
225
290
  def fps(self) -> Optional[float]:
226
291
  """Calculates and returns the frames per second (FPS) if possible."""
227
- if self.frame_rate_num is not None and self.frame_rate_den is not None and self.frame_rate_den != 0:
292
+ if (
293
+ self.frame_rate_num is not None
294
+ and self.frame_rate_den is not None
295
+ and self.frame_rate_den != 0
296
+ ):
228
297
  return self.frame_rate_num / self.frame_rate_den
229
298
  return None
230
299
 
@@ -236,17 +305,30 @@ class FFMpegMeta(models.Model):
236
305
  """
237
306
  logger.info("Running ffprobe on %s", file_path)
238
307
  try:
239
- probe_data = ffmpeg_wrapper.get_stream_info(file_path) # Use the new utility
308
+ probe_data = ffmpeg_wrapper.get_stream_info(
309
+ file_path
310
+ ) # Use the new utility
240
311
  except Exception as probe_err:
241
- logger.error("ffprobe execution failed for %s: %s", file_path, probe_err, exc_info=True)
242
- raise RuntimeError(f"ffprobe execution failed for {file_path}") from probe_err
312
+ logger.error(
313
+ "ffprobe execution failed for %s: %s",
314
+ file_path,
315
+ probe_err,
316
+ exc_info=True,
317
+ )
318
+ raise RuntimeError(
319
+ f"ffprobe execution failed for {file_path}"
320
+ ) from probe_err
243
321
 
244
322
  if not probe_data or "streams" not in probe_data:
245
- logger.error("Failed to get valid stream info from ffprobe for %s", file_path)
323
+ logger.error(
324
+ "Failed to get valid stream info from ffprobe for %s", file_path
325
+ )
246
326
  # Raise exception instead of returning None
247
327
  raise RuntimeError(f"Invalid stream info from ffprobe for {file_path}")
248
328
 
249
- video_stream = next((s for s in probe_data["streams"] if s.get("codec_type") == "video"), None)
329
+ video_stream = next(
330
+ (s for s in probe_data["streams"] if s.get("codec_type") == "video"), None
331
+ )
250
332
 
251
333
  if not video_stream:
252
334
  logger.warning("No video stream found in ffprobe output for %s", file_path)
@@ -258,7 +340,11 @@ class FFMpegMeta(models.Model):
258
340
  height = video_stream.get("height")
259
341
  duration_str = video_stream.get("duration")
260
342
  # --- FIX: Handle potential format key ---
261
- if duration_str is None and "format" in probe_data and "duration" in probe_data["format"]:
343
+ if (
344
+ duration_str is None
345
+ and "format" in probe_data
346
+ and "duration" in probe_data["format"]
347
+ ):
262
348
  duration_str = probe_data["format"]["duration"]
263
349
  logger.debug("Using duration from format block: %s", duration_str)
264
350
  # --- End Fix ---
@@ -278,17 +364,25 @@ class FFMpegMeta(models.Model):
278
364
  frame_rate_num = int(num_str)
279
365
  frame_rate_den = int(den_str)
280
366
  if frame_rate_den == 0: # Avoid division by zero
281
- logger.warning("Invalid frame rate denominator (0) for %s", file_path)
367
+ logger.warning(
368
+ "Invalid frame rate denominator (0) for %s", file_path
369
+ )
282
370
  frame_rate_num, frame_rate_den = None, None
283
371
  except ValueError:
284
- logger.warning("Could not parse frame rate '%s' for %s", frame_rate_str, file_path)
372
+ logger.warning(
373
+ "Could not parse frame rate '%s' for %s", frame_rate_str, file_path
374
+ )
285
375
  frame_rate_num, frame_rate_den = None, None
286
376
 
287
377
  codec_name = video_stream.get("codec_name")
288
378
  pixel_format = video_stream.get("pix_fmt")
289
379
  bit_rate_str = video_stream.get("bit_rate")
290
380
  # --- FIX: Handle potential format key for bit_rate ---
291
- if bit_rate_str is None and "format" in probe_data and "bit_rate" in probe_data["format"]:
381
+ if (
382
+ bit_rate_str is None
383
+ and "format" in probe_data
384
+ and "bit_rate" in probe_data["format"]
385
+ ):
292
386
  bit_rate_str = probe_data["format"]["bit_rate"]
293
387
  logger.debug("Using bit_rate from format block: %s", bit_rate_str)
294
388
  # --- End Fix ---
@@ -306,12 +400,23 @@ class FFMpegMeta(models.Model):
306
400
  bit_rate=bit_rate,
307
401
  raw_probe_data=probe_data,
308
402
  )
309
- logger.info("Successfully created FFMpegMeta for %s (ID: %d)", file_path.name, instance.pk)
403
+ logger.info(
404
+ "Successfully created FFMpegMeta for %s (ID: %d)",
405
+ file_path.name,
406
+ instance.pk,
407
+ )
310
408
  return instance
311
409
  except Exception as e:
312
- logger.error("Error creating FFMpegMeta DB record from %s: %s", file_path.name, e, exc_info=True)
410
+ logger.error(
411
+ "Error creating FFMpegMeta DB record from %s: %s",
412
+ file_path.name,
413
+ e,
414
+ exc_info=True,
415
+ )
313
416
  # Raise exception instead of returning None
314
- raise RuntimeError(f"Database error creating FFMpegMeta for {file_path.name}") from e
417
+ raise RuntimeError(
418
+ f"Database error creating FFMpegMeta for {file_path.name}"
419
+ ) from e
315
420
 
316
421
  def __str__(self):
317
422
  """Returns a string summary of the FFmpeg metadata."""
@@ -345,7 +450,9 @@ class VideoImportMeta(models.Model):
345
450
  result_html = ""
346
451
 
347
452
  result_html += f"Video anonymized: {self.video_anonymized}\n"
348
- result_html += f"Video patient data detected: {self.video_patient_data_detected}\n"
453
+ result_html += (
454
+ f"Video patient data detected: {self.video_patient_data_detected}\n"
455
+ )
349
456
  result_html += f"Outside detected: {self.outside_detected}\n"
350
457
  result_html += f"Patient data removed: {self.patient_data_removed}\n"
351
458
  result_html += f"Outside removed: {self.outside_removed}\n"
@@ -19,7 +19,11 @@ if TYPE_CHECKING:
19
19
  from .video_prediction_meta import VideoPredictionMeta
20
20
 
21
21
 
22
- def apply_running_mean_logic(instance: "VideoPredictionMeta", confidence_array: np.ndarray, window_size_in_seconds: Optional[float] = None) -> np.ndarray:
22
+ def apply_running_mean_logic(
23
+ instance: "VideoPredictionMeta",
24
+ confidence_array: np.ndarray,
25
+ window_size_in_seconds: Optional[float] = None,
26
+ ) -> np.ndarray:
23
27
  """
24
28
  Apply a running mean filter to the confidence array for smoothing.
25
29
  """
@@ -27,7 +31,9 @@ def apply_running_mean_logic(instance: "VideoPredictionMeta", confidence_array:
27
31
  fps = video_obj.get_fps()
28
32
 
29
33
  if fps is None or fps <= 0:
30
- logger.warning(f"Invalid FPS ({fps}) for {video_obj}. Cannot apply running mean. Returning original array.")
34
+ logger.warning(
35
+ f"Invalid FPS ({fps}) for {video_obj}. Cannot apply running mean. Returning original array."
36
+ )
31
37
  return confidence_array
32
38
 
33
39
  if window_size_in_seconds is None:
@@ -52,14 +58,18 @@ def apply_running_mean_logic(instance: "VideoPredictionMeta", confidence_array:
52
58
  running_mean = running_mean[start_index:end_index]
53
59
 
54
60
  if running_mean.shape != confidence_array.shape:
55
- logger.warning(f"Running mean output shape {running_mean.shape} differs from input {confidence_array.shape}. Check padding/slicing.")
61
+ logger.warning(
62
+ f"Running mean output shape {running_mean.shape} differs from input {confidence_array.shape}. Check padding/slicing."
63
+ )
56
64
  # Return original array on shape mismatch to avoid downstream errors
57
65
  return confidence_array
58
66
 
59
67
  return running_mean
60
68
 
61
69
 
62
- def calculate_prediction_array_logic(instance: "VideoPredictionMeta", window_size_in_seconds: Optional[float] = None) -> Optional[np.ndarray]:
70
+ def calculate_prediction_array_logic(
71
+ instance: "VideoPredictionMeta", window_size_in_seconds: Optional[float] = None
72
+ ) -> Optional[np.ndarray]:
63
73
  """
64
74
  Fetches predictions, applies smoothing, and returns the binary prediction array.
65
75
  Does not save the array itself.
@@ -72,19 +82,29 @@ def calculate_prediction_array_logic(instance: "VideoPredictionMeta", window_siz
72
82
  num_frames = video_obj.frame_count
73
83
 
74
84
  if num_frames is None or num_frames <= 0:
75
- logger.warning(f"Cannot calculate prediction array for {video_obj} with invalid frame count ({num_frames}).")
85
+ logger.warning(
86
+ f"Cannot calculate prediction array for {video_obj} with invalid frame count ({num_frames})."
87
+ )
76
88
  return None
77
89
 
78
90
  if not label_list:
79
- logger.warning(f"No labels found for model {model_meta}. Cannot calculate prediction array.")
91
+ logger.warning(
92
+ f"No labels found for model {model_meta}. Cannot calculate prediction array."
93
+ )
80
94
  return None
81
95
 
82
96
  prediction_array = np.zeros((num_frames, len(label_list)))
83
97
 
84
- base_pred_qs = ImageClassificationAnnotation.objects.filter(model_meta=model_meta, frame__video_file=video_obj)
98
+ base_pred_qs = ImageClassificationAnnotation.objects.filter(
99
+ model_meta=model_meta, frame__video_file=video_obj
100
+ )
85
101
 
86
102
  for i, label in enumerate(label_list):
87
- predictions = base_pred_qs.filter(label=label).order_by("frame__frame_number").values_list("frame__frame_number", "confidence")
103
+ predictions = (
104
+ base_pred_qs.filter(label=label)
105
+ .order_by("frame__frame_number")
106
+ .values_list("frame__frame_number", "confidence")
107
+ )
88
108
 
89
109
  # Initialize with 0.5 (neutral confidence)
90
110
  confidences = np.full(num_frames, 0.5)
@@ -94,12 +114,18 @@ def calculate_prediction_array_logic(instance: "VideoPredictionMeta", window_siz
94
114
  confidences[frame_num] = confidence
95
115
  found_predictions = True
96
116
  else:
97
- logger.warning(f"Prediction found for out-of-bounds frame number {frame_num} (max: {num_frames - 1}). Skipping.")
117
+ logger.warning(
118
+ f"Prediction found for out-of-bounds frame number {frame_num} (max: {num_frames - 1}). Skipping."
119
+ )
98
120
 
99
121
  if not found_predictions:
100
- logger.warning(f"No predictions found for label '{label.name}' in {video_obj}. Using default confidence.")
122
+ logger.warning(
123
+ f"No predictions found for label '{label.name}' in {video_obj}. Using default confidence."
124
+ )
101
125
 
102
- smooth_confidences = apply_running_mean_logic(instance, confidences, window_size_in_seconds)
126
+ smooth_confidences = apply_running_mean_logic(
127
+ instance, confidences, window_size_in_seconds
128
+ )
103
129
  # Threshold smoothed confidences
104
130
  binary_predictions = smooth_confidences > 0.5
105
131
  prediction_array[:, i] = binary_predictions
@@ -107,7 +133,9 @@ def calculate_prediction_array_logic(instance: "VideoPredictionMeta", window_siz
107
133
  return prediction_array
108
134
 
109
135
 
110
- def create_video_segments_for_label_logic(instance: "VideoPredictionMeta", segments: List[Tuple[int, int]], label: "Label"):
136
+ def create_video_segments_for_label_logic(
137
+ instance: "VideoPredictionMeta", segments: List[Tuple[int, int]], label: "Label"
138
+ ):
111
139
  """
112
140
  Creates LabelVideoSegment instances for the given label and segments.
113
141
  """
@@ -128,18 +156,29 @@ def create_video_segments_for_label_logic(instance: "VideoPredictionMeta", segme
128
156
  }
129
157
  # Check for existence before creating the object instance
130
158
  if not LabelVideoSegment.objects.filter(
131
- video_file=video_obj, prediction_meta=instance, label=label, start_frame_number=start_frame, end_frame_number=end_frame
159
+ video_file=video_obj,
160
+ prediction_meta=instance,
161
+ label=label,
162
+ start_frame_number=start_frame,
163
+ end_frame_number=end_frame,
132
164
  ).exists():
133
165
  segments_to_create.append(LabelVideoSegment(**segment_data))
134
166
 
135
167
  if segments_to_create:
136
168
  LabelVideoSegment.objects.bulk_create(segments_to_create)
137
- logger.info(f"Created {len(segments_to_create)} video segments for label '{label.name}' in {video_obj}.")
169
+ logger.info(
170
+ f"Created {len(segments_to_create)} video segments for label '{label.name}' in {video_obj}."
171
+ )
138
172
  else:
139
- logger.info(f"No new video segments needed for label '{label.name}' in {video_obj}.")
173
+ logger.info(
174
+ f"No new video segments needed for label '{label.name}' in {video_obj}."
175
+ )
140
176
 
141
177
 
142
- def create_video_segments_logic(instance: "VideoPredictionMeta", segment_length_threshold_in_s: Optional[float] = None):
178
+ def create_video_segments_logic(
179
+ instance: "VideoPredictionMeta",
180
+ segment_length_threshold_in_s: Optional[float] = None,
181
+ ):
143
182
  """
144
183
  Generates LabelVideoSegments based on the stored prediction array.
145
184
  """
@@ -150,7 +189,9 @@ def create_video_segments_logic(instance: "VideoPredictionMeta", segment_length_
150
189
  fps = video_obj.get_fps()
151
190
 
152
191
  if fps is None or fps <= 0:
153
- logger.warning(f"Cannot create video segments for {video_obj} with invalid FPS ({fps}).")
192
+ logger.warning(
193
+ f"Cannot create video segments for {video_obj} with invalid FPS ({fps})."
194
+ )
154
195
  return
155
196
 
156
197
  min_frame_length = int(segment_length_threshold_in_s * fps)
@@ -167,17 +208,25 @@ def create_video_segments_logic(instance: "VideoPredictionMeta", segment_length_
167
208
  instance.calculate_prediction_array() # This will save the array internally
168
209
  prediction_array = instance.get_prediction_array() # Fetch again
169
210
  if prediction_array is None:
170
- logger.error(f"Failed to get or calculate prediction array for {instance}. Cannot create segments.")
211
+ logger.error(
212
+ f"Failed to get or calculate prediction array for {instance}. Cannot create segments."
213
+ )
171
214
  return
172
215
 
173
216
  if prediction_array.shape[1] != len(label_list):
174
- logger.warning(f"Prediction array shape {prediction_array.shape} incompatible with label list length {len(label_list)} for {instance}.")
217
+ logger.warning(
218
+ f"Prediction array shape {prediction_array.shape} incompatible with label list length {len(label_list)} for {instance}."
219
+ )
175
220
  return
176
221
 
177
- logger.info(f"Creating video segments for {instance} (min length: {min_frame_length} frames)...")
222
+ logger.info(
223
+ f"Creating video segments for {instance} (min length: {min_frame_length} frames)..."
224
+ )
178
225
  for i, label in enumerate(label_list):
179
226
  binary_predictions = prediction_array[:, i].astype(bool)
180
- segments = find_segments_in_prediction_array(binary_predictions, min_frame_length)
227
+ segments = find_segments_in_prediction_array(
228
+ binary_predictions, min_frame_length
229
+ )
181
230
  if segments:
182
231
  create_video_segments_for_label_logic(instance, segments, label)
183
232
  logger.info(f"Finished creating video segments for {instance}.")