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,21 +1,25 @@
1
- import os
2
1
  import subprocess
3
2
  import pathlib
4
3
  import math
5
4
  import logging
6
- import json
7
5
 
8
6
  # Setup basic logging
9
- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
7
+ logging.basicConfig(
8
+ level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
9
+ )
10
+
10
11
 
11
12
  def get_video_duration(video_path: pathlib.Path) -> float:
12
13
  """Gets the duration of a video file using ffprobe."""
13
14
  cmd = [
14
15
  "ffprobe",
15
- "-v", "error",
16
- "-show_entries", "format=duration",
17
- "-of", "default=noprint_wrappers=1:nokey=1",
18
- str(video_path)
16
+ "-v",
17
+ "error",
18
+ "-show_entries",
19
+ "format=duration",
20
+ "-of",
21
+ "default=noprint_wrappers=1:nokey=1",
22
+ str(video_path),
19
23
  ]
20
24
  try:
21
25
  result = subprocess.run(cmd, capture_output=True, text=True, check=True)
@@ -28,6 +32,7 @@ def get_video_duration(video_path: pathlib.Path) -> float:
28
32
  logging.error(f"Could not parse duration from ffprobe output: {result.stdout}")
29
33
  raise
30
34
 
35
+
31
36
  def split_video(input_path: str, interval: int):
32
37
  """
33
38
  Splits a video into segments of a specified interval using ffmpeg.
@@ -54,11 +59,13 @@ def split_video(input_path: str, interval: int):
54
59
  return
55
60
 
56
61
  num_segments = math.ceil(duration / interval)
57
- logging.info(f"Splitting into {num_segments} segments of approximately {interval} seconds each.")
62
+ logging.info(
63
+ f"Splitting into {num_segments} segments of approximately {interval} seconds each."
64
+ )
58
65
 
59
66
  for i in range(num_segments):
60
67
  start_time = i * interval
61
- output_filename = output_dir / f"segment_{i+1:03d}{input_file.suffix}"
68
+ output_filename = output_dir / f"segment_{i + 1:03d}{input_file.suffix}"
62
69
 
63
70
  # Use -t for interval duration. For the last segment, ffmpeg with -c copy
64
71
  # might automatically stop at the end, or we could calculate exact duration.
@@ -69,26 +76,30 @@ def split_video(input_path: str, interval: int):
69
76
 
70
77
  cmd = [
71
78
  "ffmpeg",
72
- "-i", str(input_file),
73
- "-ss", str(start_time),
74
- "-t", str(interval),
75
- "-c", "copy", # Fast, lossless splitting
76
- "-avoid_negative_ts", "make_zero", # Avoids issues with negative timestamps
77
- str(output_filename)
79
+ "-i",
80
+ str(input_file),
81
+ "-ss",
82
+ str(start_time),
83
+ "-t",
84
+ str(interval),
85
+ "-c",
86
+ "copy", # Fast, lossless splitting
87
+ "-avoid_negative_ts",
88
+ "make_zero", # Avoids issues with negative timestamps
89
+ str(output_filename),
78
90
  ]
79
91
 
80
92
  logging.info(f"Running command: {' '.join(cmd)}")
81
93
  try:
82
94
  result = subprocess.run(cmd, capture_output=True, text=True, check=True)
83
95
  logging.info(f"Successfully created segment: {output_filename}")
84
- if result.stderr: # ffmpeg often outputs info to stderr
85
- logging.debug(f"ffmpeg output for segment {i+1}:\n{result.stderr}")
96
+ if result.stderr: # ffmpeg often outputs info to stderr
97
+ logging.debug(f"ffmpeg output for segment {i + 1}:\n{result.stderr}")
86
98
  except subprocess.CalledProcessError as e:
87
- logging.error(f"Error creating segment {i+1}: {output_filename}")
99
+ logging.error(f"Error creating segment {i + 1}: {output_filename}")
88
100
  logging.error(f"Command failed: {' '.join(cmd)}")
89
101
  logging.error(f"ffmpeg stderr:\n{e.stderr}")
90
102
  # Decide if you want to stop on error or continue
91
103
  # return # Uncomment to stop on first error
92
104
 
93
105
  logging.info("Video splitting completed.")
94
-
@@ -4,19 +4,17 @@ from rest_framework import status
4
4
  from endoreg_db.models import VideoFile
5
5
  from ..serializers.Frames_NICE_and_PARIS_classifications import (
6
6
  ForNiceClassificationSerializer,
7
- ForParisClassificationSerializer
7
+ ForParisClassificationSerializer,
8
8
  )
9
9
 
10
- import logging
11
-
12
10
 
13
11
  class ForNiceClassificationView(APIView):
14
12
  """
