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

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

Potentially problematic release.


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

Files changed (453) hide show
  1. endoreg_db/admin.py +10 -5
  2. endoreg_db/apps.py +4 -7
  3. endoreg_db/authz/auth.py +1 -0
  4. endoreg_db/authz/backends.py +1 -1
  5. endoreg_db/authz/management/commands/list_routes.py +2 -0
  6. endoreg_db/authz/middleware.py +8 -7
  7. endoreg_db/authz/permissions.py +21 -10
  8. endoreg_db/authz/policy.py +14 -19
  9. endoreg_db/authz/views_auth.py +14 -10
  10. endoreg_db/codemods/rename_datetime_fields.py +8 -1
  11. endoreg_db/exceptions.py +5 -2
  12. endoreg_db/forms/__init__.py +0 -1
  13. endoreg_db/forms/examination_form.py +4 -3
  14. endoreg_db/forms/patient_finding_intervention_form.py +30 -8
  15. endoreg_db/forms/patient_form.py +9 -13
  16. endoreg_db/forms/questionnaires/__init__.py +1 -1
  17. endoreg_db/forms/settings/__init__.py +4 -1
  18. endoreg_db/forms/unit.py +2 -1
  19. endoreg_db/helpers/count_db.py +17 -14
  20. endoreg_db/helpers/default_objects.py +2 -1
  21. endoreg_db/helpers/download_segmentation_model.py +4 -3
  22. endoreg_db/helpers/interact.py +0 -5
  23. endoreg_db/helpers/test_video_helper.py +33 -25
  24. endoreg_db/import_files/__init__.py +1 -1
  25. endoreg_db/import_files/context/__init__.py +1 -1
  26. endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
  27. endoreg_db/import_files/context/ensure_center.py +4 -4
  28. endoreg_db/import_files/context/file_lock.py +3 -3
  29. endoreg_db/import_files/context/import_context.py +11 -12
  30. endoreg_db/import_files/context/validate_directories.py +1 -0
  31. endoreg_db/import_files/file_storage/create_report_file.py +57 -34
  32. endoreg_db/import_files/file_storage/create_video_file.py +64 -35
  33. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
  34. endoreg_db/import_files/file_storage/state_management.py +146 -83
  35. endoreg_db/import_files/file_storage/storage.py +5 -1
  36. endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
  37. endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
  38. endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
  39. endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
  40. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
  41. endoreg_db/import_files/report_import_service.py +36 -30
  42. endoreg_db/import_files/video_import_service.py +27 -23
  43. endoreg_db/logger_conf.py +56 -40
  44. endoreg_db/management/__init__.py +1 -1
  45. endoreg_db/management/commands/__init__.py +1 -1
  46. endoreg_db/management/commands/check_auth.py +45 -38
  47. endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
  48. endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
  49. endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
  50. endoreg_db/management/commands/fix_video_paths.py +75 -54
  51. endoreg_db/management/commands/import_report.py +1 -3
  52. endoreg_db/management/commands/list_routes.py +2 -0
  53. endoreg_db/management/commands/load_ai_model_data.py +8 -2
  54. endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
  55. endoreg_db/management/commands/load_center_data.py +3 -3
  56. endoreg_db/management/commands/load_distribution_data.py +35 -38
  57. endoreg_db/management/commands/load_endoscope_data.py +0 -3
  58. endoreg_db/management/commands/load_examination_data.py +20 -4
  59. endoreg_db/management/commands/load_finding_data.py +18 -3
  60. endoreg_db/management/commands/load_gender_data.py +17 -24
  61. endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
  62. endoreg_db/management/commands/load_information_source.py +0 -3
  63. endoreg_db/management/commands/load_lab_value_data.py +14 -3
  64. endoreg_db/management/commands/load_legacy_data.py +303 -0
  65. endoreg_db/management/commands/load_name_data.py +1 -2
  66. endoreg_db/management/commands/load_pdf_type_data.py +4 -8
  67. endoreg_db/management/commands/load_profession_data.py +0 -1
  68. endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
  69. endoreg_db/management/commands/load_requirement_data.py +6 -2
  70. endoreg_db/management/commands/load_unit_data.py +0 -4
  71. endoreg_db/management/commands/load_user_groups.py +5 -7
  72. endoreg_db/management/commands/model_input.py +169 -0
  73. endoreg_db/management/commands/register_ai_model.py +22 -16
  74. endoreg_db/management/commands/setup_endoreg_db.py +110 -32
  75. endoreg_db/management/commands/storage_management.py +14 -8
  76. endoreg_db/management/commands/summarize_db_content.py +154 -63
  77. endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
  78. endoreg_db/management/commands/validate_video_files.py +82 -50
  79. endoreg_db/management/commands/video_validation.py +4 -6
  80. endoreg_db/migrations/0001_initial.py +112 -63
  81. endoreg_db/migrations/__init__.py +0 -0
  82. endoreg_db/models/__init__.py +8 -0
  83. endoreg_db/models/administration/ai/active_model.py +5 -5
  84. endoreg_db/models/administration/ai/ai_model.py +41 -18
  85. endoreg_db/models/administration/ai/model_type.py +1 -0
  86. endoreg_db/models/administration/case/case.py +22 -22
  87. endoreg_db/models/administration/center/__init__.py +5 -5
  88. endoreg_db/models/administration/center/center.py +6 -2
  89. endoreg_db/models/administration/center/center_resource.py +18 -4
  90. endoreg_db/models/administration/center/center_shift.py +3 -1
  91. endoreg_db/models/administration/center/center_waste.py +6 -2
  92. endoreg_db/models/administration/person/__init__.py +1 -1
  93. endoreg_db/models/administration/person/employee/__init__.py +1 -1
  94. endoreg_db/models/administration/person/employee/employee_type.py +3 -1
  95. endoreg_db/models/administration/person/examiner/__init__.py +1 -1
  96. endoreg_db/models/administration/person/examiner/examiner.py +10 -2
  97. endoreg_db/models/administration/person/names/first_name.py +6 -4
  98. endoreg_db/models/administration/person/names/last_name.py +4 -3
  99. endoreg_db/models/administration/person/patient/__init__.py +1 -1
  100. endoreg_db/models/administration/person/patient/patient.py +0 -1
  101. endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
  102. endoreg_db/models/administration/person/person.py +1 -1
  103. endoreg_db/models/administration/product/__init__.py +7 -6
  104. endoreg_db/models/administration/product/product.py +6 -2
  105. endoreg_db/models/administration/product/product_group.py +9 -7
  106. endoreg_db/models/administration/product/product_material.py +9 -2
  107. endoreg_db/models/administration/product/reference_product.py +64 -15
  108. endoreg_db/models/administration/qualification/qualification.py +3 -1
  109. endoreg_db/models/administration/shift/shift.py +3 -1
  110. endoreg_db/models/administration/shift/shift_type.py +12 -4
  111. endoreg_db/models/aidataset/__init__.py +5 -0
  112. endoreg_db/models/aidataset/aidataset.py +193 -0
  113. endoreg_db/models/label/__init__.py +1 -1
  114. endoreg_db/models/label/label.py +10 -2
  115. endoreg_db/models/label/label_set.py +3 -1
  116. endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
  117. endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
  118. endoreg_db/models/media/__init__.py +12 -5
  119. endoreg_db/models/media/frame/__init__.py +1 -1
  120. endoreg_db/models/media/frame/frame.py +34 -8
  121. endoreg_db/models/media/pdf/__init__.py +2 -1
  122. endoreg_db/models/media/pdf/raw_pdf.py +11 -4
  123. endoreg_db/models/media/pdf/report_file.py +6 -2
  124. endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
  125. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
  126. endoreg_db/models/media/video/create_from_file.py +20 -41
  127. endoreg_db/models/media/video/pipe_1.py +75 -30
  128. endoreg_db/models/media/video/pipe_2.py +37 -12
  129. endoreg_db/models/media/video/video_file.py +36 -24
  130. endoreg_db/models/media/video/video_file_ai.py +235 -70
  131. endoreg_db/models/media/video/video_file_anonymize.py +240 -65
  132. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
  133. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
  134. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
  135. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
  136. endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
  137. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
  138. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
  139. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
  140. endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
  141. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
  142. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
  143. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
  144. endoreg_db/models/media/video/video_file_io.py +85 -33
  145. endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
  146. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
  147. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
  148. endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
  149. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
  150. endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
  151. endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
  152. endoreg_db/models/media/video/video_file_segments.py +118 -27
  153. endoreg_db/models/media/video/video_metadata.py +25 -6
  154. endoreg_db/models/media/video/video_processing.py +54 -15
  155. endoreg_db/models/medical/__init__.py +3 -13
  156. endoreg_db/models/medical/contraindication/__init__.py +3 -1
  157. endoreg_db/models/medical/disease.py +18 -6
  158. endoreg_db/models/medical/event.py +6 -2
  159. endoreg_db/models/medical/examination/__init__.py +5 -1
  160. endoreg_db/models/medical/examination/examination.py +22 -6
  161. endoreg_db/models/medical/examination/examination_indication.py +23 -7
  162. endoreg_db/models/medical/examination/examination_time.py +6 -2
  163. endoreg_db/models/medical/finding/__init__.py +3 -1
  164. endoreg_db/models/medical/finding/finding.py +37 -12
  165. endoreg_db/models/medical/finding/finding_classification.py +27 -8
  166. endoreg_db/models/medical/finding/finding_intervention.py +19 -6
  167. endoreg_db/models/medical/finding/finding_type.py +3 -1
  168. endoreg_db/models/medical/hardware/__init__.py +1 -1
  169. endoreg_db/models/medical/hardware/endoscope.py +14 -2
  170. endoreg_db/models/medical/laboratory/__init__.py +1 -1
  171. endoreg_db/models/medical/laboratory/lab_value.py +139 -39
  172. endoreg_db/models/medical/medication/__init__.py +7 -3
  173. endoreg_db/models/medical/medication/medication.py +3 -1
  174. endoreg_db/models/medical/medication/medication_indication.py +3 -1
  175. endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
  176. endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
  177. endoreg_db/models/medical/medication/medication_schedule.py +3 -1
  178. endoreg_db/models/medical/patient/__init__.py +2 -10
  179. endoreg_db/models/medical/patient/medication_examples.py +3 -14
  180. endoreg_db/models/medical/patient/patient_disease.py +17 -5
  181. endoreg_db/models/medical/patient/patient_event.py +12 -4
  182. endoreg_db/models/medical/patient/patient_examination.py +52 -15
  183. endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
  184. endoreg_db/models/medical/patient/patient_finding.py +105 -29
  185. endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
  186. endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
  187. endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
  188. endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
  189. endoreg_db/models/medical/patient/patient_medication.py +25 -7
  190. endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
  191. endoreg_db/models/metadata/model_meta.py +40 -12
  192. endoreg_db/models/metadata/model_meta_logic.py +51 -16
  193. endoreg_db/models/metadata/sensitive_meta.py +65 -28
  194. endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
  195. endoreg_db/models/metadata/video_meta.py +146 -39
  196. endoreg_db/models/metadata/video_prediction_logic.py +70 -21
  197. endoreg_db/models/metadata/video_prediction_meta.py +80 -27
  198. endoreg_db/models/operation_log.py +63 -0
  199. endoreg_db/models/other/__init__.py +10 -10
  200. endoreg_db/models/other/distribution/__init__.py +9 -7
  201. endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
  202. endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
  203. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
  204. endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
  205. endoreg_db/models/other/emission/__init__.py +1 -1
  206. endoreg_db/models/other/emission/emission_factor.py +9 -3
  207. endoreg_db/models/other/information_source.py +15 -5
  208. endoreg_db/models/other/material.py +3 -1
  209. endoreg_db/models/other/transport_route.py +3 -1
  210. endoreg_db/models/other/unit.py +6 -2
  211. endoreg_db/models/report/report.py +0 -1
  212. endoreg_db/models/requirement/requirement.py +84 -27
  213. endoreg_db/models/requirement/requirement_error.py +5 -6
  214. endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
  215. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
  216. endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
  217. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
  218. endoreg_db/models/requirement/requirement_operator.py +28 -8
  219. endoreg_db/models/requirement/requirement_set.py +34 -11
  220. endoreg_db/models/state/__init__.py +1 -0
  221. endoreg_db/models/state/audit_ledger.py +9 -2
  222. endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
  223. endoreg_db/models/state/processing_history/processing_history.py +136 -0
  224. endoreg_db/models/state/raw_pdf.py +0 -1
  225. endoreg_db/models/state/video.py +2 -3
  226. endoreg_db/models/utils.py +4 -2
  227. endoreg_db/queries/__init__.py +2 -6
  228. endoreg_db/queries/annotations/__init__.py +1 -3
  229. endoreg_db/queries/annotations/legacy.py +37 -26
  230. endoreg_db/root_urls.py +3 -4
  231. endoreg_db/schemas/examination_evaluation.py +3 -0
  232. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
  233. endoreg_db/serializers/__init__.py +2 -8
  234. endoreg_db/serializers/administration/__init__.py +1 -2
  235. endoreg_db/serializers/administration/ai/__init__.py +0 -1
  236. endoreg_db/serializers/administration/ai/active_model.py +3 -1
  237. endoreg_db/serializers/administration/ai/ai_model.py +5 -3
  238. endoreg_db/serializers/administration/ai/model_type.py +3 -1
  239. endoreg_db/serializers/administration/center.py +7 -2
  240. endoreg_db/serializers/administration/gender.py +4 -2
  241. endoreg_db/serializers/anonymization.py +13 -13
  242. endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
  243. endoreg_db/serializers/examination/__init__.py +1 -1
  244. endoreg_db/serializers/examination/base.py +12 -13
  245. endoreg_db/serializers/examination/dropdown.py +6 -7
  246. endoreg_db/serializers/examination_serializer.py +3 -6
  247. endoreg_db/serializers/finding/__init__.py +1 -1
  248. endoreg_db/serializers/finding/finding.py +14 -7
  249. endoreg_db/serializers/finding_classification/__init__.py +3 -3
  250. endoreg_db/serializers/finding_classification/choice.py +3 -3
  251. endoreg_db/serializers/finding_classification/classification.py +2 -4
  252. endoreg_db/serializers/label_video_segment/__init__.py +5 -3
  253. endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
  254. endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
  255. endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
  256. endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
  257. endoreg_db/serializers/meta/__init__.py +1 -2
  258. endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
  259. endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
  260. endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
  261. endoreg_db/serializers/misc/__init__.py +2 -2
  262. endoreg_db/serializers/misc/file_overview.py +11 -7
  263. endoreg_db/serializers/misc/stats.py +10 -8
  264. endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
  265. endoreg_db/serializers/misc/upload_job.py +32 -29
  266. endoreg_db/serializers/patient/__init__.py +2 -1
  267. endoreg_db/serializers/patient/patient.py +32 -15
  268. endoreg_db/serializers/patient/patient_dropdown.py +11 -3
  269. endoreg_db/serializers/patient_examination/__init__.py +1 -1
  270. endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
  271. endoreg_db/serializers/patient_finding/__init__.py +1 -1
  272. endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
  273. endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
  274. endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
  275. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
  276. endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
  277. endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
  278. endoreg_db/serializers/pdf/__init__.py +1 -3
  279. endoreg_db/serializers/requirements/requirement_schema.py +1 -6
  280. endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
  281. endoreg_db/serializers/video/__init__.py +2 -2
  282. endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
  283. endoreg_db/serializers/video/video_file_brief.py +6 -2
  284. endoreg_db/serializers/video/video_file_detail.py +36 -23
  285. endoreg_db/serializers/video/video_file_list.py +4 -2
  286. endoreg_db/serializers/video/video_processing_history.py +54 -50
  287. endoreg_db/services/__init__.py +1 -1
  288. endoreg_db/services/anonymization.py +2 -2
  289. endoreg_db/services/examination_evaluation.py +40 -17
  290. endoreg_db/services/model_meta_from_hf.py +76 -0
  291. endoreg_db/services/polling_coordinator.py +101 -70
  292. endoreg_db/services/pseudonym_service.py +27 -22
  293. endoreg_db/services/report_import.py +6 -3
  294. endoreg_db/services/segment_sync.py +75 -59
  295. endoreg_db/services/video_import.py +6 -7
  296. endoreg_db/urls/__init__.py +2 -2
  297. endoreg_db/urls/ai.py +7 -25
  298. endoreg_db/urls/anonymization.py +61 -15
  299. endoreg_db/urls/auth.py +4 -4
  300. endoreg_db/urls/classification.py +4 -9
  301. endoreg_db/urls/examination.py +27 -18
  302. endoreg_db/urls/media.py +27 -34
  303. endoreg_db/urls/patient.py +11 -7
  304. endoreg_db/urls/requirements.py +3 -1
  305. endoreg_db/urls/root_urls.py +2 -3
  306. endoreg_db/urls/stats.py +24 -16
  307. endoreg_db/urls/upload.py +3 -11
  308. endoreg_db/utils/__init__.py +14 -15
  309. endoreg_db/utils/ai/__init__.py +1 -1
  310. endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
  311. endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
  312. endoreg_db/utils/ai/get.py +2 -1
  313. endoreg_db/utils/ai/inference_dataset.py +14 -15
  314. endoreg_db/utils/ai/model_training/config.py +117 -0
  315. endoreg_db/utils/ai/model_training/dataset.py +74 -0
  316. endoreg_db/utils/ai/model_training/losses.py +68 -0
  317. endoreg_db/utils/ai/model_training/metrics.py +78 -0
  318. endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
  319. endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
  320. endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
  321. endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
  322. endoreg_db/utils/ai/predict.py +4 -4
  323. endoreg_db/utils/ai/preprocess.py +19 -11
  324. endoreg_db/utils/calc_duration_seconds.py +4 -4
  325. endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
  326. endoreg_db/utils/check_video_files.py +74 -47
  327. endoreg_db/utils/cropping.py +10 -9
  328. endoreg_db/utils/dataloader.py +11 -3
  329. endoreg_db/utils/dates.py +3 -4
  330. endoreg_db/utils/defaults/set_default_center.py +7 -6
  331. endoreg_db/utils/env.py +6 -2
  332. endoreg_db/utils/extract_specific_frames.py +24 -9
  333. endoreg_db/utils/file_operations.py +30 -18
  334. endoreg_db/utils/fix_video_path_direct.py +57 -41
  335. endoreg_db/utils/frame_anonymization_utils.py +157 -157
  336. endoreg_db/utils/hashs.py +3 -18
  337. endoreg_db/utils/links/requirement_link.py +96 -52
  338. endoreg_db/utils/ocr.py +30 -25
  339. endoreg_db/utils/operation_log.py +61 -0
  340. endoreg_db/utils/parse_and_generate_yaml.py +12 -13
  341. endoreg_db/utils/paths.py +6 -6
  342. endoreg_db/utils/permissions.py +40 -24
  343. endoreg_db/utils/pipelines/process_video_dir.py +50 -26
  344. endoreg_db/utils/product/sum_emissions.py +5 -3
  345. endoreg_db/utils/product/sum_weights.py +4 -2
  346. endoreg_db/utils/pydantic_models/__init__.py +3 -4
  347. endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
  348. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
  349. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
  350. endoreg_db/utils/setup_config.py +21 -5
  351. endoreg_db/utils/storage.py +3 -1
  352. endoreg_db/utils/translation.py +19 -15
  353. endoreg_db/utils/uuid.py +1 -0
  354. endoreg_db/utils/validate_endo_roi.py +12 -4
  355. endoreg_db/utils/validate_subcategory_dict.py +26 -24
  356. endoreg_db/utils/validate_video_detailed.py +207 -149
  357. endoreg_db/utils/video/__init__.py +7 -3
  358. endoreg_db/utils/video/extract_frames.py +30 -18
  359. endoreg_db/utils/video/ffmpeg_wrapper.py +217 -52
  360. endoreg_db/utils/video/names.py +11 -6
  361. endoreg_db/utils/video/streaming_processor.py +175 -101
  362. endoreg_db/utils/video/video_splitter.py +30 -19
  363. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
  364. endoreg_db/views/__init__.py +0 -20
  365. endoreg_db/views/anonymization/__init__.py +6 -2
  366. endoreg_db/views/anonymization/media_management.py +2 -6
  367. endoreg_db/views/anonymization/overview.py +34 -1
  368. endoreg_db/views/anonymization/validate.py +79 -18
  369. endoreg_db/views/auth/__init__.py +1 -1
  370. endoreg_db/views/auth/keycloak.py +16 -14
  371. endoreg_db/views/examination/__init__.py +12 -15
  372. endoreg_db/views/examination/examination.py +5 -5
  373. endoreg_db/views/examination/examination_manifest_cache.py +5 -5
  374. endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
  375. endoreg_db/views/examination/get_finding_classifications.py +9 -7
  376. endoreg_db/views/examination/get_findings.py +8 -10
  377. endoreg_db/views/examination/get_instruments.py +3 -2
  378. endoreg_db/views/examination/get_interventions.py +1 -1
  379. endoreg_db/views/finding/__init__.py +2 -2
  380. endoreg_db/views/finding/finding.py +58 -54
  381. endoreg_db/views/finding/get_classifications.py +1 -1
  382. endoreg_db/views/finding/get_interventions.py +1 -1
  383. endoreg_db/views/finding_classification/__init__.py +5 -5
  384. endoreg_db/views/finding_classification/finding_classification.py +5 -6
  385. endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
  386. endoreg_db/views/media/__init__.py +13 -13
  387. endoreg_db/views/media/pdf_media.py +9 -9
  388. endoreg_db/views/media/sensitive_metadata.py +10 -7
  389. endoreg_db/views/media/video_media.py +4 -4
  390. endoreg_db/views/meta/__init__.py +1 -1
  391. endoreg_db/views/meta/sensitive_meta_list.py +20 -22
  392. endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
  393. endoreg_db/views/misc/__init__.py +6 -34
  394. endoreg_db/views/misc/center.py +2 -1
  395. endoreg_db/views/misc/csrf.py +2 -1
  396. endoreg_db/views/misc/gender.py +2 -1
  397. endoreg_db/views/misc/stats.py +141 -106
  398. endoreg_db/views/patient/__init__.py +1 -3
  399. endoreg_db/views/patient/patient.py +141 -99
  400. endoreg_db/views/patient_examination/__init__.py +5 -5
  401. endoreg_db/views/patient_examination/patient_examination.py +43 -42
  402. endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
  403. endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
  404. endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
  405. endoreg_db/views/patient_examination/video.py +114 -80
  406. endoreg_db/views/patient_finding/__init__.py +1 -1
  407. endoreg_db/views/patient_finding/patient_finding.py +17 -10
  408. endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
  409. endoreg_db/views/patient_finding_classification/__init__.py +1 -1
  410. endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
  411. endoreg_db/views/report/reimport.py +1 -1
  412. endoreg_db/views/report/report_stream.py +5 -8
  413. endoreg_db/views/requirement/__init__.py +2 -1
  414. endoreg_db/views/requirement/evaluate.py +7 -9
  415. endoreg_db/views/requirement/lookup.py +2 -3
  416. endoreg_db/views/requirement/lookup_store.py +0 -1
  417. endoreg_db/views/requirement/requirement_utils.py +2 -4
  418. endoreg_db/views/stats/__init__.py +4 -4
  419. endoreg_db/views/stats/stats_views.py +152 -115
  420. endoreg_db/views/video/__init__.py +18 -27
  421. endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
  422. endoreg_db/views/{ai → video/ai}/label.py +20 -16
  423. endoreg_db/views/video/correction.py +5 -6
  424. endoreg_db/views/video/reimport.py +134 -99
  425. endoreg_db/views/video/segments_crud.py +134 -44
  426. endoreg_db/views/video/video_apply_mask.py +13 -12
  427. endoreg_db/views/video/video_correction.py +2 -1
  428. endoreg_db/views/video/video_download_processed.py +15 -15
  429. endoreg_db/views/video/video_meta_stats.py +7 -6
  430. endoreg_db/views/video/video_processing_history.py +3 -2
  431. endoreg_db/views/video/video_remove_frames.py +13 -12
  432. endoreg_db/views/video/video_stream.py +110 -82
  433. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
  434. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +436 -433
  435. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +0 -119
  436. endoreg_db/management/commands/import_fallback_video.py +0 -203
  437. endoreg_db/management/commands/import_video.py +0 -422
  438. endoreg_db/management/commands/import_video_with_classification.py +0 -367
  439. endoreg_db/models/media/processing_history/processing_history.py +0 -96
  440. endoreg_db/serializers/label/__init__.py +0 -7
  441. endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
  442. endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
  443. endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
  444. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
  445. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
  446. endoreg_db/services/__old/pdf_import.py +0 -1487
  447. endoreg_db/services/__old/video_import.py +0 -1306
  448. endoreg_db/tasks/upload_tasks.py +0 -216
  449. endoreg_db/tasks/video_ingest.py +0 -161
  450. endoreg_db/tasks/video_processing_tasks.py +0 -327
  451. endoreg_db/views/misc/translation.py +0 -182
  452. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
  453. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
