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

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

Potentially problematic release.


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

Files changed (503) hide show
  1. endoreg_db/authz/auth.py +74 -0
  2. endoreg_db/authz/backends.py +168 -0
  3. endoreg_db/authz/management/commands/list_routes.py +18 -0
  4. endoreg_db/authz/middleware.py +83 -0
  5. endoreg_db/authz/permissions.py +127 -0
  6. endoreg_db/authz/policy.py +218 -0
  7. endoreg_db/authz/views_auth.py +66 -0
  8. endoreg_db/config/env.py +13 -8
  9. endoreg_db/data/__init__.py +2 -11
  10. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +3 -3
  11. endoreg_db/data/event_classification/data.yaml +4 -0
  12. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  13. endoreg_db/data/examination/examinations/data.yaml +114 -14
  14. endoreg_db/data/examination/time-type/data.yaml +0 -3
  15. endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
  16. endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
  17. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
  18. endoreg_db/data/finding/00_generic.yaml +35 -0
  19. endoreg_db/data/finding/00_generic_complication.yaml +9 -0
  20. endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
  21. endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
  22. endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
  23. endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
  24. endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
  25. endoreg_db/data/finding_classification/00_generic.yaml +44 -0
  26. endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
  27. endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
  28. endoreg_db/data/finding_classification/02_colonoscopy_baseline.yaml +83 -0
  29. endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
  30. endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
  31. endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
  32. endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
  33. endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
  34. endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
  35. endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
  36. endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
  37. endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
  38. endoreg_db/data/finding_classification_choice/{colonoscopy_not_complete_reason.yaml → 02_colonoscopy_generic.yaml} +1 -1
  39. endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
  40. endoreg_db/data/finding_classification_choice/{colonoscopy_location.yaml → 02_colonoscopy_location.yaml} +23 -4
  41. endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
  42. endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
  43. endoreg_db/data/finding_classification_choice/{colon_lesion_paris.yaml → 02_colonoscopy_polyp_morphology.yaml} +26 -8
  44. endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
  45. endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
  46. endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
  47. endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
  48. endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
  49. endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
  50. endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
  51. endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
  52. endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
  53. endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
  54. endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
  55. endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
  56. endoreg_db/data/finding_type/data.yaml +8 -12
  57. endoreg_db/data/requirement/01_patient_data.yaml +93 -0
  58. endoreg_db/data/requirement/old/colon_polyp_intervention.yaml +49 -0
  59. endoreg_db/data/requirement/old/coloreg_colon_polyp.yaml +49 -0
  60. endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
  61. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +29 -12
  62. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  63. endoreg_db/data/requirement_set/{endoscopy_bleeding_risk.yaml → 02_endoscopy_bleeding_risk.yaml} +0 -6
  64. endoreg_db/data/requirement_set/90_coloreg.yaml +190 -0
  65. endoreg_db/data/requirement_set/_old_ +109 -0
  66. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  67. endoreg_db/data/setup_config.yaml +4 -4
  68. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  69. endoreg_db/exceptions.py +4 -2
  70. endoreg_db/forms/examination_form.py +1 -1
  71. endoreg_db/helpers/data_loader.py +125 -53
  72. endoreg_db/helpers/default_objects.py +116 -81
  73. endoreg_db/import_files/__init__.py +27 -0
  74. endoreg_db/import_files/context/__init__.py +7 -0
  75. endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
  76. endoreg_db/import_files/context/ensure_center.py +17 -0
  77. endoreg_db/import_files/context/file_lock.py +66 -0
  78. endoreg_db/import_files/context/import_context.py +43 -0
  79. endoreg_db/import_files/context/validate_directories.py +56 -0
  80. endoreg_db/import_files/file_storage/__init__.py +15 -0
  81. endoreg_db/import_files/file_storage/create_report_file.py +76 -0
  82. endoreg_db/import_files/file_storage/create_video_file.py +75 -0
  83. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
  84. endoreg_db/import_files/file_storage/state_management.py +400 -0
  85. endoreg_db/import_files/file_storage/storage.py +36 -0
  86. endoreg_db/import_files/import_service.md +26 -0
  87. endoreg_db/import_files/processing/__init__.py +11 -0
  88. endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
  89. endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
  90. endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
  91. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
  92. endoreg_db/import_files/pseudonymization/fake.py +52 -0
  93. endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
  94. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
  95. endoreg_db/import_files/report_import_service.py +141 -0
  96. endoreg_db/import_files/video_import_service.py +150 -0
  97. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  98. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  99. endoreg_db/management/commands/import_report.py +130 -65
  100. endoreg_db/management/commands/import_video.py +9 -10
  101. endoreg_db/management/commands/import_video_with_classification.py +2 -2
  102. endoreg_db/management/commands/list_routes.py +18 -0
  103. endoreg_db/management/commands/load_ai_model_data.py +5 -5
  104. endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
  105. endoreg_db/management/commands/load_base_db_data.py +5 -134
  106. endoreg_db/management/commands/load_center_data.py +12 -12
  107. endoreg_db/management/commands/load_contraindication_data.py +14 -16
  108. endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
  109. endoreg_db/management/commands/load_disease_classification_data.py +15 -18
  110. endoreg_db/management/commands/load_disease_data.py +25 -28
  111. endoreg_db/management/commands/load_endoscope_data.py +20 -27
  112. endoreg_db/management/commands/load_event_data.py +14 -16
  113. endoreg_db/management/commands/load_examination_data.py +31 -44
  114. endoreg_db/management/commands/load_examination_indication_data.py +20 -21
  115. endoreg_db/management/commands/load_finding_data.py +52 -80
  116. endoreg_db/management/commands/load_information_source.py +21 -23
  117. endoreg_db/management/commands/load_lab_value_data.py +17 -26
  118. endoreg_db/management/commands/load_medication_data.py +13 -12
  119. endoreg_db/management/commands/load_organ_data.py +15 -19
  120. endoreg_db/management/commands/load_pdf_type_data.py +19 -18
  121. endoreg_db/management/commands/load_profession_data.py +14 -17
  122. endoreg_db/management/commands/load_qualification_data.py +20 -23
  123. endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
  124. endoreg_db/management/commands/load_requirement_data.py +62 -39
  125. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  126. endoreg_db/management/commands/load_risk_data.py +7 -6
  127. endoreg_db/management/commands/load_shift_data.py +20 -23
  128. endoreg_db/management/commands/load_tag_data.py +8 -11
  129. endoreg_db/management/commands/load_unit_data.py +17 -19
  130. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  131. endoreg_db/management/commands/start_filewatcher.py +46 -37
  132. endoreg_db/management/commands/storage_management.py +271 -203
  133. endoreg_db/management/commands/validate_video_files.py +1 -5
  134. endoreg_db/migrations/0001_initial.py +297 -250
  135. endoreg_db/models/__init__.py +78 -123
  136. endoreg_db/models/administration/__init__.py +21 -42
  137. endoreg_db/models/administration/ai/active_model.py +2 -2
  138. endoreg_db/models/administration/ai/ai_model.py +7 -6
  139. endoreg_db/models/administration/case/__init__.py +1 -15
  140. endoreg_db/models/administration/case/case.py +3 -3
  141. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  142. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  143. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  144. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  145. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  146. endoreg_db/models/administration/center/center.py +33 -19
  147. endoreg_db/models/administration/center/center_product.py +12 -9
  148. endoreg_db/models/administration/center/center_resource.py +25 -19
  149. endoreg_db/models/administration/center/center_shift.py +21 -17
  150. endoreg_db/models/administration/center/center_waste.py +16 -8
  151. endoreg_db/models/administration/person/__init__.py +2 -0
  152. endoreg_db/models/administration/person/employee/employee.py +10 -5
  153. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  154. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  155. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  156. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  157. endoreg_db/models/administration/person/patient/patient.py +129 -100
  158. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  159. endoreg_db/models/administration/person/person.py +4 -0
  160. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  161. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  162. endoreg_db/models/administration/product/product.py +20 -15
  163. endoreg_db/models/administration/product/product_material.py +17 -18
  164. endoreg_db/models/administration/product/product_weight.py +12 -8
  165. endoreg_db/models/administration/product/reference_product.py +23 -55
  166. endoreg_db/models/administration/qualification/qualification.py +7 -3
  167. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  168. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  169. endoreg_db/models/administration/shift/shift.py +16 -12
  170. endoreg_db/models/administration/shift/shift_type.py +23 -31
  171. endoreg_db/models/label/__init__.py +8 -9
  172. endoreg_db/models/label/annotation/image_classification.py +10 -9
  173. endoreg_db/models/label/annotation/video_segmentation_annotation.py +23 -28
  174. endoreg_db/models/label/label.py +15 -15
  175. endoreg_db/models/label/label_set.py +19 -6
  176. endoreg_db/models/label/label_type.py +1 -1
  177. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  178. endoreg_db/models/label/label_video_segment/label_video_segment.py +98 -102
  179. endoreg_db/models/label/video_segmentation_label.py +4 -0
  180. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  181. endoreg_db/models/media/frame/frame.py +22 -22
  182. endoreg_db/models/media/pdf/raw_pdf.py +194 -194
  183. endoreg_db/models/media/pdf/report_file.py +25 -29
  184. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +55 -47
  185. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  186. endoreg_db/models/media/processing_history/__init__.py +5 -0
  187. endoreg_db/models/media/processing_history/processing_history.py +96 -0
  188. endoreg_db/models/media/video/__init__.py +1 -0
  189. endoreg_db/models/media/video/create_from_file.py +139 -77
  190. endoreg_db/models/media/video/pipe_2.py +8 -9
  191. endoreg_db/models/media/video/video_file.py +174 -112
  192. endoreg_db/models/media/video/video_file_ai.py +288 -74
  193. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  194. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  195. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  196. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  197. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  198. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  199. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  200. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  201. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  202. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  203. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  204. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  205. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  206. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  207. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  208. endoreg_db/models/media/video/video_file_io.py +113 -61
  209. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  210. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  211. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  212. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  213. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  214. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  215. endoreg_db/models/media/video/video_file_segments.py +24 -17
  216. endoreg_db/models/media/video/video_metadata.py +19 -35
  217. endoreg_db/models/media/video/video_processing.py +96 -95
  218. endoreg_db/models/medical/contraindication/README.md +1 -0
  219. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  220. endoreg_db/models/medical/disease.py +22 -16
  221. endoreg_db/models/medical/event.py +31 -18
  222. endoreg_db/models/medical/examination/__init__.py +13 -6
  223. endoreg_db/models/medical/examination/examination.py +39 -20
  224. endoreg_db/models/medical/examination/examination_indication.py +30 -95
  225. endoreg_db/models/medical/examination/examination_time.py +23 -8
  226. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  227. endoreg_db/models/medical/examination/examination_type.py +3 -4
  228. endoreg_db/models/medical/finding/finding.py +32 -40
  229. endoreg_db/models/medical/finding/finding_classification.py +42 -72
  230. endoreg_db/models/medical/finding/finding_intervention.py +25 -22
  231. endoreg_db/models/medical/finding/finding_type.py +13 -12
  232. endoreg_db/models/medical/hardware/endoscope.py +26 -26
  233. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  234. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  235. endoreg_db/models/medical/medication/medication.py +22 -10
  236. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  237. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  238. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  239. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  240. endoreg_db/models/medical/organ/__init__.py +15 -12
  241. endoreg_db/models/medical/patient/medication_examples.py +6 -6
  242. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  243. endoreg_db/models/medical/patient/patient_event.py +19 -22
  244. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  245. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  246. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  247. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  248. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  249. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  250. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  251. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  252. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  253. endoreg_db/models/medical/risk/risk.py +7 -6
  254. endoreg_db/models/medical/risk/risk_type.py +8 -5
  255. endoreg_db/models/metadata/model_meta.py +60 -29
  256. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  257. endoreg_db/models/metadata/pdf_meta.py +31 -24
  258. endoreg_db/models/metadata/sensitive_meta.py +105 -85
  259. endoreg_db/models/metadata/sensitive_meta_logic.py +198 -103
  260. endoreg_db/models/metadata/video_meta.py +51 -31
  261. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  262. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  263. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  264. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  265. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  266. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  267. endoreg_db/models/other/emission/emission_factor.py +18 -8
  268. endoreg_db/models/other/gender.py +10 -5
  269. endoreg_db/models/other/information_source.py +50 -29
  270. endoreg_db/models/other/material.py +9 -5
  271. endoreg_db/models/other/resource.py +6 -4
  272. endoreg_db/models/other/tag.py +10 -5
  273. endoreg_db/models/other/transport_route.py +13 -8
  274. endoreg_db/models/other/unit.py +10 -6
  275. endoreg_db/models/other/waste.py +6 -5
  276. endoreg_db/models/report/report.py +6 -0
  277. endoreg_db/models/requirement/requirement.py +329 -361
  278. endoreg_db/models/requirement/requirement_error.py +85 -0
  279. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  280. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  281. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  282. endoreg_db/models/requirement/requirement_operator.py +103 -112
  283. endoreg_db/models/requirement/requirement_set.py +74 -57
  284. endoreg_db/models/state/__init__.py +4 -4
  285. endoreg_db/models/state/abstract.py +2 -2
  286. endoreg_db/models/state/anonymization.py +12 -0
  287. endoreg_db/models/state/audit_ledger.py +49 -51
  288. endoreg_db/models/state/label_video_segment.py +9 -0
  289. endoreg_db/models/state/raw_pdf.py +101 -68
  290. endoreg_db/models/state/sensitive_meta.py +6 -2
  291. endoreg_db/models/state/video.py +110 -90
  292. endoreg_db/models/upload_job.py +35 -34
  293. endoreg_db/models/utils.py +28 -25
  294. endoreg_db/queries/__init__.py +3 -1
  295. endoreg_db/root_urls.py +21 -2
  296. endoreg_db/schemas/examination_evaluation.py +1 -1
  297. endoreg_db/serializers/__init__.py +2 -10
  298. endoreg_db/serializers/anonymization.py +18 -10
  299. endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
  300. endoreg_db/serializers/meta/__init__.py +1 -6
  301. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  302. endoreg_db/serializers/misc/file_overview.py +11 -99
  303. endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
  304. endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
  305. endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
  306. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  307. endoreg_db/serializers/video/segmentation.py +2 -1
  308. endoreg_db/serializers/video/video_file_list.py +65 -34
  309. endoreg_db/serializers/video/video_processing_history.py +20 -5
  310. endoreg_db/services/__old/pdf_import.py +1487 -0
  311. endoreg_db/services/__old/video_import.py +1306 -0
  312. endoreg_db/services/anonymization.py +128 -89
  313. endoreg_db/services/lookup_service.py +65 -52
  314. endoreg_db/services/lookup_store.py +2 -2
  315. endoreg_db/services/pdf_import.py +0 -1382
  316. endoreg_db/services/report_import.py +10 -0
  317. endoreg_db/services/video_import.py +6 -1255
  318. endoreg_db/tasks/upload_tasks.py +79 -70
  319. endoreg_db/tasks/video_ingest.py +8 -4
  320. endoreg_db/urls/__init__.py +5 -32
  321. endoreg_db/urls/ai.py +32 -0
  322. endoreg_db/urls/media.py +121 -83
  323. endoreg_db/urls/root_urls.py +29 -0
  324. endoreg_db/utils/__init__.py +15 -5
  325. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  326. endoreg_db/utils/case_generator/__init__.py +3 -0
  327. endoreg_db/utils/dataloader.py +142 -40
  328. endoreg_db/utils/defaults/set_default_center.py +32 -0
  329. endoreg_db/utils/names.py +22 -16
  330. endoreg_db/utils/paths.py +110 -46
  331. endoreg_db/utils/permissions.py +2 -1
  332. endoreg_db/utils/pipelines/Readme.md +1 -1
  333. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  334. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +655 -0
  335. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
  336. endoreg_db/utils/setup_config.py +8 -5
  337. endoreg_db/utils/storage.py +115 -0
  338. endoreg_db/utils/validate_endo_roi.py +8 -2
  339. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  340. endoreg_db/views/__init__.py +85 -183
  341. endoreg_db/views/ai/__init__.py +8 -0
  342. endoreg_db/views/ai/label.py +155 -0
  343. endoreg_db/views/anonymization/media_management.py +202 -166
  344. endoreg_db/views/anonymization/overview.py +99 -67
  345. endoreg_db/views/anonymization/validate.py +182 -44
  346. endoreg_db/views/media/__init__.py +7 -20
  347. endoreg_db/views/media/pdf_media.py +197 -174
  348. endoreg_db/views/media/sensitive_metadata.py +193 -138
  349. endoreg_db/views/media/video_media.py +89 -82
  350. endoreg_db/views/meta/__init__.py +0 -8
  351. endoreg_db/views/misc/__init__.py +1 -7
  352. endoreg_db/views/misc/upload_views.py +94 -93
  353. endoreg_db/views/patient/patient.py +5 -4
  354. endoreg_db/views/report/__init__.py +5 -7
  355. endoreg_db/views/{pdf → report}/reimport.py +22 -22
  356. endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +46 -39
  357. endoreg_db/views/requirement/evaluate.py +188 -187
  358. endoreg_db/views/requirement/lookup.py +17 -3
  359. endoreg_db/views/requirement/lookup_store.py +22 -90
  360. endoreg_db/views/requirement/requirement_utils.py +89 -0
  361. endoreg_db/views/video/__init__.py +23 -24
  362. endoreg_db/views/video/correction.py +201 -172
  363. endoreg_db/views/video/reimport.py +1 -1
  364. endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +77 -40
  365. endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
  366. endoreg_db/views/video/video_stream.py +7 -8
  367. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +7 -3
  368. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +391 -413
  369. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
  370. endoreg_db/data/finding/anatomy_colon.yaml +0 -128
  371. endoreg_db/data/finding/colonoscopy.yaml +0 -40
  372. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
  373. endoreg_db/data/finding/complication.yaml +0 -16
  374. endoreg_db/data/finding/data.yaml +0 -105
  375. endoreg_db/data/finding/examination_setting.yaml +0 -16
  376. endoreg_db/data/finding/medication_related.yaml +0 -18
  377. endoreg_db/data/finding/outcome.yaml +0 -12
  378. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +0 -95
  379. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
  380. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
  381. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  382. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  383. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -68
  384. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  385. endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -80
  386. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
  387. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
  388. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
  389. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
  390. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
  391. endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
  392. endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
  393. endoreg_db/data/finding_classification/histology_colo.yaml +0 -51
  394. endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
  395. endoreg_db/data/finding_classification/medication_related.yaml +0 -23
  396. endoreg_db/data/finding_classification/visualized.yaml +0 -33
  397. endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  398. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  399. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  400. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  401. endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  402. endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  403. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  404. endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  405. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
  406. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  407. endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
  408. endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
  409. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  410. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
  411. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
  412. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  413. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  414. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
  415. endoreg_db/data/requirement/age.yaml +0 -26
  416. endoreg_db/data/requirement/gender.yaml +0 -25
  417. endoreg_db/management/commands/init_default_ai_model.py +0 -112
  418. endoreg_db/management/commands/reset_celery_schedule.py +0 -9
  419. endoreg_db/management/commands/validate_video.py +0 -204
  420. endoreg_db/migrations/0002_add_video_correction_models.py +0 -52
  421. endoreg_db/migrations/0003_add_center_display_name.py +0 -30
  422. endoreg_db/models/administration/permissions/__init__.py +0 -44
  423. endoreg_db/models/rule/__init__.py +0 -13
  424. endoreg_db/models/rule/rule.py +0 -27
  425. endoreg_db/models/rule/rule_applicator.py +0 -224
  426. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  427. endoreg_db/models/rule/rule_type.py +0 -20
  428. endoreg_db/models/rule/ruleset.py +0 -17
  429. endoreg_db/renames.yml +0 -8
  430. endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
  431. endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
  432. endoreg_db/serializers/_old/video.py +0 -71
  433. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
  434. endoreg_db/serializers/meta/report_meta.py +0 -53
  435. endoreg_db/serializers/report/__init__.py +0 -9
  436. endoreg_db/serializers/report/mixins.py +0 -45
  437. endoreg_db/serializers/report/report.py +0 -105
  438. endoreg_db/serializers/report/report_list.py +0 -22
  439. endoreg_db/serializers/report/secure_file_url.py +0 -26
  440. endoreg_db/serializers/video/video_metadata.py +0 -105
  441. endoreg_db/services/requirements_object.py +0 -147
  442. endoreg_db/services/storage_aware_video_processor.py +0 -344
  443. endoreg_db/urls/files.py +0 -6
  444. endoreg_db/urls/label_video_segment_validate.py +0 -33
  445. endoreg_db/urls/label_video_segments.py +0 -46
  446. endoreg_db/urls/report.py +0 -48
  447. endoreg_db/urls/video.py +0 -61
  448. endoreg_db/utils/case_generator/case_generator.py +0 -159
  449. endoreg_db/utils/case_generator/utils.py +0 -30
  450. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +0 -368
  451. endoreg_db/views/label/__init__.py +0 -5
  452. endoreg_db/views/label/label.py +0 -15
  453. endoreg_db/views/label_video_segment/__init__.py +0 -16
  454. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
  455. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
  456. endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
  457. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
  458. endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
  459. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
  460. endoreg_db/views/label_video_segment/validate.py +0 -226
  461. endoreg_db/views/media/segments.py +0 -71
  462. endoreg_db/views/meta/available_files_list.py +0 -146
  463. endoreg_db/views/meta/report_meta.py +0 -53
  464. endoreg_db/views/meta/sensitive_meta_detail.py +0 -148
  465. endoreg_db/views/misc/secure_file_serving_view.py +0 -80
  466. endoreg_db/views/misc/secure_file_url_view.py +0 -84
  467. endoreg_db/views/misc/secure_url_validate.py +0 -79
  468. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
  469. endoreg_db/views/patient_finding_location/__init__.py +0 -5
  470. endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
  471. endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
  472. endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
  473. endoreg_db/views/pdf/__init__.py +0 -8
  474. endoreg_db/views/report/report_list.py +0 -112
  475. endoreg_db/views/report/report_with_secure_url.py +0 -28
  476. endoreg_db/views/report/start_examination.py +0 -7
  477. endoreg_db/views/video/segmentation.py +0 -274
  478. endoreg_db/views/video/task_status.py +0 -49
  479. endoreg_db/views/video/timeline.py +0 -46
  480. endoreg_db/views/video/video_analyze.py +0 -52
  481. endoreg_db/views.py +0 -0
  482. /endoreg_db/data/requirement/{colonoscopy_baseline_austria.yaml → old/colonoscopy_baseline_austria.yaml} +0 -0
  483. /endoreg_db/data/requirement/{disease_cardiovascular.yaml → old/disease_cardiovascular.yaml} +0 -0
  484. /endoreg_db/data/requirement/{disease_classification_choice_cardiovascular.yaml → old/disease_classification_choice_cardiovascular.yaml} +0 -0
  485. /endoreg_db/data/requirement/{disease_hepatology.yaml → old/disease_hepatology.yaml} +0 -0
  486. /endoreg_db/data/requirement/{disease_misc.yaml → old/disease_misc.yaml} +0 -0
  487. /endoreg_db/data/requirement/{disease_renal.yaml → old/disease_renal.yaml} +0 -0
  488. /endoreg_db/data/requirement/{endoscopy_bleeding_risk.yaml → old/endoscopy_bleeding_risk.yaml} +0 -0
  489. /endoreg_db/data/requirement/{event_cardiology.yaml → old/event_cardiology.yaml} +0 -0
  490. /endoreg_db/data/requirement/{event_requirements.yaml → old/event_requirements.yaml} +0 -0
  491. /endoreg_db/data/requirement/{finding_colon_polyp.yaml → old/finding_colon_polyp.yaml} +0 -0
  492. /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
  493. /endoreg_db/data/requirement/{lab_value.yaml → old/lab_value.yaml} +0 -0
  494. /endoreg_db/data/requirement/{medication.yaml → old/medication.yaml} +0 -0
  495. /endoreg_db/data/requirement_operator/{age.yaml → _old/age.yaml} +0 -0
  496. /endoreg_db/data/requirement_operator/{lab_operators.yaml → _old/lab_operators.yaml} +0 -0
  497. /endoreg_db/data/requirement_operator/{model_operators.yaml → _old/model_operators.yaml} +0 -0
  498. /endoreg_db/{models/media/video/refactor_plan.md → import_files/pseudonymization/__init__.py} +0 -0
  499. /endoreg_db/{models/media/video/video_file_frames.py → import_files/pseudonymization/pseudonymize.py} +0 -0
  500. /endoreg_db/models/{metadata/frame_ocr_result.py → report/__init__.py} +0 -0
  501. /endoreg_db/{urls/sensitive_meta.py → models/report/images.py} +0 -0
  502. /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
  503. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,10 +1,12 @@
