endoreg-db 0.8.9.2__py3-none-any.whl → 0.8.9.10__py3-none-any.whl

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

Potentially problematic release.


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

Files changed (450) hide show
  1. endoreg_db/admin.py +10 -5
  2. endoreg_db/apps.py +4 -7
  3. endoreg_db/authz/auth.py +1 -0
  4. endoreg_db/authz/backends.py +1 -1
  5. endoreg_db/authz/management/commands/list_routes.py +2 -0
  6. endoreg_db/authz/middleware.py +8 -7
  7. endoreg_db/authz/permissions.py +21 -10
  8. endoreg_db/authz/policy.py +14 -19
  9. endoreg_db/authz/views_auth.py +14 -10
  10. endoreg_db/codemods/rename_datetime_fields.py +8 -1
  11. endoreg_db/exceptions.py +5 -2
  12. endoreg_db/forms/__init__.py +0 -1
  13. endoreg_db/forms/examination_form.py +4 -3
  14. endoreg_db/forms/patient_finding_intervention_form.py +30 -8
  15. endoreg_db/forms/patient_form.py +9 -13
  16. endoreg_db/forms/questionnaires/__init__.py +1 -1
  17. endoreg_db/forms/settings/__init__.py +4 -1
  18. endoreg_db/forms/unit.py +2 -1
  19. endoreg_db/helpers/count_db.py +17 -14
  20. endoreg_db/helpers/default_objects.py +2 -1
  21. endoreg_db/helpers/download_segmentation_model.py +4 -3
  22. endoreg_db/helpers/interact.py +0 -5
  23. endoreg_db/helpers/test_video_helper.py +33 -25
  24. endoreg_db/import_files/__init__.py +1 -1
  25. endoreg_db/import_files/context/__init__.py +1 -1
  26. endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
  27. endoreg_db/import_files/context/ensure_center.py +4 -4
  28. endoreg_db/import_files/context/file_lock.py +3 -3
  29. endoreg_db/import_files/context/import_context.py +11 -12
  30. endoreg_db/import_files/context/validate_directories.py +1 -0
  31. endoreg_db/import_files/file_storage/create_report_file.py +57 -34
  32. endoreg_db/import_files/file_storage/create_video_file.py +64 -35
  33. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
  34. endoreg_db/import_files/file_storage/state_management.py +89 -122
  35. endoreg_db/import_files/file_storage/storage.py +5 -1
  36. endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
  37. endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
  38. endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
  39. endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
  40. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
  41. endoreg_db/import_files/report_import_service.py +36 -30
  42. endoreg_db/import_files/video_import_service.py +27 -23
  43. endoreg_db/logger_conf.py +56 -40
  44. endoreg_db/management/__init__.py +1 -1
  45. endoreg_db/management/commands/__init__.py +1 -1
  46. endoreg_db/management/commands/check_auth.py +45 -38
  47. endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
  48. endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
  49. endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
  50. endoreg_db/management/commands/fix_video_paths.py +75 -54
  51. endoreg_db/management/commands/import_report.py +1 -3
  52. endoreg_db/management/commands/list_routes.py +2 -0
  53. endoreg_db/management/commands/load_ai_model_data.py +8 -2
  54. endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
  55. endoreg_db/management/commands/load_center_data.py +3 -3
  56. endoreg_db/management/commands/load_distribution_data.py +35 -38
  57. endoreg_db/management/commands/load_endoscope_data.py +0 -3
  58. endoreg_db/management/commands/load_examination_data.py +20 -4
  59. endoreg_db/management/commands/load_finding_data.py +18 -3
  60. endoreg_db/management/commands/load_gender_data.py +17 -24
  61. endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
  62. endoreg_db/management/commands/load_information_source.py +0 -3
  63. endoreg_db/management/commands/load_lab_value_data.py +14 -3
  64. endoreg_db/management/commands/load_legacy_data.py +303 -0
  65. endoreg_db/management/commands/load_name_data.py +1 -2
  66. endoreg_db/management/commands/load_pdf_type_data.py +4 -8
  67. endoreg_db/management/commands/load_profession_data.py +0 -1
  68. endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
  69. endoreg_db/management/commands/load_requirement_data.py +6 -2
  70. endoreg_db/management/commands/load_unit_data.py +0 -4
  71. endoreg_db/management/commands/load_user_groups.py +5 -7
  72. endoreg_db/management/commands/model_input.py +169 -0
  73. endoreg_db/management/commands/register_ai_model.py +22 -16
  74. endoreg_db/management/commands/setup_endoreg_db.py +110 -32
  75. endoreg_db/management/commands/storage_management.py +14 -8
  76. endoreg_db/management/commands/summarize_db_content.py +154 -63
  77. endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
  78. endoreg_db/management/commands/validate_video_files.py +82 -50
  79. endoreg_db/management/commands/video_validation.py +4 -6
  80. endoreg_db/migrations/0001_initial.py +112 -63
  81. endoreg_db/models/__init__.py +8 -0
  82. endoreg_db/models/administration/ai/active_model.py +5 -5
  83. endoreg_db/models/administration/ai/ai_model.py +41 -18
  84. endoreg_db/models/administration/ai/model_type.py +1 -0
  85. endoreg_db/models/administration/case/case.py +22 -22
  86. endoreg_db/models/administration/center/__init__.py +5 -5
  87. endoreg_db/models/administration/center/center.py +6 -2
  88. endoreg_db/models/administration/center/center_resource.py +18 -4
  89. endoreg_db/models/administration/center/center_shift.py +3 -1
  90. endoreg_db/models/administration/center/center_waste.py +6 -2
  91. endoreg_db/models/administration/person/__init__.py +1 -1
  92. endoreg_db/models/administration/person/employee/__init__.py +1 -1
  93. endoreg_db/models/administration/person/employee/employee_type.py +3 -1
  94. endoreg_db/models/administration/person/examiner/__init__.py +1 -1
  95. endoreg_db/models/administration/person/examiner/examiner.py +10 -2
  96. endoreg_db/models/administration/person/names/first_name.py +6 -4
  97. endoreg_db/models/administration/person/names/last_name.py +4 -3
  98. endoreg_db/models/administration/person/patient/__init__.py +1 -1
  99. endoreg_db/models/administration/person/patient/patient.py +0 -1
  100. endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
  101. endoreg_db/models/administration/person/person.py +1 -1
  102. endoreg_db/models/administration/product/__init__.py +7 -6
  103. endoreg_db/models/administration/product/product.py +6 -2
  104. endoreg_db/models/administration/product/product_group.py +9 -7
  105. endoreg_db/models/administration/product/product_material.py +9 -2
  106. endoreg_db/models/administration/product/reference_product.py +64 -15
  107. endoreg_db/models/administration/qualification/qualification.py +3 -1
  108. endoreg_db/models/administration/shift/shift.py +3 -1
  109. endoreg_db/models/administration/shift/shift_type.py +12 -4
  110. endoreg_db/models/aidataset/__init__.py +5 -0
  111. endoreg_db/models/aidataset/aidataset.py +193 -0
  112. endoreg_db/models/label/__init__.py +1 -1
  113. endoreg_db/models/label/label.py +10 -2
  114. endoreg_db/models/label/label_set.py +3 -1
  115. endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
  116. endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
  117. endoreg_db/models/media/__init__.py +12 -5
  118. endoreg_db/models/media/frame/__init__.py +1 -1
  119. endoreg_db/models/media/frame/frame.py +34 -8
  120. endoreg_db/models/media/pdf/__init__.py +2 -1
  121. endoreg_db/models/media/pdf/raw_pdf.py +11 -4
  122. endoreg_db/models/media/pdf/report_file.py +6 -2
  123. endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
  124. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
  125. endoreg_db/models/media/video/create_from_file.py +20 -41
  126. endoreg_db/models/media/video/pipe_1.py +75 -30
  127. endoreg_db/models/media/video/pipe_2.py +37 -12
  128. endoreg_db/models/media/video/video_file.py +36 -24
  129. endoreg_db/models/media/video/video_file_ai.py +235 -70
  130. endoreg_db/models/media/video/video_file_anonymize.py +240 -65
  131. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
  132. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
  133. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
  134. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
  135. endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
  136. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
  137. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
  138. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
  139. endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
  140. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
  141. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
  142. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
  143. endoreg_db/models/media/video/video_file_io.py +85 -33
  144. endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
  145. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
  146. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
  147. endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
  148. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
  149. endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
  150. endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
  151. endoreg_db/models/media/video/video_file_segments.py +118 -27
  152. endoreg_db/models/media/video/video_metadata.py +25 -6
  153. endoreg_db/models/media/video/video_processing.py +54 -15
  154. endoreg_db/models/medical/__init__.py +3 -13
  155. endoreg_db/models/medical/contraindication/__init__.py +3 -1
  156. endoreg_db/models/medical/disease.py +18 -6
  157. endoreg_db/models/medical/event.py +6 -2
  158. endoreg_db/models/medical/examination/__init__.py +5 -1
  159. endoreg_db/models/medical/examination/examination.py +22 -6
  160. endoreg_db/models/medical/examination/examination_indication.py +23 -7
  161. endoreg_db/models/medical/examination/examination_time.py +6 -2
  162. endoreg_db/models/medical/finding/__init__.py +3 -1
  163. endoreg_db/models/medical/finding/finding.py +37 -12
  164. endoreg_db/models/medical/finding/finding_classification.py +27 -8
  165. endoreg_db/models/medical/finding/finding_intervention.py +19 -6
  166. endoreg_db/models/medical/finding/finding_type.py +3 -1
  167. endoreg_db/models/medical/hardware/__init__.py +1 -1
  168. endoreg_db/models/medical/hardware/endoscope.py +14 -2
  169. endoreg_db/models/medical/laboratory/__init__.py +1 -1
  170. endoreg_db/models/medical/laboratory/lab_value.py +139 -39
  171. endoreg_db/models/medical/medication/__init__.py +7 -3
  172. endoreg_db/models/medical/medication/medication.py +3 -1
  173. endoreg_db/models/medical/medication/medication_indication.py +3 -1
  174. endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
  175. endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
  176. endoreg_db/models/medical/medication/medication_schedule.py +3 -1
  177. endoreg_db/models/medical/patient/__init__.py +2 -10
  178. endoreg_db/models/medical/patient/medication_examples.py +3 -14
  179. endoreg_db/models/medical/patient/patient_disease.py +17 -5
  180. endoreg_db/models/medical/patient/patient_event.py +12 -4
  181. endoreg_db/models/medical/patient/patient_examination.py +52 -15
  182. endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
  183. endoreg_db/models/medical/patient/patient_finding.py +105 -29
  184. endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
  185. endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
  186. endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
  187. endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
  188. endoreg_db/models/medical/patient/patient_medication.py +25 -7
  189. endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
  190. endoreg_db/models/metadata/model_meta.py +40 -12
  191. endoreg_db/models/metadata/model_meta_logic.py +51 -16
  192. endoreg_db/models/metadata/sensitive_meta.py +65 -28
  193. endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
  194. endoreg_db/models/metadata/video_meta.py +146 -39
  195. endoreg_db/models/metadata/video_prediction_logic.py +70 -21
  196. endoreg_db/models/metadata/video_prediction_meta.py +80 -27
  197. endoreg_db/models/operation_log.py +63 -0
  198. endoreg_db/models/other/__init__.py +10 -10
  199. endoreg_db/models/other/distribution/__init__.py +9 -7
  200. endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
  201. endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
  202. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
  203. endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
  204. endoreg_db/models/other/emission/__init__.py +1 -1
  205. endoreg_db/models/other/emission/emission_factor.py +9 -3
  206. endoreg_db/models/other/information_source.py +15 -5
  207. endoreg_db/models/other/material.py +3 -1
  208. endoreg_db/models/other/transport_route.py +3 -1
  209. endoreg_db/models/other/unit.py +6 -2
  210. endoreg_db/models/report/report.py +0 -1
  211. endoreg_db/models/requirement/requirement.py +84 -27
  212. endoreg_db/models/requirement/requirement_error.py +5 -6
  213. endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
  214. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
  215. endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
  216. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
  217. endoreg_db/models/requirement/requirement_operator.py +28 -8
  218. endoreg_db/models/requirement/requirement_set.py +34 -11
  219. endoreg_db/models/state/__init__.py +1 -0
  220. endoreg_db/models/state/audit_ledger.py +9 -2
  221. endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
  222. endoreg_db/models/state/processing_history/processing_history.py +136 -0
  223. endoreg_db/models/state/raw_pdf.py +0 -1
  224. endoreg_db/models/state/video.py +2 -4
  225. endoreg_db/models/utils.py +4 -2
  226. endoreg_db/queries/__init__.py +2 -6
  227. endoreg_db/queries/annotations/__init__.py +1 -3
  228. endoreg_db/queries/annotations/legacy.py +37 -26
  229. endoreg_db/root_urls.py +3 -4
  230. endoreg_db/schemas/examination_evaluation.py +3 -0
  231. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
  232. endoreg_db/serializers/__init__.py +2 -8
  233. endoreg_db/serializers/administration/__init__.py +1 -2
  234. endoreg_db/serializers/administration/ai/__init__.py +0 -1
  235. endoreg_db/serializers/administration/ai/active_model.py +3 -1
  236. endoreg_db/serializers/administration/ai/ai_model.py +5 -3
  237. endoreg_db/serializers/administration/ai/model_type.py +3 -1
  238. endoreg_db/serializers/administration/center.py +7 -2
  239. endoreg_db/serializers/administration/gender.py +4 -2
  240. endoreg_db/serializers/anonymization.py +13 -13
  241. endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
  242. endoreg_db/serializers/examination/__init__.py +1 -1
  243. endoreg_db/serializers/examination/base.py +12 -13
  244. endoreg_db/serializers/examination/dropdown.py +6 -7
  245. endoreg_db/serializers/examination_serializer.py +3 -6
  246. endoreg_db/serializers/finding/__init__.py +1 -1
  247. endoreg_db/serializers/finding/finding.py +14 -7
  248. endoreg_db/serializers/finding_classification/__init__.py +3 -3
  249. endoreg_db/serializers/finding_classification/choice.py +3 -3
  250. endoreg_db/serializers/finding_classification/classification.py +2 -4
  251. endoreg_db/serializers/label_video_segment/__init__.py +5 -3
  252. endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
  253. endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
  254. endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
  255. endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
  256. endoreg_db/serializers/meta/__init__.py +1 -2
  257. endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
  258. endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
  259. endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
  260. endoreg_db/serializers/misc/__init__.py +2 -2
  261. endoreg_db/serializers/misc/file_overview.py +11 -7
  262. endoreg_db/serializers/misc/stats.py +10 -8
  263. endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
  264. endoreg_db/serializers/misc/upload_job.py +32 -29
  265. endoreg_db/serializers/patient/__init__.py +2 -1
  266. endoreg_db/serializers/patient/patient.py +32 -15
  267. endoreg_db/serializers/patient/patient_dropdown.py +11 -3
  268. endoreg_db/serializers/patient_examination/__init__.py +1 -1
  269. endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
  270. endoreg_db/serializers/patient_finding/__init__.py +1 -1
  271. endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
  272. endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
  273. endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
  274. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
  275. endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
  276. endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
  277. endoreg_db/serializers/pdf/__init__.py +1 -3
  278. endoreg_db/serializers/requirements/requirement_schema.py +1 -6
  279. endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
  280. endoreg_db/serializers/video/__init__.py +2 -2
  281. endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
  282. endoreg_db/serializers/video/video_file_brief.py +6 -2
  283. endoreg_db/serializers/video/video_file_detail.py +36 -23
  284. endoreg_db/serializers/video/video_file_list.py +4 -2
  285. endoreg_db/serializers/video/video_processing_history.py +54 -50
  286. endoreg_db/services/__init__.py +1 -1
  287. endoreg_db/services/anonymization.py +2 -2
  288. endoreg_db/services/examination_evaluation.py +40 -17
  289. endoreg_db/services/model_meta_from_hf.py +76 -0
  290. endoreg_db/services/polling_coordinator.py +101 -70
  291. endoreg_db/services/pseudonym_service.py +27 -22
  292. endoreg_db/services/report_import.py +6 -3
  293. endoreg_db/services/segment_sync.py +75 -59
  294. endoreg_db/services/video_import.py +6 -7
  295. endoreg_db/urls/__init__.py +2 -2
  296. endoreg_db/urls/ai.py +7 -25
  297. endoreg_db/urls/anonymization.py +61 -15
  298. endoreg_db/urls/auth.py +4 -4
  299. endoreg_db/urls/classification.py +4 -9
  300. endoreg_db/urls/examination.py +27 -18
  301. endoreg_db/urls/media.py +27 -34
  302. endoreg_db/urls/patient.py +11 -7
  303. endoreg_db/urls/requirements.py +3 -1
  304. endoreg_db/urls/root_urls.py +2 -3
  305. endoreg_db/urls/stats.py +24 -16
  306. endoreg_db/urls/upload.py +3 -11
  307. endoreg_db/utils/__init__.py +14 -15
  308. endoreg_db/utils/ai/__init__.py +1 -1
  309. endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
  310. endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
  311. endoreg_db/utils/ai/get.py +2 -1
  312. endoreg_db/utils/ai/inference_dataset.py +14 -15
  313. endoreg_db/utils/ai/model_training/config.py +117 -0
  314. endoreg_db/utils/ai/model_training/dataset.py +74 -0
  315. endoreg_db/utils/ai/model_training/losses.py +68 -0
  316. endoreg_db/utils/ai/model_training/metrics.py +78 -0
  317. endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
  318. endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
  319. endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
  320. endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
  321. endoreg_db/utils/ai/predict.py +4 -4
  322. endoreg_db/utils/ai/preprocess.py +19 -11
  323. endoreg_db/utils/calc_duration_seconds.py +4 -4
  324. endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
  325. endoreg_db/utils/check_video_files.py +74 -47
  326. endoreg_db/utils/cropping.py +10 -9
  327. endoreg_db/utils/dataloader.py +11 -3
  328. endoreg_db/utils/dates.py +3 -4
  329. endoreg_db/utils/defaults/set_default_center.py +7 -6
  330. endoreg_db/utils/env.py +6 -2
  331. endoreg_db/utils/extract_specific_frames.py +24 -9
  332. endoreg_db/utils/file_operations.py +30 -18
  333. endoreg_db/utils/fix_video_path_direct.py +57 -41
  334. endoreg_db/utils/frame_anonymization_utils.py +157 -157
  335. endoreg_db/utils/hashs.py +3 -18
  336. endoreg_db/utils/links/requirement_link.py +96 -52
  337. endoreg_db/utils/ocr.py +30 -25
  338. endoreg_db/utils/operation_log.py +61 -0
  339. endoreg_db/utils/parse_and_generate_yaml.py +12 -13
  340. endoreg_db/utils/paths.py +6 -6
  341. endoreg_db/utils/permissions.py +40 -24
  342. endoreg_db/utils/pipelines/process_video_dir.py +50 -26
  343. endoreg_db/utils/product/sum_emissions.py +5 -3
  344. endoreg_db/utils/product/sum_weights.py +4 -2
  345. endoreg_db/utils/pydantic_models/__init__.py +3 -4
  346. endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
  347. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
  348. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
  349. endoreg_db/utils/setup_config.py +21 -5
  350. endoreg_db/utils/storage.py +3 -1
  351. endoreg_db/utils/translation.py +19 -15
  352. endoreg_db/utils/uuid.py +1 -0
  353. endoreg_db/utils/validate_endo_roi.py +12 -4
  354. endoreg_db/utils/validate_subcategory_dict.py +26 -24
  355. endoreg_db/utils/validate_video_detailed.py +207 -149
  356. endoreg_db/utils/video/__init__.py +7 -3
  357. endoreg_db/utils/video/extract_frames.py +30 -18
  358. endoreg_db/utils/video/names.py +11 -6
  359. endoreg_db/utils/video/streaming_processor.py +175 -101
  360. endoreg_db/utils/video/video_splitter.py +30 -19
  361. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
  362. endoreg_db/views/__init__.py +0 -20
  363. endoreg_db/views/anonymization/__init__.py +6 -2
  364. endoreg_db/views/anonymization/media_management.py +2 -6
  365. endoreg_db/views/anonymization/overview.py +34 -1
  366. endoreg_db/views/anonymization/validate.py +79 -18
  367. endoreg_db/views/auth/__init__.py +1 -1
  368. endoreg_db/views/auth/keycloak.py +16 -14
  369. endoreg_db/views/examination/__init__.py +12 -15
  370. endoreg_db/views/examination/examination.py +5 -5
  371. endoreg_db/views/examination/examination_manifest_cache.py +5 -5
  372. endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
  373. endoreg_db/views/examination/get_finding_classifications.py +9 -7
  374. endoreg_db/views/examination/get_findings.py +8 -10
  375. endoreg_db/views/examination/get_instruments.py +3 -2
  376. endoreg_db/views/examination/get_interventions.py +1 -1
  377. endoreg_db/views/finding/__init__.py +2 -2
  378. endoreg_db/views/finding/finding.py +58 -54
  379. endoreg_db/views/finding/get_classifications.py +1 -1
  380. endoreg_db/views/finding/get_interventions.py +1 -1
  381. endoreg_db/views/finding_classification/__init__.py +5 -5
  382. endoreg_db/views/finding_classification/finding_classification.py +5 -6
  383. endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
  384. endoreg_db/views/media/__init__.py +13 -13
  385. endoreg_db/views/media/pdf_media.py +9 -9
  386. endoreg_db/views/media/sensitive_metadata.py +10 -7
  387. endoreg_db/views/media/video_media.py +4 -4
  388. endoreg_db/views/meta/__init__.py +1 -1
  389. endoreg_db/views/meta/sensitive_meta_list.py +20 -22
  390. endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
  391. endoreg_db/views/misc/__init__.py +6 -34
  392. endoreg_db/views/misc/center.py +2 -1
  393. endoreg_db/views/misc/csrf.py +2 -1
  394. endoreg_db/views/misc/gender.py +2 -1
  395. endoreg_db/views/misc/stats.py +141 -106
  396. endoreg_db/views/patient/__init__.py +1 -3
  397. endoreg_db/views/patient/patient.py +141 -99
  398. endoreg_db/views/patient_examination/__init__.py +5 -5
  399. endoreg_db/views/patient_examination/patient_examination.py +43 -42
  400. endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
  401. endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
  402. endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
  403. endoreg_db/views/patient_examination/video.py +114 -80
  404. endoreg_db/views/patient_finding/__init__.py +1 -1
  405. endoreg_db/views/patient_finding/patient_finding.py +17 -10
  406. endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
  407. endoreg_db/views/patient_finding_classification/__init__.py +1 -1
  408. endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
  409. endoreg_db/views/report/reimport.py +1 -1
  410. endoreg_db/views/report/report_stream.py +5 -8
  411. endoreg_db/views/requirement/__init__.py +2 -1
  412. endoreg_db/views/requirement/evaluate.py +7 -9
  413. endoreg_db/views/requirement/lookup.py +2 -3
  414. endoreg_db/views/requirement/lookup_store.py +0 -1
  415. endoreg_db/views/requirement/requirement_utils.py +2 -4
  416. endoreg_db/views/stats/__init__.py +4 -4
  417. endoreg_db/views/stats/stats_views.py +152 -115
  418. endoreg_db/views/video/__init__.py +18 -27
  419. endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
  420. endoreg_db/views/{ai → video/ai}/label.py +20 -16
  421. endoreg_db/views/video/correction.py +5 -6
  422. endoreg_db/views/video/reimport.py +134 -99
  423. endoreg_db/views/video/segments_crud.py +134 -44
  424. endoreg_db/views/video/video_apply_mask.py +13 -12
  425. endoreg_db/views/video/video_correction.py +2 -1
  426. endoreg_db/views/video/video_download_processed.py +15 -15
  427. endoreg_db/views/video/video_meta_stats.py +7 -6
  428. endoreg_db/views/video/video_processing_history.py +3 -2
  429. endoreg_db/views/video/video_remove_frames.py +13 -12
  430. endoreg_db/views/video/video_stream.py +110 -82
  431. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
  432. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +434 -431
  433. endoreg_db/management/commands/import_fallback_video.py +0 -203
  434. endoreg_db/management/commands/import_video.py +0 -422
  435. endoreg_db/management/commands/import_video_with_classification.py +0 -367
  436. endoreg_db/models/media/processing_history/processing_history.py +0 -96
  437. endoreg_db/serializers/label/__init__.py +0 -7
  438. endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
  439. endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
  440. endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
  441. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
  442. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
  443. endoreg_db/services/__old/pdf_import.py +0 -1487
  444. endoreg_db/services/__old/video_import.py +0 -1306
  445. endoreg_db/tasks/upload_tasks.py +0 -216
  446. endoreg_db/tasks/video_ingest.py +0 -161
  447. endoreg_db/tasks/video_processing_tasks.py +0 -327
  448. endoreg_db/views/misc/translation.py +0 -182
  449. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
  450. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