15
13
  NICE Classification API View
16
-
14
+
17
15
  GET: Führt NICE-Klassifikation für alle Videos durch
18
16
  POST: Führt NICE-Klassifikation für spezifizierte Videos durch
19
-
17
+
20
18
  POST Body: {"video_ids": [1, 2, 3]} oder leerer Body für alle Videos
21
19
  """
22
20
 
@@ -34,12 +32,14 @@ class ForNiceClassificationView(APIView):
34
32
  try:
35
33
  # Handle POST data for specific video IDs
36
34
  video_ids = None
37
- if request.method == 'POST' and hasattr(request, 'data'):
38
- video_ids = request.data.get('video_ids', None)
35
+ if request.method == "POST" and hasattr(request, "data"):
36
+ video_ids = request.data.get("video_ids", None)
39
37
 
40
38
  if video_ids:
41
39
  videos = VideoFile.objects.filter(id__in=video_ids)
42
- print(f"[DEBUG] Processing NICE classification for specific videos: {video_ids}")
40
+ print(
41
+ f"[DEBUG] Processing NICE classification for specific videos: {video_ids}"
42
+ )
43
43
  else:
44
44
  videos = VideoFile.objects.all()
45
45
  print("[DEBUG] Processing NICE classification for all videos")
@@ -51,7 +51,7 @@ class ForNiceClassificationView(APIView):
51
51
  if not videos.exists():
52
52
  return Response(
53
53
  {"error": "No videos found in the database."},
54
- status=status.HTTP_404_NOT_FOUND
54
+ status=status.HTTP_404_NOT_FOUND,
55
55
  )
56
56
 
57
57
  serializer = ForNiceClassificationSerializer()
@@ -60,7 +60,7 @@ class ForNiceClassificationView(APIView):
60
60
  if not response_data:
61
61
  return Response(
62
62
  {"error": "No valid segments for NICE classification."},
63
- status=status.HTTP_404_NOT_FOUND
63
+ status=status.HTTP_404_NOT_FOUND,
64
64
  )
65
65
 
66
66
  return Response(response_data, status=status.HTTP_200_OK)
@@ -68,17 +68,17 @@ class ForNiceClassificationView(APIView):
68
68
  except Exception as e:
69
69
  return Response(
70
70
  {"error": f"Internal server error: {str(e)}"},
71
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
71
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
72
72
  )
73
73
 
74
74
 
75
75
  class ForParisClassificationView(APIView):
76
76
  """
77
77
  PARIS Classification API View
78
-
78
+
79
79
  GET: Führt PARIS-Klassifikation für alle Videos durch
80
80
  POST: Führt PARIS-Klassifikation für spezifizierte Videos durch
81
-
81
+
82
82
  POST Body: {"video_ids": [1, 2, 3]} oder leerer Body für alle Videos