@@ -1,36 +1,39 @@
1
1
  """
2
2
  Django management command to validate video file existence and accessibility.
3
3
  """
4
+
5
+ import logging
6
+ from pathlib import Path
7
+
4
8
  from django.core.management.base import BaseCommand
5
9
 
6
10
  from endoreg_db.models import VideoFile
7
- from pathlib import Path
8
- import logging
9
11
 
10
12
  logger = logging.getLogger(__name__)
11
13
 
12
14
  # TODO Review if this is still used. Delete if not.
13
15
 
16
+
14
17
  class Command(BaseCommand):
15
- help = 'Validate video file existence and accessibility'
18
+ help = "Validate video file existence and accessibility"
16
19
 
17
20
  def add_arguments(self, parser):
18
21
  parser.add_argument(
19
- '--video-id',
22
+ "--video-id",
20
23
  type=int,
21
- help='Check specific video ID',
24
+ help="Check specific video ID",
22
25
  )
23
26
  parser.add_argument(
24
- '--verbose',
25
- action='store_true',
26
- help='Enable verbose output',
27
+ "--verbose",
28
+ action="store_true",
29
+ help="Enable verbose output",
27
30
  )
28
31
 
29
32
  def handle(self, *args, **options):
30
33
  """Validate video files and their accessibility."""