@@ -1,367 +0,0 @@
1
- """
2
- Management command to import a video file and automatically run NICE/PARIS classifications.
3
- This command extends the basic import_video functionality with automatic classification.
4
- """
5
- from django.core.management import BaseCommand, call_command
6
- from django.db import connection
7
- from pathlib import Path
8
- from endoreg_db.models import VideoFile, ModelMeta
9
- from endoreg_db.models.administration.center import Center
10
- from endoreg_db.models.medical.hardware import EndoscopyProcessor
11
-
12
- # TODO Migrate
13
- from endoreg_db.serializers.Frames_NICE_and_PARIS_classifications import (
14
- ForNiceClassificationSerializer,
15
- ForParisClassificationSerializer
16
- )
17
-
18
- from ...helpers.default_objects import (
19
- get_latest_segmentation_model
20
- )
21
-
22
- from endoreg_db.utils.video.ffmpeg_wrapper import check_ffmpeg_availability
23
-
24
- from endoreg_db.helpers.data_loader import (
25
- load_disease_data,
26
- load_gender_data,
27
- load_event_data,
28
- load_information_source,
29
- load_examination_data,
30
- load_center_data,
31
- load_endoscope_data,
32
- load_ai_model_label_data,
33
- load_ai_model_data,
34
- load_default_ai_model
35
- )
36
-
37
-
38
- class Command(BaseCommand):
39
- help = """
40
- Imports a video file and automatically runs NICE/PARIS classifications.
41
-
42
- Workflow:
43
- 1. Validates dependencies (FFMPEG, center, processor)
44
- 2. Loads reference data
45
- 3. Imports video using VideoFile.create_from_file_initialized()
46
- 4. Runs VideoFile.pipe_1() for AI segmentation
47
- 5. Automatically runs NICE classification
48
- 6. Automatically runs PARIS classification (if frame_dir available)
49
- 7. Reports results of all steps
50
- """
51
-
52
- def add_arguments(self, parser):
53
- parser.add_argument(
54
- "--verbose",
55
- action="store_true",
56
- help="Display verbose output",
57
- )
58
- parser.add_argument(
59
- "--center_name",
60
- type=str,
61
- default="university_hospital_wuerzburg",
62
- help="Name of the center to associate with video",
63
- )
64
-
65
- parser.add_argument(
66
- "video_file",
67
- type=Path,
68
- help="Path to the video file to import",
69
- )
70
-
71
- parser.add_argument(
72
- "--frame_dir_root",
73
- type=str,
74
- default="~/test-data/raw_frame_dir",
75
- help="Path to the frame directory",
76
- )
77
-
78
- parser.add_argument(
79
- "--video_dir_root",
80
- type=str,
81
- default="~/test-data/raw_video_dir",
82
- help="Path to the video directory",
83
- )
84
-
85
- parser.add_argument(
86
- "--delete_source",
87
- action="store_true",
88
- default=False,
89
- help="Delete the source video file after importing",
90
- )
91
-
92
- parser.add_argument(
93
- "--save_video_file",
94
- action="store_true",
95
- default=True,
96
- help="Save the video file to the video directory",
97
- )
98
-
99
- parser.add_argument(
100
- "--model_path",
101
- type=str,
102
- default="./data/models/colo_segmentation_RegNetX800MF_6.safetensors",
103
- help="Path to the model file",
104
- )
105
-
106
- parser.add_argument(
107
- "--segmentation",
108
- action="store_true",
109
- help="Whether to use segmentation",
110
- )
111
-
112
- parser.add_argument(
113
- "--processor_name",
114
- type=str,
115
- default="olympus_cv_1500",
116
- help="Name of the processor to associate with video",
117
- )
118
-
119
- # NEW: Classification options
120
- parser.add_argument(
121
- "--skip_classification",
122
- action="store_true",
123
- default=False,
124
- help="Skip automatic NICE/PARIS classification after import",
125
- )
126
-
127
- parser.add_argument(
128
- "--classification_only",
129
- type=str,
130
- choices=['nice', 'paris', 'both'],
131
- default='both',
132
- help="Which classification types to run (default: both)",
133
- )
134
-
135
- def handle(self, *args, **options):
136
- """
137
- Main command handler that orchestrates the complete workflow.
138
- """
139
- # Check FFMPEG availability
140
- try:
141
- check_ffmpeg_availability()
142
- self.stdout.write(self.style.SUCCESS("✓ FFMPEG is available"))
143
- except FileNotFoundError as e:
144
- self.stderr.write(self.style.ERROR(f"✗ FFMPEG not found: {str(e)}"))
145
- return
146
-
147
- self.stdout.write(f"Database: {connection.alias}")
148
- self.stdout.write(self.style.SUCCESS("=== Starting Video Import and Classification Workflow ==="))
149
-
150
- # Load reference data
151
- self._load_reference_data()
152
-
153
- # Parse options
154
- segmentation = options["segmentation"]
155
- skip_classification = options["skip_classification"]
156
- classification_only = options["classification_only"]
157
-
158
- # Get AI model if segmentation is enabled
159
- self.ai_model_meta = None
160
- if segmentation:
161
- load_ai_model_label_data()
162
- load_ai_model_data()
163
- load_default_ai_model()
164
- try:
165
- self.ai_model_meta = get_latest_segmentation_model()
166
- except ModelMeta.DoesNotExist as exc:
167
- raise AssertionError("No ModelMeta found for the latest default segmentation AiModel") from exc
168
-
169
- # Validate input parameters
170
- video_file = Path(options["video_file"]).expanduser()
171
- if not video_file.exists():
172
- self.stdout.write(self.style.ERROR(f"✗ Video file not found: {video_file}"))
173
- return
174
-
175
- # Validate center and processor
176
- center, processor = self._validate_center_and_processor(options)
177
- if not center or not processor:
178
- return
179
-
180
- # Step 1: Import Video
181
- self.stdout.write(self.style.SUCCESS("\n=== Step 1: Importing Video ==="))
182
- video_file_obj = self._import_video(options, video_file)
183
- if not video_file_obj:
184
- return
185
-
186
- # Step 2: Run Pipeline 1 (AI Segmentation)
187
- self.stdout.write(self.style.SUCCESS("\n=== Step 2: Running AI Segmentation (Pipeline 1) ==="))
188
- pipeline_success = self._run_pipeline_1(video_file_obj)
189
- if not pipeline_success:
190
- self.stdout.write(self.style.WARNING("⚠ Pipeline 1 failed, but continuing with classification..."))
191
-
192
- # Step 3: Run Classifications (if not skipped)
193
- if not skip_classification:
194
- self.stdout.write(self.style.SUCCESS("\n=== Step 3: Running Polyp Classifications ==="))
195
- self._run_classifications(video_file_obj, classification_only)
196
- else:
197
- self.stdout.write(self.style.WARNING("⚠ Skipping classifications as requested"))
198
-
199
- self.stdout.write(self.style.SUCCESS("\n=== Workflow Complete ==="))
200
- self.stdout.write(f"✓ Video imported with ID: {video_file_obj.id}")
201
- self.stdout.write(f"✓ Video available at: {video_file_obj.processed_file}")
202
-
203
- def _load_reference_data(self):
204
- """Load all necessary reference data."""
205
- self.stdout.write("Loading reference data...")
206
- load_gender_data()
207
- load_disease_data()
208
- load_event_data()
209
- load_information_source()
210
- load_examination_data()
211
- load_center_data()
212
- load_endoscope_data()
213
- self.stdout.write(self.style.SUCCESS("✓ Reference data loaded"))
214
-
215
- def _validate_center_and_processor(self, options):
216
- """Validate that center and processor exist."""
217
- center_name = options["center_name"]
218
- processor_name = options["processor_name"]
219
-
220
- # Validate center
221
- try:
222
- center = Center.objects.get(name=center_name)
223
- self.stdout.write(self.style.SUCCESS(f"✓ Using center: {center.name_en}"))
224
- except Center.DoesNotExist:
225
- self.stdout.write(self.style.ERROR(f"✗ Center not found: {center_name}"))
226
- return None, None
227
-
228
- # Validate processor
229
- try:
230
- processors_qs = EndoscopyProcessor.objects.filter(centers=center)
231
- proc_count = processors_qs.count()
232
-
233
- if proc_count == 0:
234
- processor = EndoscopyProcessor.objects.filter(name=processor_name).first()
235
- if processor is None:
236
- self.stderr.write(self.style.ERROR(f"✗ No processors found for center or fallback"))
237
- return None, None
238
- processor.centers.add(center)
239
- self.stdout.write(self.style.WARNING(f"⚠ Linked fallback processor '{processor.name}' to center"))
240
- elif proc_count == 1:
241
- processor = processors_qs.first()
242
- else:
243
- processor = self._choose_processor_interactively(processors_qs)
244
-
245
- self.stdout.write(self.style.SUCCESS(f"✓ Using processor: {processor.name}"))
246
- return center, processor
247
-
248
- except EndoscopyProcessor.DoesNotExist:
249
- self.stdout.write(self.style.ERROR(f"✗ Processor not found: {processor_name}"))
250
- return None, None
251
-
252
- def _import_video(self, options, video_file):
253
- """Import the video file into the database."""
254
- try:
255
- video_file_obj = VideoFile.create_from_file_initialized(
256
- file_path=video_file,
257
- center_name=options["center_name"],
258
- processor_name=options["processor_name"],
259
- delete_source=options["delete_source"],
260
- save_video_file=options["save_video_file"],
261
- )
262
-
263
- if video_file_obj:
264
- self.stdout.write(self.style.SUCCESS(f"✓ Video imported successfully (ID: {video_file_obj.id})"))
265
- return video_file_obj
266
- else:
267
- self.stdout.write(self.style.ERROR("✗ Failed to create VideoFile instance"))
268
- return None
269
-
270
- except Exception as e:
271
- self.stdout.write(self.style.ERROR(f"✗ Error importing video: {str(e)}"))
272
- return None
273
-
274
- def _run_pipeline_1(self, video_file_obj):
275
- """Run the AI segmentation pipeline."""
276
- try:
277
- if self.ai_model_meta:
278
- success = video_file_obj.pipe_1(model_name=self.ai_model_meta.model.name)
279
- else:
280
- # Get the default model meta if segmentation is not enabled
281
- ai_model_meta = ModelMeta.objects.filter(
282
- model__name="colo_segmentation_RegNetX800MF_6"
283
- ).first()
284
-
285
- if ai_model_meta:
286
- success = video_file_obj.pipe_1(model_name=ai_model_meta.model.name)
287
- else:
288
- success = video_file_obj.pipe_1(model_name="colo_segmentation_RegNetX800MF_6")
289
-
290
- if success:
291
- self.stdout.write(self.style.SUCCESS("✓ Pipeline 1 completed successfully"))
292
- return True
293
- else:
294
- self.stdout.write(self.style.ERROR("✗ Pipeline 1 failed"))
295
- return False
296
-
297
- except Exception as e:
298
- self.stdout.write(self.style.ERROR(f"✗ Pipeline 1 error: {str(e)}"))
299
- return False
300
-
301
- def _run_classifications(self, video_file_obj, classification_only):
302
- """Run NICE and/or PARIS classifications."""
303
- video_list = [video_file_obj]
304
-
305
- # Run NICE Classification
306
- if classification_only in ['nice', 'both']:
307
- self.stdout.write("Running NICE classification...")
308
- try:
309
- nice_serializer = ForNiceClassificationSerializer()
310
- nice_results = nice_serializer.to_representation(video_list)
311
-
312
- if nice_results and isinstance(nice_results, dict) and 'data' in nice_results:
313
- segments_count = sum(len(item.get('frames', [])) for item in nice_results['data'] if 'frames' in item)
314
- self.stdout.write(self.style.SUCCESS(f"✓ NICE classification completed ({segments_count} segments found)"))
315
- else:
316
- self.stdout.write(self.style.WARNING("⚠ NICE classification completed but no valid segments found"))
317
-
318
- except Exception as e:
319
- self.stdout.write(self.style.ERROR(f"✗ NICE classification failed: {str(e)}"))
320
-
321
- # Run PARIS Classification
322
- if classification_only in ['paris', 'both']:
323
- self.stdout.write("Running PARIS classification...")
324
- try:
325
- # Check if video has frame_dir (required for PARIS)
326
- if not getattr(video_file_obj, "frame_dir", None):
327
- self.stdout.write(self.style.WARNING("⚠ PARIS classification skipped: no frame_dir available"))
328
- else:
329
- paris_serializer = ForParisClassificationSerializer()
330
- paris_results = paris_serializer.to_representation(video_list)
331
-
332
- if paris_results and isinstance(paris_results, dict) and 'data' in paris_results:
333
- segments_count = sum(len(item.get('frames', [])) for item in paris_results['data'] if 'frames' in item)
334
- self.stdout.write(self.style.SUCCESS(f"✓ PARIS classification completed ({segments_count} segments found)"))
335
- else:
336
- self.stdout.write(self.style.WARNING("⚠ PARIS classification completed but no valid segments found"))
337
-
338
- except Exception as e:
339
- self.stdout.write(self.style.ERROR(f"✗ PARIS classification failed: {str(e)}"))
340
-
341
- def _choose_processor_interactively(self, processors_qs):
342
- """Interactively choose a processor from multiple options."""
343
- processors = list(processors_qs)
344
-
345
- self.stdout.write(self.style.ERROR(
346
- f"\nThe center has {len(processors)} endoscopy processors.\n"
347
- "Choose one for this import:\n"
348
- ))
349
- for idx, proc in enumerate(processors, 1):
350
- self.stdout.write(f" [{idx}] {proc.name}")
351
-
352
- while True:
353
- try:
354
- choice = input("Processor number › ").strip()
355
- except (EOFError, KeyboardInterrupt):
356
- self.stderr.write("\nAborted.")
357
- raise SystemExit(1)
358
-
359
- try:
360
- index = int(choice) - 1
361
- if not 0 <= index < len(processors):
362
- raise ValueError
363
- except ValueError:
364
- self.stderr.write("❌ Please enter a number in the list above.\n")
365
- continue
366
-
367
- return processors[index]
@@ -1,96 +0,0 @@
1
- from typing import Optional
2
-
3
- from django.contrib.contenttypes.fields import GenericForeignKey
4
- from django.contrib.contenttypes.models import ContentType
5
- from django.db import models
6
-
7
-
8
- class ProcessingHistory(models.Model):
9
- """
10
- Generic processing history for media files (video, pdf, ...).
11
-
12
- Stores:
13
- - which object (VideoFile/RawPdfFile/other) this entry belongs to
14
- - whether processing/anonymization succeeded
15
- - timestamps
16
- """
17
-
18
- created_at = models.DateTimeField(auto_now_add=True)
19
- success = models.BooleanField(default=False, blank=True)
20
-
21
- content_type = models.ForeignKey(ContentType, on_delete=models.CASCADE)
22
- object_id = models.PositiveBigIntegerField()
23
- content_object = GenericForeignKey("content_type", "object_id")
24
-
25
- class Meta:
26
- ordering = ["-created_at"]
27
-
28
- def __str__(self) -> str:
29
- return f"{self.content_type} #{self.object_id}, Success: {self.success}"
30
-
31
- # --- public helpers that work with *objects* ---
32
-
33
- @classmethod
34
- def get_or_create_for_object(
35
- cls,
36
- *,
37
- obj: models.Model,
38
- success: Optional[bool],
39
- ) -> "ProcessingHistory":
40
- """
41
- Get or create a ProcessingHistory entry for a concrete model instance.
42
-
43
- - Returns existing entry for (object_id, content_type) if present
44
- - Otherwise creates one with the given success flag (default False)
45
- - If an entry exists and `success` is provided, it updates `success`
46
- """
47
- ct = ContentType.objects.get_for_model(obj, for_concrete_model=False)
48
-
49
- ph, created = cls.objects.get_or_create(
50
- object_id=obj.pk,
51
- content_type=ct,
52
- defaults={"success": success},
53
- )
54
-
55
- if not created and success is not None and ph.success != success:
56
- ph.success = success
57
- ph.save(update_fields=["success"])
58
-
59
- return ph
60
-
61
- @classmethod
62
- def has_history(
63
- cls,
64
- *,
65
- object_id: int,
66
- content_type: ContentType,
67
- success: Optional[bool] = None,
68
- ) -> bool:
69
- """
70
- Return True if there is at least one ProcessingHistory entry for the
71
- given (object_id, content_type) combination.
72
-
73
- If `success` is not None, only entries matching that success flag are
74
- considered.
75
- """
76
- qs = cls.objects.filter(object_id=object_id, content_type=content_type)
77
- if success is not None:
78
- qs = qs.filter(success=success)
79
- return qs.exists()
80
-
81
- @classmethod
82
- def has_history_for_object(
83
- cls,
84
- *,
85
- obj: models.Model,
86
- success: Optional[bool] = None,
87
- ) -> bool:
88
- """
89
- Convenience wrapper for model instances.
90
- """
91
- ct = ContentType.objects.get_for_model(obj, for_concrete_model=False)
92
- return cls.has_history(
93
- object_id=obj.pk,
94
- content_type=ct,
95
- success=success,
96
- )
@@ -1,7 +0,0 @@
1
- from .label import LabelSerializer
2
- from .image_classification_annotation import ImageClassificationAnnotationSerializer
3
-
4
- __all__ = [
5
- 'LabelSerializer',
6
- 'ImageClassificationAnnotationSerializer',
7
- ]
@@ -1,149 +0,0 @@
1
- from typing import TYPE_CHECKING
2
- from django.core.exceptions import ObjectDoesNotExist
3
- from rest_framework import serializers
4
- from endoreg_db.models import (
5
- Label,
6
- VideoFile,
7
- LabelVideoSegment,
8
- InformationSource
9
- )
10
-
11
- import logging
12
- logger = logging.getLogger(__name__)
13
-
14
- if TYPE_CHECKING:
15
- from endoreg_db.serializers import LabelVideoSegmentSerializer
16
-
17
- def _get_video_file(self, video_id):
18
- """
19
- Retrieve a VideoFile instance by its ID.
20
-
21
- Raises a serializers.ValidationError if the VideoFile does not exist.
22
- """
23
- try:
24
- return VideoFile.objects.get(id=video_id)
25
- except ObjectDoesNotExist as exc:
26
- raise serializers.ValidationError(f"VideoFile with id {video_id} does not exist") from exc
27
-
28
- def _get_label(self, label_id, label_name):
29
- """
30
- Retrieve a Label instance by ID or name, creating it if necessary.
31
-
32
- Raises a ValidationError if the label cannot be found, created, or if neither identifier is provided.
33
- """
34
- if label_id:
35
- try:
36
- return Label.objects.get(id=label_id)
37
- except ObjectDoesNotExist as exc:
38
- raise serializers.ValidationError(f"Label with id {label_id} does not exist") from exc
39
- elif label_name:
40
- label, _ = Label.get_or_create_from_name(label_name)
41
- if not label:
42
- raise serializers.ValidationError(f"Failed to create or retrieve label with name {label_name}")
43
- return label
44
- else:
45
- raise serializers.ValidationError("Either label_id or label_name must be provided")
46
-
47
- def _validate_fps(self, video_file):
48
- """
49
- Validate and return the frames per second (FPS) value from a video file.
50
-
51
- Raises a validation error if the FPS is missing, not a positive number, or cannot be converted to a float.
52
-
53
- Returns:
54
- float: The validated FPS value.
55
- """
56
- fps_raw = video_file.get_fps()
57
- if fps_raw is None:
58
- raise serializers.ValidationError("Video file must have a defined FPS")
59
- try:
60
- fps = float(fps_raw)
61
- if fps <= 0:
62
- raise ValueError("FPS must be a positive number")
63
- except (ValueError, TypeError):
64
- raise serializers.ValidationError("Invalid FPS format in video file")
65
- return fps
66
-
67
- def _calculate_frame_numbers(self, validated_data, fps):
68
- """
69
- Convert start and end times in the input data to corresponding frame numbers using the provided FPS value.
70
-
71
- Parameters:
72
- validated_data (dict): Input data containing 'start_time' and 'end_time' keys.
73
- fps (float): Frames per second of the video.
74
-
75
- Returns:
76
- dict: The input data dictionary updated with 'start_frame_number' and 'end_frame_number' keys.
77
-
78
- Raises:
79
- serializers.ValidationError: If frame numbers cannot be determined from the provided data.
80
- """
81
- if validated_data.get('start_time') is not None:
82
- validated_data['start_frame_number'] = int(float(validated_data['start_time']) * fps)
83
- if validated_data.get('end_time') is not None:
84
- validated_data['end_frame_number'] = int(float(validated_data['end_time']) * fps)
85
- if 'start_frame_number' not in validated_data or 'end_frame_number' not in validated_data:
86
- raise serializers.ValidationError("Could not determine frame numbers from provided data")
87
- return validated_data
88
-
89
- def _get_information_source(self):
90
- """
91
- Retrieve or create the information source for manual annotation.
92
-
93
- Returns:
94
- InformationSource: The source object representing manual annotation.
95
- """
96
- source, _ = InformationSource.objects.get_or_create(
97
- name='Manual Annotation',
98
- defaults={
99
- 'description': 'Manually created label segments via web interface',
100
- }
101
- )
102
- return source
103
-
104
- def _create(self:"LabelVideoSegmentSerializer", validated_data):
105
- """
106
- Create and persist a new LabelVideoSegment instance from validated input data.
107
-
108
- This method extracts relevant fields from the input, retrieves or creates related objects (video file, label, information source), validates video FPS, calculates frame numbers from time values, and creates a new LabelVideoSegment. Raises a validation error if required data is missing or invalid, or if creation fails.
109
-
110
- Returns:
111
- LabelVideoSegment: The newly created and saved segment instance.
112
- """
113
- try:
114
- # Extract convenience fields
115
- video_id = validated_data.pop('video_id')
116
- label_id = validated_data.pop('label_id', None)
117
- label_name = validated_data.pop('label_name', None)
118
- start_time = validated_data.pop('start_time', None)
119
- end_time = validated_data.pop('end_time', None)
120
-
121
- video_file = self.get_video_file(video_id)
122
- label = self.get_label(label_id, label_name)
123
- fps = self.validate_fps(video_file)
124
- validated_data['start_time'] = start_time
125
- validated_data['end_time'] = end_time
126
- validated_data = self.calculate_frame_numbers(validated_data, fps)
127
- source = self.get_information_source()
128
-
129
- # Create the segment directly
130
- segment = LabelVideoSegment.safe_create(
131
- video_file=video_file,
132
- label=label,
133
- source=source,
134
- start_frame_number=validated_data['start_frame_number'],
135
- end_frame_number=validated_data['end_frame_number'],
136
- prediction_meta=None # No prediction meta for manual annotations
137
- )
138
- segment.save()
139
-
140
- logger.info("Created new video segment: %s for video %s with label %s",
141
- segment.pk, video_id, label.name if label else 'None')
142
- return segment
143
-
144
- except serializers.ValidationError as e:
145
- logger.error("Validation error while creating video segment: %s", str(e))
146
- raise
147
- except Exception as e:
148
- logger.error("Unexpected error while creating video segment: %s", str(e))
149
- raise serializers.ValidationError(f"Failed to create segment: {str(e)}")