83
83
  """
84
84
 
@@ -96,27 +96,32 @@ class ForParisClassificationView(APIView):
96
96
  try:
97
97
  # Handle POST data for specific video IDs
98
98
  video_ids = None
99
- if request.method == 'POST' and hasattr(request, 'data'):
100
- video_ids = request.data.get('video_ids', None)
99
+ if request.method == "POST" and hasattr(request, "data"):
100
+ video_ids = request.data.get("video_ids", None)
101
101
 
102
102
  if video_ids:
103
103
  videos = VideoFile.objects.filter(id__in=video_ids)
104
- print(f"[DEBUG] Processing PARIS classification for specific videos: {video_ids}")
104
+ print(
105
+ f"[DEBUG] Processing PARIS classification for specific videos: {video_ids}"
106
+ )
105
107
  else:
106
108
  videos = VideoFile.objects.all()
107
- print(f"[DEBUG] Processing PARIS classification for all videos")
109
+ print("[DEBUG] Processing PARIS classification for all videos")
108
110
 
109
111
  print(f"[DEBUG] Total videos found: {videos.count()}")
110
112
 
111
113
  filtered_videos = [
112
- video for video in videos
113
- if getattr(video, "frame_dir", None) # no more readable_predictions check
114
+ video
115
+ for video in videos
116
+ if getattr(
117
+ video, "frame_dir", None
118
+ ) # no more readable_predictions check
114
119
  ]
115
120
 
116
121
  if not filtered_videos:
117
122
  return Response(
118
123
  {"error": "No videos with valid frame_dir found."},
119
- status=status.HTTP_404_NOT_FOUND
124
+ status=status.HTTP_404_NOT_FOUND,
120
125
  )
121
126
 
122
127
  serializer = ForParisClassificationSerializer()
@@ -125,7 +130,7 @@ class ForParisClassificationView(APIView):
125
130
  if not response_data:
126
131
  return Response(
127
132
  {"error": "No valid PARIS segments found."},
128
- status=status.HTTP_404_NOT_FOUND
133
+ status=status.HTTP_404_NOT_FOUND,
129
134
  )
130
135
 
131
136
  return Response(response_data, status=status.HTTP_200_OK)
@@ -133,16 +138,16 @@ class ForParisClassificationView(APIView):
133
138
  except Exception as e:
134
139
  return Response(
135
140
  {"error": f"Internal server error: {str(e)}"},
136
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
141
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
137
142
  )
138
143
 
139
144
 
140
145
  class BatchClassificationView(APIView):
141
146
  """
142
147
  Batch Classification API View
143
-
148
+
144
149
  POST: Führt beide Klassifikationstypen (NICE und PARIS) für spezifizierte Videos durch