31
- verbose = options['verbose']
32
- video_id = options.get('video_id')
33
- fix_missing = options['fix_missing']
34
+ verbose = options["verbose"]
35
+ video_id = options.get("video_id")
36
+ fix_missing = options["fix_missing", False]
34
37
 
35
38
  if verbose:
36
39
  self.stdout.write(self.style.SUCCESS("Starting video validation..."))
@@ -41,7 +44,9 @@ class Command(BaseCommand):
41
44
  videos = [VideoFile.objects.get(pk=video_id)]
42
45
  self.stdout.write(f"Checking specific video ID: {video_id}")
43
46
  except VideoFile.DoesNotExist:
44
- self.stdout.write(self.style.ERROR(f"Video with ID {video_id} not found"))
47
+ self.stdout.write(
48
+ self.style.ERROR(f"Video with ID {video_id} not found")
49
+ )
45
50
  return
46
51
  else:
47
52
  videos = VideoFile.objects.all()
@@ -53,19 +58,19 @@ class Command(BaseCommand):
53
58
 
54
59
  for video in videos:
55
60
  video_status = self.check_video_file(video, verbose)
56
-
57
- if video_status['status'] == 'missing':
61
+
62
+ if video_status["status"] == "missing":
58
63
  missing_files.append(video_status)