1
- from django.db import models
2
1
  from typing import TYPE_CHECKING
3
2
 
3
+ from django.db import models
4
4
 
5
5
  if TYPE_CHECKING:
6
- from .label_type import LabelType
7
6
  from .label_set import LabelSet
7
+ from .label_type import LabelType
8
+
9
+
8
10
  class LabelManager(models.Manager):
9
11
  """Manager class for handling Label model operations."""
10
12
 
@@ -25,16 +27,16 @@ class Label(models.Model):
25
27
  """
26
28
 
27
29
  name = models.CharField(max_length=255)
28
- label_type = models.ForeignKey(
29
- "LabelType", on_delete=models.CASCADE, related_name="labels", blank=True, null=True
30
- )
30
+ label_type = models.ForeignKey("LabelType", on_delete=models.CASCADE, related_name="labels", blank=True, null=True)
31
31
  description = models.TextField(blank=True, null=True)
32
32
 
33
33
  objects = LabelManager()
34
34
 
35
35
  if TYPE_CHECKING:
36
- label_type: "LabelType"
37
- label_sets: models.QuerySet["LabelSet"]
36
+ label_type: models.ForeignKey["LabelType|None"]
37
+
38
+ @property
39
+ def label_sets(self) -> models.QuerySet["LabelSet"]: ...
38
40
 
39
41
  def natural_key(self):
40
42
  """Return the natural key of this label"""
@@ -42,7 +44,7 @@ class Label(models.Model):
42
44
 
43
45
  def __str__(self):
44
46
  return str(self.name)
45
-
47
+
46
48
  @classmethod
47
49
  def get_outside_label(cls):
48
50
  """