145
-
150
+
146
151
  POST Body: {
147
152
  "video_ids": [1, 2, 3],
148
153
  "types": ["nice", "paris"] # Optional, default beide
@@ -151,8 +156,8 @@ class BatchClassificationView(APIView):
151
156
 
152
157
  def post(self, request):
153
158
  try:
154
- video_ids = request.data.get('video_ids', None)
155
- classification_types = request.data.get('types', ['nice', 'paris'])
159
+ video_ids = request.data.get("video_ids", None)
160
+ classification_types = request.data.get("types", ["nice", "paris"])
156
161
 
157
162
  if video_ids:
158
163
  videos = VideoFile.objects.filter(id__in=video_ids)
@@ -161,67 +166,71 @@ class BatchClassificationView(APIView):
161
166
 
162
167
  if not videos.exists():
163
168
  return Response(
164
- {"error": "No videos found."},
165
- status=status.HTTP_404_NOT_FOUND
169
+ {"error": "No videos found."}, status=status.HTTP_404_NOT_FOUND
166
170
  )
167
171
 
168
172
  results = {}
169
173
 
170
- if 'nice' in classification_types:
174
+ if "nice" in classification_types:
171
175
  nice_serializer = ForNiceClassificationSerializer()
172
- results['nice'] = nice_serializer.to_representation(videos)
176
+ results["nice"] = nice_serializer.to_representation(videos)
173
177
 
174
- if 'paris' in classification_types:
178
+ if "paris" in classification_types:
175
179
  # Filter videos for PARIS (need frame_dir)
176
180
  filtered_videos = [
177
- video for video in videos
178
- if getattr(video, "frame_dir", None)
181
+ video for video in videos if getattr(video, "frame_dir", None)
179
182
  ]
180
-
183
+
181
184
  if filtered_videos:
182
185
  paris_serializer = ForParisClassificationSerializer()
183
- results['paris'] = paris_serializer.to_representation(filtered_videos)
186
+ results["paris"] = paris_serializer.to_representation(
187
+ filtered_videos
188
+ )
184
189
  else:
185
- results['paris'] = {"error": "No videos with valid frame_dir found for PARIS classification."}
190
+ results["paris"] = {
191
+ "error": "No videos with valid frame_dir found for PARIS classification."
192
+ }
186
193
 
187
- return Response({
188
- "message": "Batch classification completed.",
189
- "results": results
190
- }, status=status.HTTP_200_OK)
194
+ return Response(
195
+ {"message": "Batch classification completed.", "results": results},
196
+ status=status.HTTP_200_OK,
197
+ )
191
198
 
192
199
  except Exception as e:
193
200
  return Response(
194
201
  {"error": f"Internal server error: {str(e)}"},
195
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
202
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
196
203
  )
197
204
 
198
205
 
199
206
  class ClassificationStatusView(APIView):
200
207
  """
201
208
  Classification Status API View
202
-
209
+
203
210
  GET: Gibt den Status der Klassifikationen für ein Video zurück
204
211
  """
205
212
 
206
213
  def get(self, request, video_id):
207
214
  try:
208
215
  video = VideoFile.objects.get(id=video_id)
209
-
216
+
210
217
  # Check if classifications exist for this video
211
218
  # This would typically check for saved classification results in the database
212
219
  # For now, we'll return basic status information
213
-
220
+
214
221
  status_info = {
215
222
  "video_id": video_id,
216
- "video_name": getattr(video, 'original_file_name', 'Unknown'),
223
+ "video_name": getattr(video, "original_file_name", "Unknown"),
217
224
  "has_frame_dir": bool(getattr(video, "frame_dir", None)),
218
225
  "nice_classification_available": True, # Always available for NICE
219
- "paris_classification_available": bool(getattr(video, "frame_dir", None)),
226
+ "paris_classification_available": bool(
227
+ getattr(video, "frame_dir", None)
228
+ ),
220
229
  "last_processed": None, # Would come from classification results table
221
230
  "classification_results": {
222
231
  "nice": None, # Would contain saved NICE results
223
- "paris": None # Would contain saved PARIS results
224
- }
232
+ "paris": None, # Would contain saved PARIS results
233
+ },
225
234
  }
226
235
 
227
236
  return Response(status_info, status=status.HTTP_200_OK)
@@ -229,10 +238,10 @@ class ClassificationStatusView(APIView):
229
238
  except VideoFile.DoesNotExist:
230
239
  return Response(
231
240
  {"error": f"Video with ID {video_id} not found."},
232
- status=status.HTTP_404_NOT_FOUND
241
+ status=status.HTTP_404_NOT_FOUND,
233
242
  )
234
243
  except Exception as e:
235
244
  return Response(
236
245
  {"error": f"Internal server error: {str(e)}"},
237
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
246
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
238
247
  )
@@ -43,20 +43,11 @@ from .media import (
43
43
  )
44
44
  from .meta import SensitiveMetaListView, SensitiveMetaVerificationView