59
- elif video_status['status'] == 'corrupted':
64
+ elif video_status["status"] == "corrupted":
60
65
  corrupted_files.append(video_status)
61
66
  else:
62
67
  accessible_files.append(video_status)
63
68
 
64
69
  # Report results
65
- self.stdout.write("\n" + "="*60)
70
+ self.stdout.write("\n" + "=" * 60)
66
71
  self.stdout.write(self.style.SUCCESS("VALIDATION COMPLETE"))
67
- self.stdout.write("="*60)
68
-
72
+ self.stdout.write("=" * 60)
73
+
69
74
  self.stdout.write(f"✅ Accessible videos: {len(accessible_files)}")
70
75
  self.stdout.write(f"❌ Missing files: {len(missing_files)}")
71
76
  self.stdout.write(f"⚠️ Potentially corrupted: {len(corrupted_files)}")
@@ -73,37 +78,43 @@ class Command(BaseCommand):
73
78
  if missing_files:
74
79
  self.stdout.write(self.style.WARNING("\nMISSING FILES:"))
75
80
  for file_info in missing_files:
76
- self.stdout.write(f" - Video ID {file_info['video_id']}: {file_info['error']}")
81
+ self.stdout.write(
82
+ f" - Video ID {file_info['video_id']}: {file_info['error']}"
83
+ )
77
84
  if fix_missing:
78
85
  self.stdout.write(" → Marking as inactive (if applicable)")
79
86
 
80
87
  if corrupted_files:
81
88
  self.stdout.write(self.style.WARNING("\nPOTENTIALLY CORRUPTED FILES:"))
82
89
  for file_info in corrupted_files:
83
- self.stdout.write(f" - Video ID {file_info['video_id']}: {file_info['error']}")
90
+ self.stdout.write(
91
+ f" - Video ID {file_info['video_id']}: {file_info['error']}"
92
+ )
84
93
 
85
94
  if verbose and accessible_files:
86
95
  self.stdout.write(self.style.SUCCESS("\nACCESSIBLE FILES:"))
87
96
  for file_info in accessible_files[:10]: # Show first 10
88
- self.stdout.write(f" ✅ Video ID {file_info['video_id']}: {file_info['path']} ({file_info['size_mb']:.1f} MB)")
89
-
97
+ self.stdout.write(
98
+ f" ✅ Video ID {file_info['video_id']}: {file_info['path']} ({file_info['size_mb']:.1f} MB)"
99
+ )
100
+
90
101
  if len(accessible_files) > 10:
91
102
  self.stdout.write(f" ... and {len(accessible_files) - 10} more")
92
103
 
93
104
  def check_video_file(self, video, verbose=False):
94
105
  """
95
106
  Check a single video file for existence and basic accessibility.
96
-
107
+
97
108
  Returns:
98
109
  dict: Status information about the video file
99
110
  """
100
111
  video_info = {
101
- 'video_id': video.id,
102
- 'video_uuid': str(video.uuid) if hasattr(video, 'uuid') else 'N/A',
103
- 'status': 'unknown',
104
- 'path': None,
105
- 'size_mb': 0,
106
- 'error': None
112
+ "video_id": video.id,
113
+ "video_uuid": str(video.video_hash) if hasattr(video, "uuid") else "N/A",
114
+ "status": "unknown",
115
+ "path": None,
116
+ "size_mb": 0,
117
+ "error": None,
107
118
  }
108
119
 
109
120
  # Helper to check a file attribute
@@ -116,42 +127,63 @@ class Command(BaseCommand):
116
127
  try:
117
128
  file_path = Path(path_getter(file_field) if path_getter else file_field)
118
129
  info = video_info.copy()
119
- info['path'] = str(file_path)
130
+ info["path"] = str(file_path)
120
131
  if not file_path.exists():
121
- info['status'] = 'missing'
122
- info['error'] = f"{label or attr.replace('_', ' ').title()} does not exist: {file_path}"
132
+ info["status"] = "missing"
133
+ info["error"] = (
134
+ f"{label or attr.replace('_', ' ').title()} does not exist: {file_path}"
135
+ )
123
136
  return info
124
137
  file_size = file_path.stat().st_size
125
- info['size_mb'] = file_size / (1024 * 1024)
138
+ info["size_mb"] = file_size / (1024 * 1024)
126
139
  if file_size == 0:
127
- info['status'] = 'corrupted'
128
- info['error'] = f"{label or attr.replace('_', ' ').title()} exists but has zero size"
140
+ info["status"] = "corrupted"
141
+ info["error"] = (
142
+ f"{label or attr.replace('_', ' ').title()} exists but has zero size"
143
+ )
129
144
  else:
130
- info['status'] = 'accessible'
145
+ info["status"] = "accessible"
131
146
  return info
132
147
  except (ValueError, OSError) as e:
133
148
  info = video_info.copy()
134
- info['path'] = str(getattr(file_field, 'path', file_field))
135
- info['status'] = 'corrupted'
136
- info['error'] = f"Cannot access {label or attr.replace('_', ' ').title()}: {e}"
149
+ info["path"] = str(getattr(file_field, "path", file_field))
150
+ info["status"] = "corrupted"
151
+ info["error"] = (
152
+ f"Cannot access {label or attr.replace('_', ' ').title()}: {e}"
153
+ )
137
154
  return info
138
155
 
139
156
  # Try each file attribute in order of preference
140
157
  result = None
141
158
  # active_file_path: direct path string
142
- result = _check_file_attr(video, 'active_file_path', label='Active file path')
143
- if result: return result
159
+ result = _check_file_attr(video, "active_file_path", label="Active file path")
160
+ if result:
161
+ return result
144
162
  # active_file: Django FileField
145
- result = _check_file_attr(video, 'active_file', path_getter=lambda f: f.path, label='Active file')
146
- if result: return result
163
+ result = _check_file_attr(
164
+ video, "active_file", path_getter=lambda f: f.path, label="Active file"
165
+ )
166
+ if result:
167
+ return result
147
168
  # raw_file: Django FileField
148
- result = _check_file_attr(video, 'raw_file', path_getter=lambda f: f.path, label='Raw file')
149
- if result: return result
169
+ result = _check_file_attr(
170
+ video, "raw_file", path_getter=lambda f: f.path, label="Raw file"
171
+ )
172
+ if result:
173
+ return result
150
174
  # processed_file: Django FileField
151
- result = _check_file_attr(video, 'processed_file', path_getter=lambda f: f.path, label='Processed file')
152
- if result: return result
175
+ result = _check_file_attr(
176
+ video,
177
+ "processed_file",
178
+ path_getter=lambda f: f.path,
179
+ label="Processed file",
180
+ )
181
+ if result:
182
+ return result
153
183
 
154
184
  # If none found
155
- video_info['status'] = 'missing'
156
- video_info['error'] = "No video file paths found (no active_file, raw_file, or processed_file)"
157
- return video_info
185
+ video_info["status"] = "missing"
186
+ video_info["error"] = (
187
+ "No video file paths found (no active_file, raw_file, or processed_file)"
188
+ )
189
+ return video_info
@@ -1,4 +1,3 @@
1
- from endoreg_db.models import VideoPredictionMeta
2
1
  from typing import TYPE_CHECKING
3
2
  from django.core.management.base import BaseCommand
4
3
 
@@ -7,16 +6,15 @@ This command handles the video validation as seen in tests _pipe_1
7
6
  """
8
7
 
9
8
  if TYPE_CHECKING:
10
- from endoreg_db.models import VideoFile, VideoState
11
-
9
+ pass
10
+
11
+
12
12
  class Command(BaseCommand):
13
13
  help = "Data extraction and validation of video files in the database and updating their states accordingly."
14
14
 
15
15
  def handle(self, *args, **options):
16
16
  """