@@ -57,7 +59,7 @@ class Label(models.Model):
57
59
  def get_low_quality_label(cls):
58
60
  """
59
61
  Retrieve the label instance with the name 'low_quality'.
60
-
62
+
61
63
  Raises:
62
64
  ValueError: If a label with the name 'low_quality' does not exist.
63
65
  """
@@ -65,19 +67,17 @@ class Label(models.Model):
65
67
  return cls.objects.get(name="low_quality")
66
68
  except Exception as exc:
67
69
  raise ValueError("'low_quality' label does not exist in the database") from exc
68
-
70
+
69
71
  @classmethod
70
- def get_or_create_from_name(cls, name:str):
72
+ def get_or_create_from_name(cls, name: str):
71
73
  """
72
74
  Retrieve or create a Label instance with the specified name.
73
-
75
+
74
76
  Parameters:
75
77
  name (str): The name of the label to retrieve or create.
76
-
78
+
77
79
  Returns:
78
80
  tuple: A tuple containing the Label instance and a boolean indicating whether the instance was created (True) or retrieved (False).
79
81
  """
80
82
  label, _created = cls.objects.get_or_create(name=name)
81
83
  return label, _created
82
-
83
-
@@ -1,6 +1,8 @@
1
- from django.db import models
2
1
  from typing import TYPE_CHECKING