45
45
  from .misc import (
46
- MODELTRANSLATION_SETTINGS,
47
46
  CenterViewSet,
48
47
  ExaminationStatsView,
49
- ExaminationTranslationOptions,
50
- FindingClassificationChoiceTranslationOptions,
51
- FindingClassificationTranslationOptions,
52
- FindingTranslationOptions,
53
48
  GenderViewSet,
54
49
  GeneralStatsView,
55
- InterventionTranslationOptions,
56
50
  SensitiveMetaStatsView,
57
- TranslatedFieldMixin,
58
- TranslatedFixtureLoader,
59
- TranslationMigrationHelper,
60
51
  UploadFileView,
61
52
  UploadStatusView,
62
53
  VideoSegmentStatsView,
@@ -77,7 +68,6 @@ from .video import ( # Video Correction (Phase 1.1) - Implemented; Existing vie
77
68
  VideoApplyMaskView,
78
69
  VideoCorrectionView,
79
70
  VideoExaminationViewSet,
80
- VideoProcessingHistoryView,
81
71
  VideoReimportView,
82
72
  VideoRemoveFramesView,
83
73
  VideoStreamView,
@@ -127,16 +117,7 @@ __all__ = [
127
117
  "VideoSegmentStatsView",
128
118
  "SensitiveMetaStatsView",
129
119
  "GeneralStatsView",
130
- "ExaminationTranslationOptions",
131
- "FindingTranslationOptions",
132
- "FindingClassificationTranslationOptions",
133
- "FindingClassificationChoiceTranslationOptions",
134
- "InterventionTranslationOptions",
135
- "TranslatedFieldMixin",
136
- "TranslationMigrationHelper",
137
- "TranslatedFixtureLoader",
138
120
  "build_multilingual_response",
139
- "MODELTRANSLATION_SETTINGS",
140
121
  "UploadFileView",
141
122
  "UploadStatusView",
142
123
  # Patient Views
@@ -158,7 +139,6 @@ __all__ = [
158
139
  "evaluate_requirements",
159
140
  "LookupViewSet",
160
141
  # Video Views (Phase 1.1 - Implemented)
161
- "VideoProcessingHistoryView",
162
142
  "VideoApplyMaskView",
163
143
  "VideoRemoveFramesView",
164
144
  "VideoCorrectionView",
@@ -10,7 +10,11 @@ from .overview import (
10
10
 
11
11
  from .validate import AnonymizationValidateView
12
12
 
13
- from .media_management import MediaManagementView, force_remove_media, reset_processing_status
13
+ from .media_management import (
14
+ MediaManagementView,
15
+ force_remove_media,
16
+ reset_processing_status,
17
+ )
14
18
 
15
19
  __all__ = [
16
20
  "AnonymizationOverviewView",
@@ -24,4 +28,4 @@ __all__ = [
24
28
  "force_remove_media",
25
29
  "reset_processing_status",
26
30
  "has_raw_video_file",
27
- ]
31
+ ]
@@ -5,9 +5,7 @@ from datetime import timedelta
5
5
  from typing import Any, Dict
6
6
 
7
7
  from django.db import transaction
8
- from django.db.models import Q
9
8
  from django.utils import timezone
10
- from numpy import delete
11
9
  from rest_framework import status
12
10
  from rest_framework.decorators import api_view, permission_classes
13
11
  from rest_framework.response import Response
@@ -195,12 +193,10 @@ class MediaManagementView(APIView):
195
193
  pdf_file_obj = None
196
194
 
197
195
  if media_type == "video":
198
- video_file_obj = (
199
- VideoFile.get_video_by_pk(pk=file_id) if file_id else None
200
- )
196
+ video_file_obj = VideoFile.get_video_by_pk(pk=file_id) if file_id else None
201
197
 
202
198
  elif media_type == "pdf":
203
- pdf_file_obj = RawPdfFile.get_pdf_by_pk(pk=file_id) if file_id else None
199
+ pdf_file_obj = RawPdfFile.get_report_by_pk(pk=file_id) if file_id else None
204
200
 
205
201
  with transaction.atomic():
206
202
  if video_file_obj:
@@ -19,6 +19,8 @@ from endoreg_db.services.polling_coordinator import (
19
19
  from endoreg_db.utils.permissions import DEBUG_PERMISSIONS
20
20
 
21
21
  from ...serializers import FileOverviewSerializer, VoPPatientDataSerializer
22
+ from endoreg_db.utils.operation_log import record_operation
23
+
22
24
 
23
25
  logger = logging.getLogger(__name__)
24
26
  PERMS = DEBUG_PERMISSIONS # shorten
@@ -127,7 +129,10 @@ def start_anonymization(request, file_id: int):
127
129
  if not info:
128
130
  return Response({"detail": "File not found"}, status=status.HTTP_404_NOT_FOUND)
129
131
 
130
- file_type = info["mediaType"]
132
+ file_type = info.get("mediaType") or "unknown"
133
+ status_before = (
134
+ info.get("anonymizationStatus") or info.get("status") or "not_started"
135
+ )
131
136
 
132
137
  # Use processing lock context to prevent duplicate processing
133
138
  with ProcessingLockContext(file_id, file_type) as lock:
@@ -151,6 +156,34 @@ def start_anonymization(request, file_id: int):
151
156
  status=status.HTTP_500_INTERNAL_SERVER_ERROR,
152
157
  )
153
158
 
159
+ # Re-read status AFTER starting
160
+ try:
161
+ info_after = AnonymizationService.get_status(file_id) or {}
162
+ except Exception:
163
+ logger.exception(
164
+ "Failed to refresh anonymization status for file %s", file_id
165
+ )
166
+ info_after = {}
167
+
168
+ status_after = (
169
+ info_after.get("anonymizationStatus")
170
+ or info_after.get("status")
171
+ or status_before
172
+ )
173
+
174
+ # 🔐 Write operation log
175
+ record_operation(
176
+ request,
177
+ action="anonymization.start",
178
+ resource_type=kind, # 'video' or 'pdf' as returned by service.start
179
+ resource_id=file_id,
180
+ status_before=str(status_before),
181
+ status_after=str(status_after),
182
+ meta={
183
+ "file_type_from_status": file_type,
184
+ },
185
+ )
186
+
154
187
  return Response(
155
188
  {
156
189
  "detail": f"Anonymization started for {kind} file",
@@ -9,6 +9,9 @@ from rest_framework.views import APIView
9
9
  from endoreg_db.models import RawPdfFile, VideoFile
10
10
  from endoreg_db.models.metadata import SensitiveMeta
11
11
  from endoreg_db.serializers.anonymization import SensitiveMetaValidateSerializer
12
+ from endoreg_db.utils.operation_log import (
13
+ record_operation, # only touched the parts where validation succeeds
14
+ )
12
15
 
13
16
  logger = logging.getLogger(__name__)
14
17
 
@@ -17,7 +20,7 @@ class AnonymizationValidateView(APIView):
17
20
  """
18
21
  POST /api/anonymization/<int:file_id>/validate/
19
22
 
20
- Validiert und aktualisiert SensitiveMeta-Felder für Videos oder PDFs.
23
+ Validiert und aktualisiert SensitiveMeta-Felder für Videos oder reports.
21
24
 
22
25
  DATA HERE IS COMING FROM THE ANONYIZATION VALIDATION COMPONENT
23
26
 
@@ -30,7 +33,7 @@ class AnonymizationValidateView(APIView):
30
33
  "examination_date": "15.02.2024", // DD.MM.YYYY bevorzugt
31
34
 
32
35
  "casenumber": "12345",
33
- "anonymized_text": "...", // nur für PDFs; Videos ignorieren
36
+ "anonymized_text": "...", // nur für reports; Videos ignorieren
34
37
  "is_verified": true // optional; default true
35
38
  "file_type": "video" // optional; "video" oder "pdf"; wenn nicht angegeben, wird zuerst Video, dann report versucht
36
39
  "center_name": editedPatient.value.centerName || '',
@@ -54,6 +57,7 @@ class AnonymizationValidateView(APIView):
54
57
  payload["is_verified"] = True
55
58
 
56
59
  file_type = payload.get("file_type")
60
+ status_before = None
57
61
 
58
62
  with transaction.atomic():
59
63
  # Try Video first (unless explicitly requesting report)
@@ -65,6 +69,7 @@ class AnonymizationValidateView(APIView):
65
69
  .filter(pk=file_id)
66
70
  .first()
67
71
  )
72
+ # TODO: The state for video will be none when no state is set and the state for pdf will always be none. After status needs to be inferred after calling the sensitive meta state update functions
68
73
  if video is not None:
69
74
  prepared_payload = self._prepare_payload(payload, video)
70
75
  try:
@@ -103,9 +108,43 @@ class AnonymizationValidateView(APIView):
103
108
  )
104
109
 
105
110
  if video.state is not None:
106
- video.state.state.anonymization_status.mark_anonymized()
107
- video.state.save(update_fields=["anonymized"])
111
+ video.state.anonymized = True
108
112
  video.sensitive_meta.state.save()
113
+ try:
114
+ if video.state is not None:
115
+ st = getattr(video.state, "anonymization_status", None)
116
+ if st is not None:
117
+ status_before = str(getattr(st, "value", st))
118
+ except Exception:
119
+ logger.exception(
120
+ "Failed to read video anonymization_status before validation"
121
+ )
122
+
123
+ # --- NEW: status AFTER validation ---
124
+ status_after = status_before
125
+ try:
126
+ if video.state is not None:
127
+ video.state.refresh_from_db()
128
+ st_after = getattr(
129
+ video.state, "anonymization_status", None
130
+ )
131
+ if st_after is not None:
132
+ status_after = str(getattr(st_after, "value", st_after))
133
+ except Exception:
134
+ logger.exception(
135
+ "Failed to read video anonymization_status after validation"
136
+ )
137
+
138
+ # --- write operation log ---
139
+ # TODO: update the function call bases on the status , once merged
140
+ record_operation(
141
+ request,
142
+ action="anonymization.validated",
143
+ resource_type="video",
144
+ resource_id=file_id,
145
+ status_before=status_before,
146
+ status_after=status_after,
147
+ )
109
148
 
110
149
  return Response(
111
150
  {"message": "Video validated."},
@@ -161,25 +200,47 @@ class AnonymizationValidateView(APIView):
161
200
  pdf.save(update_fields=["sensitive_meta"])
162
201
  pdf.sensitive_meta.get_or_create_state()
163
202
  if (
164
- pdf.sensitive_meta
165
- and pdf.sensitive_meta.state
166
- and pdf.state
167
- ):
168
- pdf.sensitive_meta.state.refresh_from_db()
169
- pdf.sensitive_meta.state.mark_dob_verified()
170
- pdf.sensitive_meta.state.mark_names_verified()
171
- pdf.sensitive_meta.create_anonymized_record()
172
- pdf.state.state.anonymization_status.mark_anonymized()
173
- pdf.state.save(update_fields=["anonymized"])
174
- pdf.sensitive_meta.state.save()
203
+ pdf.sensitive_meta
204
+ and pdf.sensitive_meta.state
205
+ ):
206
+ pdf.sensitive_meta.state.refresh_from_db()
207
+ pdf.sensitive_meta.state.mark_dob_verified()
208
+ pdf.sensitive_meta.state.mark_names_verified()
209
+ pdf.sensitive_meta.create_anonymized_record()
210
+
211
+ if pdf.state:
212
+ pdf.state.mark_anonymized()
213
+ pdf.state.save(update_fields=["anonymized"])
214
+
215
+ pdf.sensitive_meta.state.save()
175
216
  else:
176
217
  return Response(
177
- {
178
- "message": "report not validated, failed to create State."
179
- },
218
+ {"message": "report not validated, failed to create State."},
180
219
  status=status.HTTP_500_INTERNAL_SERVER_ERROR,
181
220
  )
182
221
 
222
+ status_after = status_before
223
+ try:
224
+ if pdf.state is not None:
225
+ pdf.state.refresh_from_db()
226
+ st_after = getattr(pdf.state, "anonymization_status", None)
227
+ if st_after is not None:
228
+ status_after = str(getattr(st_after, "value", st_after))
229
+ except Exception:
230
+ logger.exception(
231
+ "Failed to read pdf anonymization_status after validation"
232
+ )
233
+
234
+ # --- NEW: write operation log ---
235
+ record_operation(
236
+ request,
237
+ action="anonymization.validated",
238
+ resource_type="pdf",
239
+ resource_id=file_id,
240
+ status_before=status_before,
241
+ status_after=status_after,
242
+ )
243
+
183
244
  return Response(
184
245
  {"message": "report validated."},
185
246
  status=status.HTTP_200_OK,
@@ -10,4 +10,4 @@ __all__ = [
10
10
  "keycloak_login",
11
11
  "keycloak_callback",
12
12
  "public_home",
13
- ]
13
+ ]