17
17
  Validates video files stored in the database and updates their states based on validation results.
18
-
18
+
19
19
  This method is intended to be executed as a Django management command to ensure the integrity and correct status of video file records.
20
20
  """
21
-
22
-
@@ -1,4 +1,4 @@
1
- # Generated by Django 5.2.7 on 2025-11-26 11:26
1
+ # Generated by Django 6.0 on 2025-12-20 13:34
2
2
 
3
3
  import django.core.validators
4
4
  import django.db.models.deletion
@@ -185,11 +185,7 @@ class Migration(migrations.Migration):
185
185
  fields=[
186
186
  ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
187
187
  ('name', models.CharField(max_length=100, unique=True)),
188
- ('name_de', models.CharField(max_length=100, null=True, unique=True)),
189
- ('name_en', models.CharField(max_length=100, null=True, unique=True)),
190
188
  ('description', models.TextField(blank=True, null=True)),
191
- ('description_de', models.TextField(blank=True, null=True)),
192
- ('description_en', models.TextField(blank=True, null=True)),
193
189
  ],
194
190
  ),
195
191
  migrations.CreateModel(
@@ -197,11 +193,7 @@ class Migration(migrations.Migration):
197
193
  fields=[
198
194
  ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
199
195
  ('name', models.CharField(max_length=255, unique=True)),
200
- ('name_de', models.CharField(max_length=255, null=True, unique=True)),
201
- ('name_en', models.CharField(max_length=255, null=True, unique=True)),
202
196
  ('description', models.TextField(blank=True)),
203
- ('description_de', models.TextField(blank=True, null=True)),
204
- ('description_en', models.TextField(blank=True, null=True)),
205
197
  ],
206
198
  ),
207
199
  migrations.CreateModel(
@@ -209,11 +201,7 @@ class Migration(migrations.Migration):
209
201
  fields=[
210
202
  ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
211
203
  ('name', models.CharField(max_length=255, unique=True)),
212
- ('name_de', models.CharField(max_length=255, null=True, unique=True)),
213
- ('name_en', models.CharField(max_length=255, null=True, unique=True)),
214
204
  ('description', models.TextField(blank=True)),
215
- ('description_de', models.TextField(blank=True, null=True)),
216
- ('description_en', models.TextField(blank=True, null=True)),
217
205
  ('subcategories', models.JSONField(default=dict)),
218
206
  ('numerical_descriptors', models.JSONField(default=dict)),
219
207
  ],
@@ -231,11 +219,7 @@ class Migration(migrations.Migration):
231
219
  fields=[
232
220
  ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
233
221
  ('name', models.CharField(max_length=100, unique=True)),
234
- ('name_de', models.CharField(max_length=100, null=True, unique=True)),
235
- ('name_en', models.CharField(max_length=100, null=True, unique=True)),
236
222
  ('description', models.TextField(blank=True, null=True)),
237
- ('description_de', models.TextField(blank=True, null=True)),
238
- ('description_en', models.TextField(blank=True, null=True)),
239
223
  ],
240
224
  ),
241
225
  migrations.CreateModel(
@@ -261,6 +245,19 @@ class Migration(migrations.Migration):
261
245
  ('name', models.CharField(max_length=255, unique=True)),
262
246
  ],
263
247
  ),
248
+ migrations.CreateModel(
249
+ name='Frame',
250
+ fields=[
251
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
252
+ ('frame_number', models.PositiveIntegerField()),
253
+ ('relative_path', models.CharField(max_length=512)),
254
+ ('timestamp', models.FloatField(blank=True, null=True)),
255
+ ('is_extracted', models.BooleanField(default=False)),
256
+ ],
257
+ options={
258
+ 'ordering': ['video', 'frame_number'],
259
+ },
260
+ ),
264
261
  migrations.CreateModel(
265
262
  name='Gender',
266
263
  fields=[
@@ -387,6 +384,18 @@ class Migration(migrations.Migration):
387
384
  ('name', models.CharField(max_length=255)),
388
385
  ],
389
386
  ),
387
+ migrations.CreateModel(
388
+ name='ProcessingHistory',
389
+ fields=[
390
+ ('file_hash', models.CharField(blank=True, help_text='Content hash of the original file (e.g. video_hash/pdf_hash).', max_length=64, primary_key=True, serialize=False)),
391
+ ('created_at', models.DateTimeField(auto_now_add=True)),
392
+ ('success', models.BooleanField(blank=True, default=False)),
393
+ ('object_id', models.PositiveBigIntegerField(blank=True, null=True)),
394
+ ],
395
+ options={
396
+ 'ordering': ['-created_at'],
397
+ },
398
+ ),
390
399
  migrations.CreateModel(
391
400
  name='Product',
392
401
  fields=[
@@ -441,11 +450,11 @@ class Migration(migrations.Migration):
441
450
  ('date_created', models.DateTimeField(auto_now_add=True)),
442
451
  ('date_modified', models.DateTimeField(auto_now=True)),
443
452
  ('was_created', models.BooleanField(default=True, help_text='True if this state was created for the first time.')),
444
- ('pdf_meta_extracted', models.BooleanField(default=False, help_text='True if PDF metadata has been extracted.')),
453
+ ('pdf_meta_extracted', models.BooleanField(default=False, help_text='True if report metadata has been extracted.')),
445
454
  ],
446
455
  options={
447
- 'verbose_name': 'Raw PDF Processing State',
448
- 'verbose_name_plural': 'Raw PDF Processing States',
456
+ 'verbose_name': 'Raw report Processing State',
457
+ 'verbose_name_plural': 'Raw report Processing States',
449
458
  },
450
459
  ),
451
460
  migrations.CreateModel(
@@ -700,11 +709,7 @@ class Migration(migrations.Migration):
700
709
  fields=[
701
710
  ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
702
711
  ('name', models.CharField(max_length=100, unique=True)),
703
- ('name_de', models.CharField(max_length=100, null=True, unique=True)),
704
- ('name_en', models.CharField(max_length=100, null=True, unique=True)),
705
712
  ('description', models.TextField(blank=True, null=True)),
706
- ('description_de', models.TextField(blank=True, null=True)),
707
- ('description_en', models.TextField(blank=True, null=True)),
708
713
  ('indications', models.ManyToManyField(blank=True, related_name='examinations', to='endoreg_db.examinationindication')),
709
714
  ('examination_times', models.ManyToManyField(blank=True, related_name='examinations', to='endoreg_db.examinationtime')),
710
715
  ('examination_types', models.ManyToManyField(blank=True, to='endoreg_db.examinationtype')),
@@ -844,6 +849,32 @@ class Migration(migrations.Migration):
844
849
  'abstract': False,
845
850
  },
846
851
  ),
852
+ migrations.CreateModel(
853
+ name='ImageClassificationAnnotation',
854
+ fields=[
855
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
856
+ ('value', models.BooleanField()),
857
+ ('float_value', models.FloatField(blank=True, null=True)),
858
+ ('annotator', models.CharField(blank=True, max_length=255, null=True)),
859
+ ('date_created', models.DateTimeField(auto_now_add=True)),
860
+ ('date_modified', models.DateTimeField(auto_now=True)),
861
+ ('frame', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='image_classification_annotations', to='endoreg_db.frame')),
862
+ ],
863
+ ),
864
+ migrations.CreateModel(
865
+ name='AIDataSet',
866
+ fields=[
867
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
868
+ ('name', models.CharField(blank=True, help_text='Human-readable identifier, e.g. "Legacy multilabel dataset v1".', max_length=255, null=True)),
869
+ ('description', models.TextField(blank=True, help_text='Optional notes / explanation about this dataset.', null=True)),
870
+ ('ai_model_type', models.CharField(default='image_multilabel_classification', help_text='AI model family this dataset is for, e.g. "image_multilabel_classification". Used to pick the correct architecture and output dimension logic.', max_length=255)),
871
+ ('dataset_type', models.CharField(choices=[('image', 'Image')], default='image', help_text='Controls which annotation table will be used. Currently only "image" is implemented; later "video", "text", etc.', max_length=32)),
872
+ ('created_at', models.DateTimeField(auto_now_add=True, help_text='When this AIDataSet was created.')),
873
+ ('updated_at', models.DateTimeField(auto_now=True, help_text='When this AIDataSet was last modified.')),
874
+ ('is_active', models.BooleanField(default=True, help_text='Soft toggle to enable/disable this dataset for training.')),
875
+ ('image_annotations', models.ManyToManyField(blank=True, help_text="For dataset_type='image', this is the set of ImageClassificationAnnotation rows that define this AIDataSet. Each annotation has frame_id and label_id.", related_name='image_ai_datasets', to='endoreg_db.imageclassificationannotation')),
876
+ ],
877
+ ),
847
878
  migrations.CreateModel(
848
879
  name='InformationSource',
849
880
  fields=[
@@ -862,6 +893,11 @@ class Migration(migrations.Migration):
862
893
  'indexes': [models.Index(fields=['name'], name='endoreg_db__name_febac5_idx'), models.Index(fields=['abbreviation'], name='endoreg_db__abbrevi_5a07c4_idx')],
863
894
  },
864
895
  ),
896
+ migrations.AddField(
897
+ model_name='imageclassificationannotation',
898
+ name='information_source',
899
+ field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='image_classification_annotations', to='endoreg_db.informationsource'),
900
+ ),
865
901
  migrations.AddField(
866
902
  model_name='findingintervention',
867
903
  name='information_sources',
@@ -905,6 +941,11 @@ class Migration(migrations.Migration):
905
941
  'verbose_name_plural': 'Information Source Types',
906
942
  },
907
943
  ),
944
+ migrations.AddField(
945
+ model_name='imageclassificationannotation',
946
+ name='label',
947
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='image_classification_annotations', to='endoreg_db.label'),
948
+ ),
908
949
  migrations.CreateModel(
909
950
  name='LabelSet',
910
951
  fields=[
@@ -1013,6 +1054,11 @@ class Migration(migrations.Migration):
1013
1054
  'unique_together': {('name', 'version', 'model')},
1014
1055
  },
1015
1056
  ),
1057
+ migrations.AddField(
1058
+ model_name='imageclassificationannotation',
1059
+ name='model_meta',
1060
+ field=models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='image_classification_annotations', to='endoreg_db.modelmeta'),
1061
+ ),
1016
1062
  migrations.AddField(
1017
1063
  model_name='aimodel',
1018
1064
  name='active_meta',
@@ -1050,6 +1096,30 @@ class Migration(migrations.Migration):
1050
1096
  ('default_unit', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, to='endoreg_db.unit')),
1051
1097
  ],
1052
1098
  ),
1099
+ migrations.CreateModel(
1100
+ name='OperationLog',
1101
+ fields=[
1102
+ ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
1103
+ ('actor_username', models.CharField(blank=True, max_length=150)),
1104
+ ('actor_email', models.EmailField(blank=True, max_length=254)),
1105
+ ('actor_keycloak_id', models.CharField(blank=True, help_text='Keycloak subject/ID if you later want to store it.', max_length=255)),
1106
+ ('action', models.CharField(help_text="e.g. 'anonymization.start', 'anonymization.validate'", max_length=100)),
1107
+ ('http_method', models.CharField(blank=True, max_length=10)),
1108
+ ('path', models.CharField(blank=True, max_length=512)),
1109
+ ('resource_type', models.CharField(blank=True, help_text="e.g. 'video', 'pdf'", max_length=50)),
1110
+ ('resource_id', models.IntegerField(blank=True, help_text='ID of VideoFile / RawPdfFile etc.', null=True)),
1111
+ ('status_before', models.CharField(blank=True, max_length=50)),
1112
+ ('status_after', models.CharField(blank=True, max_length=50)),
1113
+ ('meta', models.JSONField(blank=True, null=True)),
1114
+ ('created_at', models.DateTimeField(auto_now_add=True)),
1115
+ ('actor_user', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='operation_logs', to=settings.AUTH_USER_MODEL)),
1116
+ ],
1117
+ options={
1118
+ 'verbose_name': 'Operation Log',
1119
+ 'verbose_name_plural': 'Operation Logs',
1120
+ 'ordering': ['-created_at'],
1121
+ },
1122
+ ),
1053
1123
  migrations.CreateModel(
1054
1124
  name='Patient',
1055
1125
  fields=[
@@ -1149,7 +1219,7 @@ class Migration(migrations.Migration):
1149
1219
  ('text', models.TextField(blank=True, null=True)),
1150
1220
  ('date', models.DateField(blank=True, null=True)),
1151
1221
  ('time', models.TimeField(blank=True, null=True)),
1152
- ('file', models.FileField(blank=True, null=True, upload_to='pdfs')),
1222
+ ('file', models.FileField(blank=True, null=True, upload_to='documents')),
1153
1223
  ('center', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='endoreg_db.center')),
1154
1224
  ('type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='endoreg_db.documenttype')),
1155
1225
  ('examiners', models.ManyToManyField(blank=True, to='endoreg_db.examiner')),
@@ -1457,9 +1527,8 @@ class Migration(migrations.Migration):
1457
1527
  ('text', models.TextField(blank=True, null=True)),
1458
1528
  ('date_created', models.DateTimeField(auto_now_add=True)),
1459
1529
  ('date_modified', models.DateTimeField(auto_now=True)),
1460
- ('anonymized', models.BooleanField(default=False, help_text='True if the PDF has been anonymized.')),
1461
- ('file', models.FileField(upload_to='pdfs', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['pdf'])])),
1462
- ('anonymized_file', models.FileField(blank=True, null=True, upload_to='pdfs', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['pdf'])])),
1530
+ ('file', models.FileField(upload_to='sensitive_reports', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['pdf'])])),
1531
+ ('processed_file', models.FileField(blank=True, null=True, upload_to='processed_reports_final', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['pdf'])])),
1463
1532
  ('state_report_processing_required', models.BooleanField(default=True)),
1464
1533
  ('state_report_processed', models.BooleanField(default=False)),
1465
1534
  ('raw_meta', models.JSONField(blank=True, null=True)),
@@ -1482,7 +1551,7 @@ class Migration(migrations.Migration):
1482
1551
  ('text', models.TextField(blank=True, null=True)),
1483
1552
  ('date', models.DateField(blank=True, null=True)),
1484
1553
  ('time', models.TimeField(blank=True, null=True)),
1485
- ('file', models.FileField(blank=True, null=True, upload_to='pdfs')),
1554
+ ('file', models.FileField(blank=True, null=True, upload_to='documents')),
1486
1555
  ('center', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='endoreg_db.center')),
1487
1556
  ('type', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.SET_NULL, to='endoreg_db.documenttype')),
1488
1557
  ('examiners', models.ManyToManyField(blank=True, to='endoreg_db.examiner')),
@@ -1634,7 +1703,7 @@ class Migration(migrations.Migration):
1634
1703
  name='UploadJob',
1635
1704
  fields=[
1636
1705
  ('id', models.UUIDField(default=uuid.uuid4, editable=False, help_text='Unique identifier for the upload job', primary_key=True, serialize=False)),
1637
- ('file', models.FileField(help_text='Uploaded file (PDF or video)', upload_to='uploads/%Y/%m/%d/')),
1706
+ ('file', models.FileField(help_text='Uploaded file (report or video)', upload_to='uploads/%Y/%m/%d/')),
1638
1707
  ('status', models.CharField(choices=[('pending', 'Pending'), ('processing', 'Processing'), ('anonymized', 'Anonymized'), ('error', 'Error')], default='pending', help_text='Current processing status of the upload', max_length=20)),
1639
1708
  ('content_type', models.CharField(blank=True, help_text='MIME type of the uploaded file', max_length=100)),
1640
1709
  ('error_detail', models.TextField(blank=True, help_text='Error message if processing failed')),
@@ -1652,9 +1721,8 @@ class Migration(migrations.Migration):
1652
1721
  name='VideoFile',
1653
1722
  fields=[
1654
1723
  ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
1655
- ('uuid', models.UUIDField(default=uuid.uuid4, editable=False, unique=True)),
1656
- ('raw_file', models.FileField(blank=True, null=True, upload_to='videos', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['mp4'])])),
1657
- ('processed_file', models.FileField(blank=True, null=True, upload_to='videos', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['mp4'])])),
1724
+ ('raw_file', models.FileField(blank=True, null=True, upload_to='sensitive_videos', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['mp4'])])),
1725
+ ('processed_file', models.FileField(blank=True, null=True, upload_to='processed_videos_final', validators=[django.core.validators.FileExtensionValidator(allowed_extensions=['mp4'])])),
1658
1726
  ('video_hash', models.CharField(help_text='Hash of the raw video file.', max_length=255, unique=True)),
1659
1727
  ('processed_video_hash', models.CharField(blank=True, help_text='Hash of the processed video file, unique if not null.', max_length=255, null=True, unique=True)),
1660
1728
  ('original_file_name', models.CharField(blank=True, max_length=255, null=True)),
@@ -1690,19 +1758,10 @@ class Migration(migrations.Migration):
1690
1758
  name='video_file',
1691
1759
  field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='label_video_segments', to='endoreg_db.videofile'),
1692
1760
  ),
1693
- migrations.CreateModel(
1694
- name='Frame',
1695
- fields=[
1696
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
1697
- ('frame_number', models.PositiveIntegerField()),
1698
- ('relative_path', models.CharField(max_length=512)),
1699
- ('timestamp', models.FloatField(blank=True, null=True)),
1700
- ('is_extracted', models.BooleanField(default=False)),
1701
- ('video', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frames', to='endoreg_db.videofile')),
1702
- ],
1703
- options={
1704
- 'ordering': ['video', 'frame_number'],
1705
- },
1761
+ migrations.AddField(
1762
+ model_name='frame',
1763
+ name='video',
1764
+ field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='frames', to='endoreg_db.videofile'),
1706
1765
  ),
1707
1766
  migrations.CreateModel(
1708
1767
  name='VideoMeta',
@@ -1815,23 +1874,13 @@ class Migration(migrations.Migration):
1815
1874
  ('waste', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='endoreg_db.waste')),
1816
1875
  ],
1817
1876
  ),
1818
- migrations.CreateModel(
1819
- name='ImageClassificationAnnotation',
1820
- fields=[
1821
- ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
1822
- ('value', models.BooleanField()),
1823
- ('float_value', models.FloatField(blank=True, null=True)),
1824
- ('annotator', models.CharField(blank=True, max_length=255, null=True)),
1825
- ('date_created', models.DateTimeField(auto_now_add=True)),
1826
- ('date_modified', models.DateTimeField(auto_now=True)),
1827
- ('frame', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='image_classification_annotations', to='endoreg_db.frame')),
1828
- ('information_source', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='image_classification_annotations', to='endoreg_db.informationsource')),
1829
- ('label', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='image_classification_annotations', to='endoreg_db.label')),
1830
- ('model_meta', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='image_classification_annotations', to='endoreg_db.modelmeta')),
1831
- ],
1832
- options={
1833
- 'indexes': [models.Index(fields=['frame', 'label'], name='endoreg_db__frame_i_47f916_idx'), models.Index(fields=['frame'], name='endoreg_db__frame_i_92c75a_idx')],
1834
- },
1877
+ migrations.AddIndex(
1878
+ model_name='imageclassificationannotation',
1879
+ index=models.Index(fields=['frame', 'label'], name='endoreg_db__frame_i_47f916_idx'),
1880
+ ),
1881
+ migrations.AddIndex(
1882
+ model_name='imageclassificationannotation',
1883
+ index=models.Index(fields=['frame'], name='endoreg_db__frame_i_92c75a_idx'),
1835
1884
  ),
1836
1885
  migrations.AddConstraint(
1837
1886
  model_name='patientexternalid',
File without changes
@@ -151,6 +151,12 @@ from .state import (
151
151
  VideoState,
152
152
  )
153
153
 
154
+ # AI dataset model
155
+ from .aidataset import (
156
+ AIDataSet,
157
+ )
158
+
159
+
154
160
  __all__ = [
155
161
  ####### Administration ########
156
162
  # AI
@@ -311,4 +317,6 @@ __all__ = [
311
317
  "LabelVideoSegmentState",
312
318
  "AnonymizationState",
313
319
  "RawPdfState",
320
+ ###### AIDATASET ######
321
+ "AIDataSet",
314
322
  ]
@@ -8,7 +8,8 @@ if TYPE_CHECKING:
8
8
  class ActiveModelManager(models.Manager):
9
9
  def get_by_natural_key(self, name):
10
10
  return self.get(name=name)
11
-
11
+
12
+
12
13
  class ActiveModel(models.Model):
13
14
  """
14
15
  ActiveModel represents an active instance of a model within the application.
@@ -26,10 +27,9 @@ class ActiveModel(models.Model):
26
27
  """
27
28
 
28
29
  name = models.CharField(max_length=255, unique=True)
29
-
30
- model_meta:models.ForeignKey["ModelMeta|None"] = models.ForeignKey(
31
- 'ModelMeta', on_delete=models.SET_NULL,
32
- blank=True, null=True
30
+
31
+ model_meta: models.ForeignKey["ModelMeta|None"] = models.ForeignKey(
32
+ "ModelMeta", on_delete=models.SET_NULL, blank=True, null=True
33
33
  )
34
34
 
35
35
  objects = ActiveModelManager()