3
2
 
3
+ from django.db import models
4
+
5
+
4
6
  class LabelSetManager(models.Manager):
5
7
  """
6
8
  Manager class for handling LabelSet model operations.
@@ -10,9 +12,17 @@ class LabelSetManager(models.Manager):
10
12
 
11
13
  """
12
14
 
13
- def get_by_natural_key(self, name):
14
- """Retrieves a LabelSet instance by its natural key (name)."""
15
- return self.get(name=name)
15
+ def get_by_natural_key(self, name, version=None):
16
+ """Retrieves a LabelSet instance by its natural key (name[, version])."""
17
+
18
+ queryset = self.filter(name=name)
19
+ if version not in (None, "", -1):
20
+ queryset = queryset.filter(version=version)
21
+
22
+ labelset = queryset.order_by("-version").first()
23
+ if not labelset:
24
+ raise self.model.DoesNotExist(f"LabelSet with name='{name}' and version='{version}' not found")
25
+ return labelset
16
26
 
17
27
 
18
28
  class LabelSet(models.Model):
@@ -33,12 +43,15 @@ class LabelSet(models.Model):
33
43
  objects = LabelSetManager()
34
44
 
35
45
  if TYPE_CHECKING:
46
+ from typing import cast
47
+
36
48
  from .label import Label
37
- labels: models.QuerySet["Label"]
49
+
50
+ labels = cast(models.manager.RelatedManager["Label"], labels)
38
51
 
39
52
  def natural_key(self):
40
53
  """Return the natural key of this label set"""
41
- return (self.name,)
54
+ return (self.name, self.version)
42
55
 
43
56
  def __str__(self) -> str:
44
57
  return str(self.name)
@@ -26,4 +26,4 @@ class LabelType(models.Model):
26
26
  return (self.name,)
27
27
 
28
28
  def __str__(self):
29
- return str(self.name)
29
+ return str(self.name)
@@ -1,7 +1,9 @@
1
1
  from typing import TYPE_CHECKING, Optional
2
2
 
3
3
  if TYPE_CHECKING:
4
- from endoreg_db.models import Label, VideoPredictionMeta, VideoFile
4
+ from endoreg_db.models import Label, VideoFile, VideoPredictionMeta
5
+
6
+ __all__ = ["_create_from_video"]
5
7
 
6
8
 
7
9
  def _create_from_video(
@@ -21,15 +23,10 @@ def _create_from_video(
21
23
  raise ValueError("Source must be a VideoFile instance.")
22
24
 
23
25
  if start_frame_number < 0 or end_frame_number < 0:
24
- raise ValueError(
25
- f"Frame numbers must be non-negative: start={start_frame_number}, end={end_frame_number}"
26
- )
26
+ raise ValueError(f"Frame numbers must be non-negative: start={start_frame_number}, end={end_frame_number}")
27
27
 
28
28
  if start_frame_number > end_frame_number:
29
- raise ValueError(
30
- f"Start frame number ({start_frame_number}) must be less than or equal to "
31
- f"end frame number ({end_frame_number})"
32
- )
29
+ raise ValueError(f"Start frame number ({start_frame_number}) must be less than or equal to end frame number ({end_frame_number})")
33
30
 
34
31
  segment = cls(
35
32
  start_frame_number=start_frame_number,
@@ -1,26 +1,29 @@
1
- from django.db import models
2
- from django.db.models import Q, CheckConstraint, F
3
- from typing import TYPE_CHECKING, Union, Optional, Tuple
4
- from tqdm import tqdm
5
1
  import logging
2
+ from typing import TYPE_CHECKING, Optional, Tuple, Union, cast
3
+
6
4
  from django.core.exceptions import ObjectDoesNotExist
5
+ from django.db import models
6
+ from django.db.models import CheckConstraint, F, Q
7
+ from tqdm import tqdm
8
+
7
9
  from ._create_from_video import _create_from_video
8
10
 
9
11
  logger = logging.getLogger(__name__)
10
12
 
11
13
  if TYPE_CHECKING:
12
14
  from endoreg_db.models import (
13
- LabelVideoSegmentState,
14
- VideoFile,
15
15
  Frame,
16
- Label,
16
+ ImageClassificationAnnotation,
17
17
  InformationSource,
18
+ Label,
19
+ LabelVideoSegmentState,
18
20
  ModelMeta,
19
- VideoPredictionMeta,
20
21
  PatientFinding,
21
- ImageClassificationAnnotation,
22
+ VideoFile,
23
+ VideoPredictionMeta,
22
24
  )
23
-
25
+
26
+
24
27
  class LabelVideoSegment(models.Model):
25
28
  """
26
29
  Represents a labeled segment within a video, defined by start and end frame numbers.
@@ -28,11 +31,10 @@ class LabelVideoSegment(models.Model):
28
31
  A segment must be associated with exactly one `VideoFile`.
29
32
  If it originates from a prediction, it links to a single `VideoPredictionMeta`.
30
33
  """
34
+
31
35
  start_frame_number = models.IntegerField()
32
36
  end_frame_number = models.IntegerField()
33
- source = models.ForeignKey(
34
- "InformationSource", on_delete=models.SET_NULL, null=True
35
- )
37
+ source = models.ForeignKey("InformationSource", on_delete=models.SET_NULL, null=True)
36
38
  label = models.ForeignKey("Label", on_delete=models.SET_NULL, null=True, blank=True)
37
39
 
38
40
  # Single ForeignKey to the unified VideoFile model
@@ -61,31 +63,29 @@ class LabelVideoSegment(models.Model):
61
63
  )
62
64
 
63
65
  if TYPE_CHECKING:
64
- video_file: "VideoFile"
65
- label: Optional["Label"]
66
- source: Optional["InformationSource"]
67
- prediction_meta: Optional["VideoPredictionMeta"]
68
- patient_findings: models.QuerySet["PatientFinding"]
69
- model_meta: Optional["ModelMeta"]
70
- state:"LabelVideoSegmentState"
66
+ video_file: models.ForeignKey["VideoFile"]
67
+ label: models.ForeignKey["Label|None"]
68
+ source: models.ForeignKey["InformationSource|None"]
69
+ prediction_meta: models.ForeignKey["VideoPredictionMeta|None"]
70
+
71
+ patient_findings = cast(models.manager.RelatedManager["PatientFinding"], patient_findings)
72
+ model_meta: models.ForeignKey["ModelMeta|None"]
73
+ state: models.OneToOneField["LabelVideoSegmentState"]
71
74
 
72
75
  class Meta:
73
76
  constraints = [
74
- CheckConstraint(
75
- condition=Q(start_frame_number__lt=F("end_frame_number")),
76
- name="segment_start_lt_end"
77
- ),
77
+ CheckConstraint(condition=Q(start_frame_number__lt=F("end_frame_number")), name="segment_start_lt_end"),
78
78
  ]
79
79
  indexes = [
80
- models.Index(fields=['video_file', 'label', 'start_frame_number']),
81
- models.Index(fields=['prediction_meta', 'label']),
80
+ models.Index(fields=["video_file", "label", "start_frame_number"]),
81
+ models.Index(fields=["prediction_meta", "label"]),
82
82
  ]
83
83
 
84
84
  @property
85
85
  def start_time(self) -> float:
86
86
  """
87
87
  Return the segment's start time in seconds, calculated from the start frame number and video FPS.
88
-
88
+
89
89
  Returns:
90
90
  float: Start time in seconds. Returns 0.0 if FPS is unavailable or zero.
91
91
  """
@@ -93,12 +93,12 @@ class LabelVideoSegment(models.Model):
93
93
  if fps == 0.0:
94
94
  return 0.0
95
95
  return self.start_frame_number / fps
96
-
96
+
97
97
  @property
98
98
  def end_time(self) -> float:
99
99
  """
100
100
  Return the segment's end time in seconds, calculated from the end frame number and video FPS.
101
-
101
+
102
102
  Returns:
103
103
  float: End time in seconds, or 0.0 if FPS is unavailable.
104
104
  """
@@ -131,6 +131,28 @@ class LabelVideoSegment(models.Model):
131
131
  # Should not happen if self.state exists and has the is_validated attribute.
132
132
  logger.error("AttributeError accessing 'state.is_validated' for LabelVideoSegment %s.", self.pk)
133
133
  return False
134
+
135
+ def mark_validated(
136
+ self,
137
+ is_validated: bool = True,
138
+ information_source_name: str = "frontend",
139
+ ) -> None:
140
+ """
141
+ Domain helper: update validation state (and optionally information source).
142
+ """
143
+ from endoreg_db.models import InformationSource # avoid import cycle
144
+
145
+ # ensure state exists
146
+ state, _ = self.get_or_create_state()
147
+ state.is_validated = is_validated
148
+ state.save()
149
+
150
+ # update information source
151
+ info_source, _ = InformationSource.objects.get_or_create(
152
+ name=information_source_name
153
+ )
154
+ self.source = info_source
155
+ self.save(update_fields=["source"])
134
156
 
135
157
  def extract_segment_frame_files(self, overwrite: bool = False, **kwargs) -> bool:
136
158
  """
@@ -138,55 +160,43 @@ class LabelVideoSegment(models.Model):
138
160
  Passes additional keyword arguments to extract_frames.
139
161
  """
140
162
  from endoreg_db.models import VideoFile
163
+
141
164
  if not isinstance(self.video_file, VideoFile):
142
165
  raise ValueError("Cannot extract frame files: No associated VideoFile.")
143
- return self.video_file.extract_specific_frame_range(
144
- start_frame=self.start_frame_number,
145
- end_frame=self.end_frame_number,
146
- overwrite=overwrite,
147
- **kwargs
148
- )
149
-
166
+ return self.video_file.extract_specific_frame_range(start_frame=self.start_frame_number, end_frame=self.end_frame_number, overwrite=overwrite, **kwargs)
167
+
150
168
  def delete_frame_files(self) -> None:
151
169
  """
152
170
  Delete the frame files corresponding to this segment's frame range from the associated video file.
153
-
171
+
154
172
  Raises:
155
173
  ValueError: If there is no associated VideoFile.
156
174
  """
157
175
  from endoreg_db.models import VideoFile
176
+
158
177
  if not isinstance(self.video_file, VideoFile):
159
178
  raise ValueError("Cannot delete frame files: No associated VideoFile.")
160
- self.video_file.delete_specific_frame_range(
161
- start_frame=self.start_frame_number,
162
- end_frame=self.end_frame_number
163
- )
179
+ self.video_file.delete_specific_frame_range(start_frame=self.start_frame_number, end_frame=self.end_frame_number)
180
+
164
181
  @classmethod
165
182
  def safe_create(cls, video_file, label, start_frame_number, end_frame_number, **kwargs):
166
183
  """
167
184
  Create a new LabelVideoSegment instance after validating the frame range.
168
-
185
+
169
186
  Validates that the provided start and end frame numbers are appropriate for the given video file before creating the segment. Raises a ValueError if validation fails.
170
-
187
+
171
188
  Returns:
172
- LabelVideoSegment: The newly created segment instance.
189
+ LabelVideoSegment: The newly created segment instance.
173
190
  """
174
191
  cls.validate_frame_range(start_frame_number, end_frame_number, video_file=video_file)
175
- return cls.objects.create(
176
- video_file=video_file,
177
- label=label,
178
- start_frame_number=start_frame_number,
179
- end_frame_number=end_frame_number,
180
- **kwargs
181
- )
182
-
192
+ return cls.objects.create(video_file=video_file, label=label, start_frame_number=start_frame_number, end_frame_number=end_frame_number, **kwargs)
193
+
183
194
  def save(self, *args, **kwargs):
184
195
  """
185
196
  Saves the LabelVideoSegment instance and ensures its associated state object exists.
186
-
197
+
187
198
  Overrides the default save behavior to guarantee that a related LabelVideoSegmentState is created or retrieved after saving.
188
199
  """
189
- from endoreg_db.models import LabelVideoSegmentState
190
200
  # Call the original save method first
191
201
  super().save(*args, **kwargs)
192
202
 
@@ -206,6 +216,7 @@ class LabelVideoSegment(models.Model):
206
216
  if it was created.
207
217
  """
208
218
  from endoreg_db.models import LabelVideoSegmentState
219
+
209
220
  state, created = LabelVideoSegmentState.objects.get_or_create(origin=self)
210
221
  return state, created
211
222
 
@@ -221,14 +232,7 @@ class LabelVideoSegment(models.Model):
221
232
  """
222
233
  Create a LabelVideoSegment instance from a VideoFile.
223
234
  """
224
- return _create_from_video(
225
- cls,
226
- source,
227
- prediction_meta,
228
- label,
229
- start_frame_number,
230
- end_frame_number
231
- )
235
+ return _create_from_video(cls, source, prediction_meta, label, start_frame_number, end_frame_number)
232
236
 
233
237
  def get_video(self) -> "VideoFile":
234
238
  """Returns the associated VideoFile instance."""
@@ -249,10 +253,7 @@ class LabelVideoSegment(models.Model):
249
253
  active_path = video_obj.active_file_path
250
254
  video_identifier = active_path.name if active_path else f"UUID {video_obj.uuid}"
251
255
 
252
- str_repr = (
253
- f"{video_identifier} Label - {label_name} - "
254
- f"{self.start_frame_number} - {self.end_frame_number}"
255
- )
256
+ str_repr = f"{video_identifier} Label - {label_name} - {self.start_frame_number} - {self.end_frame_number}"
256
257
  except ObjectDoesNotExist: # More specific exception
257
258
  str_repr = f"Segment {self.pk} (Error: Associated VideoFile missing)"
258
259
  except ValueError as e: # Catch specific error from get_video
@@ -266,7 +267,7 @@ class LabelVideoSegment(models.Model):
266
267
  def get_model_meta(self) -> Optional["ModelMeta"]:
267
268
  """
268
269
  Retrieve the associated ModelMeta object from the segment's prediction metadata, if available.
269
-
270
+
270
271
  Returns:
271
272
  ModelMeta or None: The related ModelMeta instance, or None if no prediction metadata is set.
272
273
  """
@@ -278,26 +279,24 @@ class LabelVideoSegment(models.Model):
278
279
  def frames(self) -> Union[models.QuerySet["Frame"], list]:
279
280
  """
280
281
  Return all frames within the segment's frame range.
281
-
282
+
282
283
  Returns:
283
284
  QuerySet[Frame] or list: Frames from the associated video file that fall within the segment's start and end frame numbers. Returns an empty list if the video file is unavailable.
284
285
  """
285
286
  return self.get_frames()
286
287
 
287
- def get_frames(self) -> Union[models.QuerySet["Frame"], list]:
288
+ def get_frames(self) -> models.QuerySet["Frame"]:
288
289
  """
289
290
  Retrieve all frames within the segment's frame range from the associated video.
290
-
291
+
291
292
  Returns:
292
293
  QuerySet[Frame]: Frames with frame numbers in [start_frame_number, end_frame_number) ordered by frame number, or an empty queryset if unavailable.
293
294
  """
294
295
  from endoreg_db.models.media.frame import Frame
296
+
295
297
  try:
296
298
  video_obj = self.get_video()
297
- return video_obj.frames.filter(
298
- frame_number__gte=self.start_frame_number,
299
- frame_number__lt=self.end_frame_number
300
- ).order_by('frame_number')
299
+ return video_obj.frames.filter(frame_number__gte=self.start_frame_number, frame_number__lt=self.end_frame_number).order_by("frame_number")
301
300
  except ValueError:
302
301
  logger.error("Cannot get frames for segment %s: No associated VideoFile.", self.pk)
303
302
  return Frame.objects.none()
@@ -309,7 +308,7 @@ class LabelVideoSegment(models.Model):
309
308
  def all_frame_annotations(self) -> models.QuerySet["ImageClassificationAnnotation"]:
310
309
  """
311
310
  Return all image classification annotations for frames within this segment that match the segment's label.
312
-
311
+
313
312
  Returns:
314
313
  QuerySet: ImageClassificationAnnotation objects for frames in the segment with the segment's label. Returns an empty queryset if the segment is not associated with a video.
315
314
  """
@@ -321,7 +320,7 @@ class LabelVideoSegment(models.Model):
321
320
  frame__video=video_obj, # Changed frame__video_file to frame__video
322
321
  frame__frame_number__gte=self.start_frame_number,
323
322
  frame__frame_number__lt=self.end_frame_number,
324
- label=self.label
323
+ label=self.label,
325
324
  )
326
325
  except ValueError:
327
326
  logger.error("Cannot get annotations for segment %s: No associated VideoFile.", self.pk)
@@ -331,7 +330,7 @@ class LabelVideoSegment(models.Model):
331
330
  def frame_predictions(self) -> models.QuerySet["ImageClassificationAnnotation"]:
332
331
  """
333
332
  Return prediction annotations for frames within this segment and matching the segment's label.
334
-
333
+
335
334
  Returns:
336
335
  QuerySet: ImageClassificationAnnotation objects for frames in the segment, filtered by label and information source type "prediction".
337
336
  """
@@ -344,17 +343,17 @@ class LabelVideoSegment(models.Model):
344
343
  frame__frame_number__gte=self.start_frame_number,
345
344
  frame__frame_number__lt=self.end_frame_number,
346
345
  label=self.label,
347
- information_source__information_source_types__name="prediction"
346
+ information_source__information_source_types__name="prediction",
348
347
  )
349
348
  except ValueError:
350
349
  logger.error("Cannot get predictions for segment %s: No associated VideoFile.", self.pk)
351
350
  return ImageClassificationAnnotation.objects.none()
352
-
351
+
353
352
  @property
354
353
  def manual_frame_annotations(self) -> models.QuerySet["ImageClassificationAnnotation"]:
355
354
  """
356
355
  Return manual image classification annotations for frames within this segment and matching the segment's label.
357
-
356
+
358
357
  Returns:
359
358
  QuerySet: Manual `ImageClassificationAnnotation` objects for the segment's frames and label. Returns an empty queryset if the segment is not associated with a video.
360
359
  """
@@ -367,7 +366,7 @@ class LabelVideoSegment(models.Model):
367
366
  frame__frame_number__gte=self.start_frame_number,
368
367
  frame__frame_number__lt=self.end_frame_number,
369
368
  label=self.label,
370
- information_source__information_source_types__name="manual_annotation"
369
+ information_source__information_source_types__name="manual_annotation",
371
370
  )
372
371
  except ValueError:
373
372
  logger.error("Cannot get manual annotations for segment %s: No associated VideoFile.", self.pk)
@@ -376,7 +375,7 @@ class LabelVideoSegment(models.Model):
376
375
  def get_segment_len_in_s(self) -> float:
377
376
  """
378
377
  Return the duration of the video segment in seconds, based on frame numbers and video FPS.
379
-
378
+
380
379
  Returns:
381
380
  float: Segment duration in seconds, or 0.0 if FPS is invalid or video is unavailable.
382
381
  """
@@ -406,10 +405,9 @@ class LabelVideoSegment(models.Model):
406
405
  logger.warning("Segment %s has no label. Cannot find frames without annotation.", self.pk)
407
406
  return []
408
407
 
409
- annotated_frame_ids = ImageClassificationAnnotation.objects.filter(
410
- frame__in=frames_qs.values_list('id', flat=True),
411
- label=self.label
412
- ).values_list('frame_id', flat=True)
408
+ annotated_frame_ids = ImageClassificationAnnotation.objects.filter(frame__in=frames_qs.values_list("id", flat=True), label=self.label).values_list(
409
+ "frame_id", flat=True
410
+ )
413
411
 
414
412
  frames_without_annotation = list(frames_qs.exclude(id__in=annotated_frame_ids)[:n_frames])
415
413
  return frames_without_annotation
@@ -417,11 +415,11 @@ class LabelVideoSegment(models.Model):
417
415
  def generate_annotations(self):
418
416
  """
419
417
  Creates image classification annotations for all frames in the segment if the segment is linked to a prediction, avoiding duplicates.
420
-
418
+
421
419
  Annotations are generated only if the segment has associated prediction metadata, model metadata, and label. Existing annotations for the same frame, label, model, and information source are not duplicated. Uses bulk creation for efficiency.
422
420
  """
423
421
  if not self.prediction_meta:
424
- logger.info("Skipping annotation generation for segment %s: Requires linked VideoPredictionMeta.", self.id)
422
+ logger.info("Skipping annotation generation for segment %s: Requires linked VideoPredictionMeta.", self.pk)
425
423
  return
426
424
 
427
425
  from endoreg_db.models import ImageClassificationAnnotation, InformationSource
@@ -434,27 +432,27 @@ class LabelVideoSegment(models.Model):
434
432
  label = self.label
435
433
 
436
434
  if not model_meta or not label:
437
- logger.warning("Missing model_meta or label for segment %s. Skipping annotation generation.", self.id)
435
+ logger.warning("Missing model_meta or label for segment %s. Skipping annotation generation.", self.pk)
438
436
  return
439
437
 
440
- frames_queryset = self.get_frames().only('id')
438
+ frames_queryset = self.get_frames().only("id")
441
439
  if not isinstance(frames_queryset, models.QuerySet):
442
- logger.error("Could not get frame queryset for segment %s. Skipping.", self.id)
440
+ logger.error("Could not get frame queryset for segment %s. Skipping.", self.pk)
443
441
  return
444
442
 
445
443
  existing_annotation_frame_ids = set(
446
444
  ImageClassificationAnnotation.objects.filter(
447
- frame_id__in=frames_queryset.values('id'),
445
+ frame_id__in=frames_queryset.values("id"),
448
446
  label=label,
449
447
  model_meta=model_meta,
450
448
  information_source=information_source,
451
- ).values_list('frame_id', flat=True)
449
+ ).values_list("frame_id", flat=True)
452
450
  )
453
451
 
454
452
  annotations_to_create = []
455
453
  frames_to_annotate = frames_queryset.exclude(id__in=existing_annotation_frame_ids)
456
454
 
457
- for frame in tqdm(frames_to_annotate.iterator(), total=frames_to_annotate.count(), desc=f"Preparing annotations for segment {self.id} ({label.name})"):
455
+ for frame in tqdm(frames_to_annotate.iterator(), total=frames_to_annotate.count(), desc=f"Preparing annotations for segment {self.pk} ({label.name})"):
458
456
  annotations_to_create.append(
459
457
  ImageClassificationAnnotation(
460
458
  frame=frame,
@@ -466,16 +464,16 @@ class LabelVideoSegment(models.Model):
466
464
  )
467
465
 
468
466
  if annotations_to_create:
469
- logger.info("Bulk creating %d annotations for segment %s...", len(annotations_to_create), self.id)
467
+ logger.info("Bulk creating %d annotations for segment %s...", len(annotations_to_create), self.pk)
470
468
  ImageClassificationAnnotation.objects.bulk_create(annotations_to_create, ignore_conflicts=True)
471
469
  logger.info("Bulk creation complete.")
472
470
  else:
473
- logger.info("No new annotations needed for segment %s.", self.id)
471
+ logger.info("No new annotations needed for segment %s.", self.pk)
474
472
 
475
473
  def _get_fps_safe(self):
476
474
  """
477
475
  Safely retrieves the frames per second (FPS) value from the associated video.
478
-
476
+
479
477
  Returns:
480
478
  float: The FPS of the associated video, or 0.0 if unavailable or invalid.
481
479
  """
@@ -488,12 +486,12 @@ class LabelVideoSegment(models.Model):
488
486
  def validate_frame_range(start_frame_number: int, end_frame_number: int, video_file=None):
489
487
  """
490
488
  Validate that the provided frame numbers define a valid segment range, optionally checking against a video's frame count.
491
-
489
+
492
490
  Parameters:
493
491
  start_frame_number (int): The starting frame number of the segment.
494
492
  end_frame_number (int): The ending frame number of the segment.
495
493
  video_file: Optional video file object to validate frame numbers against its frame count.
496
-
494
+
497
495
  Raises:
498
496
  ValueError: If frame numbers are not integers, are negative, are out of order, or exceed the video's frame count.
499
497
  """
@@ -504,8 +502,6 @@ class LabelVideoSegment(models.Model):
504
502
  if end_frame_number < start_frame_number:
505
503
  raise ValueError("end_frame_number must be equal or greater than start_frame_number.")
506
504
  if video_file is not None:
507
- frame_count = getattr(video_file, 'frame_count', None)
505
+ frame_count = getattr(video_file, "frame_count", None)
508
506
  if frame_count is not None and end_frame_number > frame_count:
509
507
  raise ValueError(f"end_frame_number ({end_frame_number}) exceeds video frame count ({frame_count}).")
510
-
511
-
@@ -1,12 +1,15 @@
1
1
  from django.db import models
2
2
 
3
+
3
4
  class VideoSegmentationLabelManager(models.Manager):
4
5
  """
5
6
  Manager for VideoSegmentationLabel with custom query methods.
6
7
  """
8
+
7
9
  def get_by_natural_key(self, name: str) -> "VideoSegmentationLabel":
8
10
  return self.get(name=name)
9
11
 
12
+
10
13
  class VideoSegmentationLabel(models.Model):
11
14
  """
12
15
  Represents a label for video segmentation annotations.
@@ -17,6 +20,7 @@ class VideoSegmentationLabel(models.Model):
17
20
  color (str): The color associated with the label.
18
21
  order_priority (int): The priority for ordering labels.
19
22
  """
23
+
20
24
  objects = VideoSegmentationLabelManager()
21
25
 
22
26
  name = models.CharField(max_length=255)
@@ -1,10 +1,11 @@
1
- from django.db import models
1
+ from typing import TYPE_CHECKING, cast
2
2
 
3
- from typing import TYPE_CHECKING
3
+ from django.db import models
4
4
 
5
5
  if TYPE_CHECKING:
6
6
  from endoreg_db.models import VideoSegmentationLabel
7
7
 
8
+
8
9
  class VideoSegmentationLabelSetManager(models.Manager):
9
10
  def get_by_natural_key(self, name):
10
11
  return self.get(name=name)
@@ -18,7 +19,7 @@ class VideoSegmentationLabelSet(models.Model):
18
19
  objects = VideoSegmentationLabelSetManager()
19
20
 
20
21
  if TYPE_CHECKING:
21
- labels: models.QuerySet["VideoSegmentationLabel"]
22
+ labels = cast(models.manager.RelatedManager["VideoSegmentationLabel"], labels)
22
23
 
23
24
  def natural_key(self):
24
25
  return (self.name,)