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

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

Potentially problematic release.


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

Files changed (402) hide show
  1. endoreg_db/data/__init__.py +22 -8
  2. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +0 -1
  3. endoreg_db/data/examination/examinations/data.yaml +114 -14
  4. endoreg_db/data/examination/time-type/data.yaml +0 -3
  5. endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
  6. endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
  7. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
  8. endoreg_db/data/finding/00_generic.yaml +35 -0
  9. endoreg_db/data/finding/00_generic_complication.yaml +9 -0
  10. endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
  11. endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
  12. endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
  13. endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
  14. endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
  15. endoreg_db/data/finding_classification/00_generic.yaml +44 -0
  16. endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
  17. endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
  18. endoreg_db/data/finding_classification/{colonoscopy_bowel_preparation.yaml → 02_colonoscopy_baseline.yaml} +35 -20
  19. endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
  20. endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
  21. endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
  22. endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
  23. endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
  24. endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
  25. endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
  26. endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
  27. endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
  28. endoreg_db/data/{_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml → finding_classification_choice/02_colonoscopy_generic.yaml} +1 -1
  29. endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
  30. endoreg_db/data/{_examples/finding_classification_choice/colonoscopy_location.yaml → finding_classification_choice/02_colonoscopy_location.yaml} +23 -4
  31. endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
  32. endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
  33. endoreg_db/data/{_examples/finding_classification_choice/colon_lesion_paris.yaml → finding_classification_choice/02_colonoscopy_polyp_morphology.yaml} +26 -8
  34. endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
  35. endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
  36. endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
  37. endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
  38. endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
  39. endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
  40. endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
  41. endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
  42. endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
  43. endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
  44. endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
  45. endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
  46. endoreg_db/data/finding_type/data.yaml +8 -12
  47. endoreg_db/data/requirement/01_patient_data.yaml +93 -0
  48. endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
  49. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +0 -2
  50. endoreg_db/data/requirement_set/90_coloreg.yaml +20 -8
  51. endoreg_db/exceptions.py +0 -1
  52. endoreg_db/forms/examination_form.py +1 -1
  53. endoreg_db/helpers/data_loader.py +124 -52
  54. endoreg_db/helpers/default_objects.py +116 -81
  55. endoreg_db/import_files/__init__.py +27 -0
  56. endoreg_db/import_files/context/__init__.py +7 -0
  57. endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
  58. endoreg_db/import_files/context/ensure_center.py +17 -0
  59. endoreg_db/import_files/context/file_lock.py +66 -0
  60. endoreg_db/import_files/context/import_context.py +43 -0
  61. endoreg_db/import_files/context/validate_directories.py +56 -0
  62. endoreg_db/import_files/file_storage/__init__.py +15 -0
  63. endoreg_db/import_files/file_storage/create_report_file.py +76 -0
  64. endoreg_db/import_files/file_storage/create_video_file.py +75 -0
  65. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
  66. endoreg_db/import_files/file_storage/state_management.py +400 -0
  67. endoreg_db/import_files/file_storage/storage.py +36 -0
  68. endoreg_db/import_files/import_service.md +26 -0
  69. endoreg_db/import_files/processing/__init__.py +11 -0
  70. endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
  71. endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
  72. endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
  73. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
  74. endoreg_db/import_files/pseudonymization/fake.py +52 -0
  75. endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
  76. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
  77. endoreg_db/import_files/report_import_service.py +141 -0
  78. endoreg_db/import_files/video_import_service.py +150 -0
  79. endoreg_db/management/commands/import_report.py +130 -65
  80. endoreg_db/management/commands/import_video_with_classification.py +1 -1
  81. endoreg_db/management/commands/load_ai_model_data.py +5 -5
  82. endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
  83. endoreg_db/management/commands/load_base_db_data.py +5 -134
  84. endoreg_db/management/commands/load_contraindication_data.py +14 -16
  85. endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
  86. endoreg_db/management/commands/load_disease_classification_data.py +15 -18
  87. endoreg_db/management/commands/load_disease_data.py +25 -28
  88. endoreg_db/management/commands/load_endoscope_data.py +20 -27
  89. endoreg_db/management/commands/load_event_data.py +14 -16
  90. endoreg_db/management/commands/load_examination_data.py +31 -44
  91. endoreg_db/management/commands/load_examination_indication_data.py +20 -21
  92. endoreg_db/management/commands/load_finding_data.py +52 -80
  93. endoreg_db/management/commands/load_information_source.py +21 -23
  94. endoreg_db/management/commands/load_lab_value_data.py +17 -26
  95. endoreg_db/management/commands/load_medication_data.py +13 -12
  96. endoreg_db/management/commands/load_organ_data.py +15 -19
  97. endoreg_db/management/commands/load_pdf_type_data.py +19 -18
  98. endoreg_db/management/commands/load_profession_data.py +14 -17
  99. endoreg_db/management/commands/load_qualification_data.py +20 -23
  100. endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
  101. endoreg_db/management/commands/load_requirement_data.py +14 -20
  102. endoreg_db/management/commands/load_risk_data.py +7 -6
  103. endoreg_db/management/commands/load_shift_data.py +20 -23
  104. endoreg_db/management/commands/load_tag_data.py +8 -11
  105. endoreg_db/management/commands/load_unit_data.py +17 -19
  106. endoreg_db/management/commands/start_filewatcher.py +46 -37
  107. endoreg_db/management/commands/validate_video_files.py +1 -5
  108. endoreg_db/migrations/0001_initial.py +1360 -1812
  109. endoreg_db/models/administration/person/patient/patient.py +72 -46
  110. endoreg_db/models/label/__init__.py +2 -2
  111. endoreg_db/models/label/annotation/video_segmentation_annotation.py +18 -26
  112. endoreg_db/models/label/label_video_segment/label_video_segment.py +23 -1
  113. endoreg_db/models/media/pdf/raw_pdf.py +136 -64
  114. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +34 -10
  115. endoreg_db/models/media/processing_history/__init__.py +5 -0
  116. endoreg_db/models/media/processing_history/processing_history.py +96 -0
  117. endoreg_db/models/media/video/create_from_file.py +101 -31
  118. endoreg_db/models/media/video/video_file.py +125 -105
  119. endoreg_db/models/media/video/video_file_io.py +31 -26
  120. endoreg_db/models/medical/contraindication/README.md +1 -0
  121. endoreg_db/models/medical/examination/examination.py +28 -8
  122. endoreg_db/models/medical/examination/examination_indication.py +13 -79
  123. endoreg_db/models/medical/examination/examination_time.py +8 -3
  124. endoreg_db/models/medical/finding/finding.py +5 -12
  125. endoreg_db/models/medical/finding/finding_classification.py +18 -37
  126. endoreg_db/models/medical/finding/finding_intervention.py +7 -9
  127. endoreg_db/models/medical/hardware/endoscope.py +6 -0
  128. endoreg_db/models/medical/patient/medication_examples.py +5 -1
  129. endoreg_db/models/medical/patient/patient_finding.py +1 -1
  130. endoreg_db/models/metadata/pdf_meta.py +22 -10
  131. endoreg_db/models/metadata/sensitive_meta.py +3 -0
  132. endoreg_db/models/metadata/sensitive_meta_logic.py +200 -124
  133. endoreg_db/models/other/information_source.py +27 -6
  134. endoreg_db/models/report/__init__.py +0 -0
  135. endoreg_db/models/report/images.py +0 -0
  136. endoreg_db/models/report/report.py +6 -0
  137. endoreg_db/models/requirement/requirement.py +59 -399
  138. endoreg_db/models/requirement/requirement_operator.py +86 -98
  139. endoreg_db/models/state/audit_ledger.py +4 -5
  140. endoreg_db/models/state/raw_pdf.py +69 -30
  141. endoreg_db/models/state/video.py +64 -49
  142. endoreg_db/models/upload_job.py +33 -9
  143. endoreg_db/models/utils.py +27 -23
  144. endoreg_db/queries/__init__.py +3 -1
  145. endoreg_db/schemas/examination_evaluation.py +1 -1
  146. endoreg_db/serializers/__init__.py +2 -8
  147. endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
  148. endoreg_db/serializers/meta/__init__.py +1 -6
  149. endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
  150. endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
  151. endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
  152. endoreg_db/serializers/video/video_file_list.py +65 -34
  153. endoreg_db/services/__old/pdf_import.py +1487 -0
  154. endoreg_db/services/__old/video_import.py +1306 -0
  155. endoreg_db/services/anonymization.py +63 -26
  156. endoreg_db/services/lookup_service.py +28 -28
  157. endoreg_db/services/lookup_store.py +2 -2
  158. endoreg_db/services/pdf_import.py +0 -1480
  159. endoreg_db/services/report_import.py +10 -0
  160. endoreg_db/services/video_import.py +6 -1165
  161. endoreg_db/tasks/upload_tasks.py +79 -70
  162. endoreg_db/tasks/video_ingest.py +8 -4
  163. endoreg_db/urls/__init__.py +0 -14
  164. endoreg_db/urls/ai.py +32 -0
  165. endoreg_db/urls/media.py +21 -24
  166. endoreg_db/utils/dataloader.py +87 -57
  167. endoreg_db/utils/paths.py +110 -46
  168. endoreg_db/utils/pipelines/Readme.md +1 -1
  169. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
  170. endoreg_db/views/__init__.py +85 -173
  171. endoreg_db/views/ai/__init__.py +8 -0
  172. endoreg_db/views/ai/label.py +155 -0
  173. endoreg_db/views/anonymization/media_management.py +8 -7
  174. endoreg_db/views/anonymization/overview.py +97 -68
  175. endoreg_db/views/anonymization/validate.py +25 -21
  176. endoreg_db/views/media/__init__.py +5 -20
  177. endoreg_db/views/media/pdf_media.py +109 -65
  178. endoreg_db/views/media/sensitive_metadata.py +163 -148
  179. endoreg_db/views/meta/__init__.py +0 -8
  180. endoreg_db/views/misc/__init__.py +1 -7
  181. endoreg_db/views/misc/upload_views.py +94 -93
  182. endoreg_db/views/report/__init__.py +7 -0
  183. endoreg_db/views/{pdf → report}/reimport.py +45 -24
  184. endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +40 -32
  185. endoreg_db/views/requirement/lookup_store.py +22 -90
  186. endoreg_db/views/video/__init__.py +23 -22
  187. endoreg_db/views/video/correction.py +201 -172
  188. endoreg_db/views/video/reimport.py +1 -1
  189. endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +75 -37
  190. endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
  191. endoreg_db/views/video/video_stream.py +7 -8
  192. {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +2 -2
  193. {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +217 -335
  194. {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
  195. endoreg_db/data/_examples/disease.yaml +0 -55
  196. endoreg_db/data/_examples/disease_classification.yaml +0 -13
  197. endoreg_db/data/_examples/disease_classification_choice.yaml +0 -62
  198. endoreg_db/data/_examples/event.yaml +0 -64
  199. endoreg_db/data/_examples/examination.yaml +0 -72
  200. endoreg_db/data/_examples/finding/anatomy_colon.yaml +0 -128
  201. endoreg_db/data/_examples/finding/colonoscopy.yaml +0 -40
  202. endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +0 -56
  203. endoreg_db/data/_examples/finding/complication.yaml +0 -16
  204. endoreg_db/data/_examples/finding/data.yaml +0 -105
  205. endoreg_db/data/_examples/finding/examination_setting.yaml +0 -16
  206. endoreg_db/data/_examples/finding/medication_related.yaml +0 -18
  207. endoreg_db/data/_examples/finding/outcome.yaml +0 -12
  208. endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +0 -68
  209. endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +0 -22
  210. endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +0 -25
  211. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  212. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  213. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +0 -68
  214. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  215. endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +0 -80
  216. endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +0 -21
  217. endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +0 -20
  218. endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +0 -26
  219. endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +0 -22
  220. endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +0 -53
  221. endoreg_db/data/_examples/finding_classification/complication_generic.yaml +0 -25
  222. endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +0 -40
  223. endoreg_db/data/_examples/finding_classification/histology_colo.yaml +0 -51
  224. endoreg_db/data/_examples/finding_classification/intervention_required.yaml +0 -26
  225. endoreg_db/data/_examples/finding_classification/medication_related.yaml +0 -23
  226. endoreg_db/data/_examples/finding_classification/visualized.yaml +0 -33
  227. endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +0 -78
  228. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  229. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  230. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  231. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  232. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  233. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  234. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  235. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  236. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +0 -82
  237. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  238. endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +0 -15
  239. endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +0 -15
  240. endoreg_db/data/_examples/finding_classification_choice/histology.yaml +0 -24
  241. endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +0 -20
  242. endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +0 -19
  243. endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +0 -11
  244. endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +0 -48
  245. endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +0 -43
  246. endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  247. endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +0 -128
  248. endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +0 -32
  249. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  250. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  251. endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +0 -15
  252. endoreg_db/data/_examples/finding_type/data.yaml +0 -43
  253. endoreg_db/data/_examples/requirement/age.yaml +0 -26
  254. endoreg_db/data/_examples/requirement/gender.yaml +0 -25
  255. endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +0 -48
  256. endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +0 -57
  257. endoreg_db/data/_examples/requirement_set/endoscopy_bleeding_risk.yaml +0 -52
  258. endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
  259. endoreg_db/data/finding/anatomy_colon.yaml +0 -128
  260. endoreg_db/data/finding/colonoscopy.yaml +0 -40
  261. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
  262. endoreg_db/data/finding/complication.yaml +0 -16
  263. endoreg_db/data/finding/data.yaml +0 -105
  264. endoreg_db/data/finding/examination_setting.yaml +0 -16
  265. endoreg_db/data/finding/medication_related.yaml +0 -18
  266. endoreg_db/data/finding/outcome.yaml +0 -12
  267. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
  268. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
  269. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  270. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  271. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -38
  272. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  273. endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -49
  274. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
  275. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
  276. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
  277. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
  278. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
  279. endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
  280. endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
  281. endoreg_db/data/finding_classification/histology_colo.yaml +0 -43
  282. endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
  283. endoreg_db/data/finding_classification/medication_related.yaml +0 -23
  284. endoreg_db/data/finding_classification/visualized.yaml +0 -33
  285. endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  286. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  287. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  288. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  289. endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  290. endoreg_db/data/finding_classification_choice/colon_lesion_paris.yaml +0 -57
  291. endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  292. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  293. endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  294. endoreg_db/data/finding_classification_choice/colonoscopy_location.yaml +0 -229
  295. endoreg_db/data/finding_classification_choice/colonoscopy_not_complete_reason.yaml +0 -19
  296. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
  297. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  298. endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
  299. endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
  300. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  301. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
  302. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
  303. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  304. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  305. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
  306. endoreg_db/data/requirement/age.yaml +0 -26
  307. endoreg_db/data/requirement/colonoscopy_baseline_austria.yaml +0 -45
  308. endoreg_db/data/requirement/disease_cardiovascular.yaml +0 -79
  309. endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +0 -41
  310. endoreg_db/data/requirement/disease_hepatology.yaml +0 -12
  311. endoreg_db/data/requirement/disease_misc.yaml +0 -12
  312. endoreg_db/data/requirement/disease_renal.yaml +0 -96
  313. endoreg_db/data/requirement/endoscopy_bleeding_risk.yaml +0 -59
  314. endoreg_db/data/requirement/event_cardiology.yaml +0 -251
  315. endoreg_db/data/requirement/event_requirements.yaml +0 -145
  316. endoreg_db/data/requirement/finding_colon_polyp.yaml +0 -50
  317. endoreg_db/data/requirement/gender.yaml +0 -25
  318. endoreg_db/data/requirement/lab_value.yaml +0 -441
  319. endoreg_db/data/requirement/medication.yaml +0 -93
  320. endoreg_db/data/requirement_operator/age.yaml +0 -13
  321. endoreg_db/data/requirement_operator/lab_operators.yaml +0 -129
  322. endoreg_db/data/requirement_operator/model_operators.yaml +0 -96
  323. endoreg_db/management/commands/init_default_ai_model.py +0 -112
  324. endoreg_db/management/commands/reset_celery_schedule.py +0 -9
  325. endoreg_db/management/commands/validate_video.py +0 -204
  326. endoreg_db/migrations/0002_requirementset_depends_on.py +0 -18
  327. endoreg_db/migrations/_old/0001_initial.py +0 -1857
  328. endoreg_db/migrations/_old/0002_add_video_correction_models.py +0 -52
  329. endoreg_db/migrations/_old/0003_add_center_display_name.py +0 -30
  330. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +0 -68
  331. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +0 -77
  332. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +0 -14
  333. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +0 -68
  334. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +0 -89
  335. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +0 -27
  336. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +0 -21
  337. endoreg_db/renames.yml +0 -8
  338. endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
  339. endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
  340. endoreg_db/serializers/_old/video.py +0 -71
  341. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
  342. endoreg_db/serializers/meta/report_meta.py +0 -53
  343. endoreg_db/serializers/report/__init__.py +0 -9
  344. endoreg_db/serializers/report/mixins.py +0 -45
  345. endoreg_db/serializers/report/report.py +0 -105
  346. endoreg_db/serializers/report/report_list.py +0 -22
  347. endoreg_db/serializers/report/secure_file_url.py +0 -26
  348. endoreg_db/services/requirements_object.py +0 -147
  349. endoreg_db/services/storage_aware_video_processor.py +0 -370
  350. endoreg_db/urls/files.py +0 -6
  351. endoreg_db/urls/label_video_segment_validate.py +0 -33
  352. endoreg_db/urls/label_video_segments.py +0 -46
  353. endoreg_db/views/label/__init__.py +0 -5
  354. endoreg_db/views/label/label.py +0 -15
  355. endoreg_db/views/label_video_segment/__init__.py +0 -16
  356. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
  357. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
  358. endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
  359. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
  360. endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
  361. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
  362. endoreg_db/views/label_video_segment/validate.py +0 -226
  363. endoreg_db/views/media/segments.py +0 -71
  364. endoreg_db/views/meta/available_files_list.py +0 -146
  365. endoreg_db/views/meta/report_meta.py +0 -53
  366. endoreg_db/views/meta/sensitive_meta_detail.py +0 -85
  367. endoreg_db/views/misc/secure_file_serving_view.py +0 -80
  368. endoreg_db/views/misc/secure_file_url_view.py +0 -84
  369. endoreg_db/views/misc/secure_url_validate.py +0 -79
  370. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
  371. endoreg_db/views/patient_finding_location/__init__.py +0 -5
  372. endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
  373. endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
  374. endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
  375. endoreg_db/views/pdf/__init__.py +0 -8
  376. endoreg_db/views/video/segmentation.py +0 -274
  377. endoreg_db/views/video/task_status.py +0 -49
  378. endoreg_db/views/video/timeline.py +0 -46
  379. endoreg_db/views/video/video_analyze.py +0 -52
  380. /endoreg_db/data/requirement/{colon_polyp_intervention.yaml → old/colon_polyp_intervention.yaml} +0 -0
  381. /endoreg_db/data/{_examples/requirement → requirement/old}/colonoscopy_baseline_austria.yaml +0 -0
  382. /endoreg_db/data/requirement/{coloreg_colon_polyp.yaml → old/coloreg_colon_polyp.yaml} +0 -0
  383. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_cardiovascular.yaml +0 -0
  384. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_classification_choice_cardiovascular.yaml +0 -0
  385. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_hepatology.yaml +0 -0
  386. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_misc.yaml +0 -0
  387. /endoreg_db/data/{_examples/requirement → requirement/old}/disease_renal.yaml +0 -0
  388. /endoreg_db/data/{_examples/requirement → requirement/old}/endoscopy_bleeding_risk.yaml +0 -0
  389. /endoreg_db/data/{_examples/requirement → requirement/old}/event_cardiology.yaml +0 -0
  390. /endoreg_db/data/{_examples/requirement → requirement/old}/event_requirements.yaml +0 -0
  391. /endoreg_db/data/{_examples/requirement → requirement/old}/finding_colon_polyp.yaml +0 -0
  392. /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
  393. /endoreg_db/data/{_examples/requirement → requirement/old}/lab_value.yaml +0 -0
  394. /endoreg_db/data/{_examples/requirement → requirement/old}/medication.yaml +0 -0
  395. /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/age.yaml +0 -0
  396. /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/lab_operators.yaml +0 -0
  397. /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/model_operators.yaml +0 -0
  398. /endoreg_db/{urls/sensitive_meta.py → import_files/pseudonymization/__init__.py} +0 -0
  399. /endoreg_db/{views/pdf/pdf_stream_views.py → import_files/pseudonymization/pseudonymize.py} +0 -0
  400. /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
  401. /endoreg_db/utils/requirement_operator_logic/{model_evaluators.py → _old/model_evaluators.py} +0 -0
  402. {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
@@ -11,30 +11,35 @@ Available Functions from lx_anonymizer (already implemented):
11
11
  - FrameCleaner.remove_frames_from_video() - Frame removal
12
12
  - VideoImportService._get_processor_roi_info() - ROI extraction
13
13
  """
14
- from rest_framework.views import APIView
15
- from rest_framework.response import Response
16
- from rest_framework import status
17
- from django.shortcuts import get_object_or_404
18
- from django.conf import settings
19
- from pathlib import Path
14
+
20
15
  import json
21
16
  import logging
17
+ from pathlib import Path
22
18
 
23
- from endoreg_db.models import VideoFile, VideoMetadata, VideoProcessingHistory, LabelVideoSegment
24
- from endoreg_db.serializers import VideoMetaSerializer, VideoProcessingHistorySerializer
19
+ from django.conf import settings
20
+ from django.shortcuts import get_object_or_404
25
21
  from lx_anonymizer import FrameCleaner
22
+ from rest_framework import status
23
+ from rest_framework.response import Response
24
+ from rest_framework.views import APIView
26
25
 
27
- from endoreg_db.models import VideoFile
26
+ from endoreg_db.models import (
27
+ LabelVideoSegment,
28
+ VideoFile,
29
+ VideoMetadata,
30
+ VideoProcessingHistory,
31
+ )
32
+ from endoreg_db.serializers import VideoMetaSerializer, VideoProcessingHistorySerializer
28
33
  from endoreg_db.serializers.video.video_file_detail import VideoDetailSerializer
34
+ from endoreg_db.utils.paths import ANONYM_VIDEO_DIR, IMPORT_VIDEO_DIR
29
35
  from endoreg_db.utils.permissions import EnvironmentAwarePermission
30
36
 
31
37
  logger = logging.getLogger(__name__)
32
38
 
39
+
33
40
  def update_processed_file(video, output_path: Path):
34
- from endoreg_db.utils import data_paths
35
- storage_root = Path(data_paths["storage"])
36
41
  try:
37
- rel_path = output_path.relative_to(storage_root)
42
+ rel_path = output_path.relative_to(ANONYM_VIDEO_DIR)
38
43
  except ValueError:
39
44
  rel_path = output_path.relative_to(Path(settings.MEDIA_ROOT))
40
45
  video.processed_file.name = str(rel_path)
@@ -45,6 +50,7 @@ class VideoCorrectionView(APIView):
45
50
  """
46
51
  GET /api/video/media/video-correction/{id}/ - Get video details for correction
47
52
  """
53
+
48
54
  permission_classes = [EnvironmentAwarePermission]
49
55
 
50
56
  def get(self, request, pk):
@@ -52,65 +58,68 @@ class VideoCorrectionView(APIView):
52
58
  ser = VideoDetailSerializer(video, context={"request": request})
53
59
  return Response(ser.data, status=status.HTTP_200_OK)
54
60
 
61
+
55
62
  def update_segments_after_frame_removal(video: VideoFile, removed_frames: list) -> dict:
56
63
  """
57
64
  Update LabelVideoSegment frame boundaries after frame removal.
58
-
65
+
59
66
  This function shifts segment start/end frames based on which frames were removed.
60
67
  Segments are deleted if all their frames are removed.
61
-
68
+
62
69
  Args:
63
70
  video: VideoFile instance whose segments should be updated
64
71
  removed_frames: List of frame numbers that were removed (sorted)
65
-
72
+
66
73
  Returns:
67
74
  dict: {
68
75
  'segments_updated': int,
69
76
  'segments_deleted': int,
70
77
  'segments_unchanged': int
71
78
  }
72
-
79
+
73
80
  Algorithm:
74
81
  For each segment:
75
82
  1. Count frames removed before segment → shift start_frame
76
83
  2. Count frames removed within segment → shift end_frame
77
84
  3. Delete segment if start_frame >= end_frame (all frames removed)
78
-
85
+
79
86
  Example:
80
87
  Original segment: frames 100-200
81
88
  Removed frames: [50, 75, 120, 150, 180]
82
-
89
+
83
90
  Frames before segment (< 100): 2 frames (50, 75)
84
91
  Frames within segment (100-200): 3 frames (120, 150, 180)
85
-
92
+
86
93
  New segment: frames (100-2) to (200-2-3) = 98-195
87
94
  """
88
95
  if not removed_frames:
89
- return {'segments_updated': 0, 'segments_deleted': 0, 'segments_unchanged': 0}
90
-
96
+ return {"segments_updated": 0, "segments_deleted": 0, "segments_unchanged": 0}
97
+
91
98
  removed_frames = sorted(set(removed_frames)) # Ensure sorted and unique
92
- segments = LabelVideoSegment.objects.filter(
93
- video_file=video
94
- ).order_by('start_frame_number')
95
-
99
+ segments = LabelVideoSegment.objects.filter(video_file=video).order_by(
100
+ "start_frame_number"
101
+ )
102
+
96
103
  segments_updated = 0
97
104
  segments_deleted = 0
98
105
  segments_unchanged = 0
99
-
106
+
100
107
  for segment in segments:
101
108
  original_start = segment.start_frame_number
102
109
  original_end = segment.end_frame_number
103
-
110
+
104
111
  # Count frames removed before this segment
105
112
  frames_before = sum(1 for f in removed_frames if f < original_start)
106
-
113
+
107
114
  # Count frames removed within this segment
108
- frames_within = sum(1 for f in removed_frames if original_start <= f <= original_end)
109
-
115
+ frames_within = sum(
116
+ 1 for f in removed_frames if original_start <= f <= original_end
117
+ )
118
+
110
119
  # Calculate new boundaries
111
120
  new_start = original_start - frames_before
112
121
  new_end = original_end - frames_before - frames_within
113
-
122
+
114
123
  # Delete segment if all frames were removed
115
124
  if new_start >= new_end:
116
125
  logger.info(
@@ -133,25 +142,25 @@ def update_segments_after_frame_removal(video: VideoFile, removed_frames: list)
133
142
  else:
134
143
  # No change needed
135
144
  segments_unchanged += 1
136
-
145
+
137
146
  logger.info(
138
147
  f"Segment update complete for video {video.id}: "
139
148
  f"{segments_updated} updated, {segments_deleted} deleted, {segments_unchanged} unchanged"
140
149
  )
141
-
150
+
142
151
  return {
143
- 'segments_updated': segments_updated,
144
- 'segments_deleted': segments_deleted,
145
- 'segments_unchanged': segments_unchanged
152
+ "segments_updated": segments_updated,
153
+ "segments_deleted": segments_deleted,
154
+ "segments_unchanged": segments_unchanged,
146
155
  }
147
156
 
148
157
 
149
- class VideoMetadataView(APIView):
158
+ class VideoMetadataStatsView(APIView):
150
159
  """
151
160
  GET /api/media/videos/{pk}/metadata/
152
-
161
+
153
162
  Retrieve analysis results for a video.
154
-
163
+
155
164
  Returns:
156
165
  {
157
166
  "id": 1,
@@ -165,24 +174,24 @@ class VideoMetadataView(APIView):
165
174
  "analyzed_at": "2025-10-09T10:00:00Z"
166
175
  }
167
176
  """
168
-
177
+
169
178
  def get(self, request, pk):
170
179
  """Get video metadata by video ID."""
171
180
  video = get_object_or_404(VideoFile, pk=pk)
172
-
181
+
173
182
  # Get or create metadata record
174
183
  metadata, created = VideoMetadata.objects.get_or_create(video=video)
175
-
176
- serializer = VideoMetaSerializer(metadata, context={'request': request})
184
+
185
+ serializer = VideoMetaSerializer(metadata, context={"request": request})
177
186
  return Response(serializer.data)
178
187
 
179
188
 
180
189
  class VideoProcessingHistoryView(APIView):
181
190
  """
182
191
  GET /api/media/videos/{pk}/processing-history/
183
-
192
+
184
193
  Retrieve processing history for a video.
185
-
194
+
186
195
  Returns list of processing operations:
187
196
  [
188
197
  {
@@ -205,20 +214,18 @@ class VideoProcessingHistoryView(APIView):
205
214
  ...
206
215
  ]
207
216
  """
208
-
217
+
209
218
  def get(self, request, pk):
210
219
  """Get processing history for a video."""
211
220
  video = get_object_or_404(VideoFile, pk=pk)
212
-
221
+
213
222
  # Get all history records, newest first
214
- history = VideoProcessingHistory.objects.filter(
215
- video=video
216
- ).order_by('-created_at')
217
-
223
+ history = VideoProcessingHistory.objects.filter(video=video).order_by(
224
+ "-created_at"
225
+ )
226
+
218
227
  serializer = VideoProcessingHistorySerializer(
219
- history,
220
- many=True,
221
- context={'request': request}
228
+ history, many=True, context={"request": request}
222
229
  )
223
230
  return Response(serializer.data)
224
231
 
@@ -226,9 +233,9 @@ class VideoProcessingHistoryView(APIView):
226
233
  class VideoApplyMaskView(APIView):
227
234
  """
228
235
  POST /api/media/videos/{pk}/apply-mask/
229
-
236
+
230
237
  Apply device mask or custom ROI mask to video.
231
-
238
+
232
239
  Request body:
233
240
  {
234
241
  "mask_type": "device", // or "custom"
@@ -241,7 +248,7 @@ class VideoApplyMaskView(APIView):
241
248
  },
242
249
  "processing_method": "streaming" // or "direct" (default: streaming)
243
250
  }
244
-
251
+
245
252
  Returns:
246
253
  {
247
254
  "task_id": "abc-123-def", // Celery task ID (future)
@@ -249,59 +256,63 @@ class VideoApplyMaskView(APIView):
249
256
  "message": "Masking complete",
250
257
  "processing_time": 125.5
251
258
  }
252
-
259
+
253
260
  Note: Currently synchronous. Will be converted to Celery task in Phase 1.2.
254
261
  """
255
-
262
+
256
263
  def post(self, request, pk):
257
264
  """Apply masking to video."""
258
265
  video = get_object_or_404(VideoFile, pk=pk)
259
-
266
+
260
267
  # Extract parameters
261
- mask_type = request.data.get('mask_type', 'device')
262
- device_name = request.data.get('device_name')
263
- roi = request.data.get('roi')
264
- processing_method = request.data.get('processing_method', 'streaming')
265
-
268
+ mask_type = request.data.get("mask_type", "device")
269
+ device_name = request.data.get("device_name")
270
+ roi = request.data.get("roi")
271
+ processing_method = request.data.get("processing_method", "streaming")
272
+
266
273
  # Validate required parameters
267
- if mask_type == 'device' and not device_name:
274
+ if mask_type == "device" and not device_name:
268
275
  return Response(
269
- {'error': 'device_name required for device mask'},
270
- status=status.HTTP_400_BAD_REQUEST
276
+ {"error": "device_name required for device mask"},
277
+ status=status.HTTP_400_BAD_REQUEST,
271
278
  )
272
-
273
- if mask_type == 'custom' and not roi:
279
+
280
+ if mask_type == "custom" and not roi:
274
281
  return Response(
275
- {'error': 'roi required for custom mask'},
276
- status=status.HTTP_400_BAD_REQUEST
282
+ {"error": "roi required for custom mask"},
283
+ status=status.HTTP_400_BAD_REQUEST,
277
284
  )
278
-
285
+
279
286
  # Create processing history record
280
287
  history = VideoProcessingHistory.objects.create(
281
288
  video=video,
282
289
  operation=VideoProcessingHistory.OPERATION_MASKING,
283
290
  status=VideoProcessingHistory.STATUS_PENDING,
284
291
  config={
285
- 'mask_type': mask_type,
286
- 'device_name': device_name,
287
- 'roi': roi,
288
- 'processing_method': processing_method
289
- }
292
+ "mask_type": mask_type,
293
+ "device_name": device_name,
294
+ "roi": roi,
295
+ "processing_method": processing_method,
296
+ },
290
297
  )
291
-
298
+
292
299
  try:
293
300
  history.mark_running()
294
-
301
+
295
302
  # Initialize FrameCleaner
296
303
  frame_cleaner = FrameCleaner()
297
-
304
+
298
305
  # Get video paths
299
- video_path = Path(video.raw_file.path) if hasattr(video.raw_file, 'path') else Path(str(video.raw_file))
300
- output_path = Path(settings.MEDIA_ROOT) / 'anonym_videos' / f"{video.uuid}_masked.mp4"
306
+ video_path = (
307
+ Path(video.raw_file.path)
308
+ if hasattr(video.raw_file, "path")
309
+ else Path(str(video.raw_file))
310
+ )
311
+ output_path = video.get_output_path()
301
312
  output_path.parent.mkdir(parents=True, exist_ok=True)
302
-
313
+
303
314
  # Load or create mask config
304
- if mask_type == 'device':
315
+ if mask_type == "device":
305
316
  # Load device-specific mask from lx_anonymizer/masks/
306
317
  mask_config = frame_cleaner._load_mask(device_name)
307
318
  else: # custom
@@ -309,58 +320,62 @@ class VideoApplyMaskView(APIView):
309
320
  mask_config = frame_cleaner._create_mask_config_from_roi(
310
321
  endoscope_roi=roi,
311
322
  )
312
-
323
+
313
324
  # Apply mask (uses existing FrameCleaner._mask_video)
314
325
  import time
326
+
315
327
  start_time = time.time()
316
-
328
+
317
329
  success = frame_cleaner._mask_video(
318
330
  input_video=video_path,
319
331
  mask_config=mask_config,
320
- output_video=output_path
332
+ output_video=output_path,
321
333
  )
322
-
334
+
323
335
  processing_time = time.time() - start_time
324
-
336
+
325
337
  if success:
326
338
  # Update video record with anonymized file
327
339
  from django.core.files import File
340
+
328
341
  processed_file_path = output_path
329
342
  update_processed_file(video, processed_file_path)
330
343
  # Mark history as success
331
344
  history.mark_success(
332
345
  output_file=str(output_path),
333
- details=f"Masking completed in {processing_time:.1f}s"
346
+ details=f"Masking completed in {processing_time:.1f}s",
334
347
  )
335
-
348
+
336
349
  logger.info(f"Video {pk} masked successfully: {output_path}")
337
-
338
- return Response({
339
- 'task_id': None, # Will be Celery task ID in Phase 1.2
340
- 'output_file': str(output_path),
341
- 'message': 'Masking complete',
342
- 'processing_time': processing_time
343
- })
350
+
351
+ return Response(
352
+ {
353
+ "task_id": None, # Will be Celery task ID in Phase 1.2
354
+ "output_file": str(output_path),
355
+ "message": "Masking complete",
356
+ "processing_time": processing_time,
357
+ }
358
+ )
344
359
  else:
345
360
  raise Exception("Masking failed - check FFmpeg logs")
346
-
361
+
347
362
  except Exception as e:
348
363
  logger.error(f"Video masking failed for {pk}: {str(e)}", exc_info=True)
349
-
364
+
350
365
  history.mark_failure(str(e))
351
-
366
+
352
367
  return Response(
353
- {'error': f'Masking failed: {str(e)}'},
354
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
368
+ {"error": f"Masking failed: {str(e)}"},
369
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
355
370
  )
356
371
 
357
372
 
358
373
  class VideoRemoveFramesView(APIView):
359
374
  """
360
375
  POST /api/media/videos/{pk}/remove-frames/
361
-
376
+
362
377
  Remove specified frames from video.
363
-
378
+
364
379
  Request body:
365
380
  {
366
381
  "frame_list": [10, 15, 20, 30], // explicit frame numbers
@@ -368,10 +383,10 @@ class VideoRemoveFramesView(APIView):
368
383
  "frame_ranges": "10-20,30,45-50", // range string
369
384
  // OR
370
385
  "detection_method": "automatic", // use analysis results
371
-
386
+
372
387
  "processing_method": "streaming" // or "traditional"
373
388
  }
374
-
389
+
375
390
  Returns:
376
391
  {
377
392
  "task_id": "abc-123-def",
@@ -380,28 +395,28 @@ class VideoRemoveFramesView(APIView):
380
395
  "message": "Frame removal complete",
381
396
  "processing_time": 180.3
382
397
  }
383
-
398
+
384
399
  Note: Currently synchronous. Will be converted to Celery task in Phase 1.2.
385
400
  """
386
-
401
+
387
402
  def post(self, request, pk):
388
403
  """Remove frames from video."""
389
404
  video = get_object_or_404(VideoFile, pk=pk)
390
-
405
+
391
406
  # Extract parameters
392
- frame_list = request.data.get('frame_list')
393
- frame_ranges = request.data.get('frame_ranges')
394
- detection_method = request.data.get('detection_method')
395
- processing_method = request.data.get('processing_method', 'streaming')
396
-
407
+ frame_list = request.data.get("frame_list")
408
+ frame_ranges = request.data.get("frame_ranges")
409
+ detection_method = request.data.get("detection_method")
410
+ processing_method = request.data.get("processing_method", "streaming")
411
+
397
412
  # Determine frames to remove
398
413
  frames_to_remove = []
399
-
414
+
400
415
  if frame_list:
401
416
  frames_to_remove = frame_list
402
417
  elif frame_ranges:
403
418
  frames_to_remove = self._parse_frame_ranges(frame_ranges)
404
- elif detection_method == 'automatic':
419
+ elif detection_method == "automatic":
405
420
  # Use existing analysis results
406
421
  try:
407
422
  metadata = VideoMetadata.objects.get(video=video)
@@ -409,122 +424,136 @@ class VideoRemoveFramesView(APIView):
409
424
  frames_to_remove = json.loads(metadata.sensitive_frame_ids)
410
425
  else:
411
426
  return Response(
412
- {'error': 'No analysis results available. Run analysis first.'},
413
- status=status.HTTP_400_BAD_REQUEST
427
+ {"error": "No analysis results available. Run analysis first."},
428
+ status=status.HTTP_400_BAD_REQUEST,
414
429
  )
415
430
  except VideoMetadata.DoesNotExist:
416
431
  return Response(
417
- {'error': 'Video not analyzed. Run analysis first.'},
418
- status=status.HTTP_400_BAD_REQUEST
432
+ {"error": "Video not analyzed. Run analysis first."},
433
+ status=status.HTTP_400_BAD_REQUEST,
419
434
  )
420
435
  else:
421
436
  return Response(
422
- {'error': 'Must provide frame_list, frame_ranges, or detection_method=automatic'},
423
- status=status.HTTP_400_BAD_REQUEST
437
+ {
438
+ "error": "Must provide frame_list, frame_ranges, or detection_method=automatic"
439
+ },
440
+ status=status.HTTP_400_BAD_REQUEST,
424
441
  )
425
-
442
+
426
443
  if not frames_to_remove:
427
444
  return Response(
428
- {'error': 'No frames to remove'},
429
- status=status.HTTP_400_BAD_REQUEST
445
+ {"error": "No frames to remove"}, status=status.HTTP_400_BAD_REQUEST
430
446
  )
431
-
447
+
432
448
  # Create processing history record
433
449
  history = VideoProcessingHistory.objects.create(
434
450
  video=video,
435
451
  operation=VideoProcessingHistory.OPERATION_FRAME_REMOVAL,
436
452
  status=VideoProcessingHistory.STATUS_PENDING,
437
453
  config={
438
- 'frames_to_remove': frames_to_remove,
439
- 'frame_count': len(frames_to_remove),
440
- 'processing_method': processing_method
441
- }
454
+ "frames_to_remove": frames_to_remove,
455
+ "frame_count": len(frames_to_remove),
456
+ "processing_method": processing_method,
457
+ },
442
458
  )
443
-
459
+
444
460
  try:
445
461
  history.mark_running()
446
-
462
+
447
463
  # Initialize FrameCleaner
448
464
  frame_cleaner = FrameCleaner()
449
-
465
+
450
466
  # Get video paths
451
- video_path = Path(video.raw_file.path) if hasattr(video.raw_file, 'path') else Path(str(video.raw_file))
452
- output_path = Path(settings.MEDIA_ROOT) / 'anonym_videos' / f"{video.uuid}_cleaned.mp4"
467
+ video_path = (
468
+ Path(video.raw_file.path)
469
+ if hasattr(video.raw_file, "path")
470
+ else Path(str(video.raw_file))
471
+ )
472
+ output_path = (
473
+ Path(settings.MEDIA_ROOT)
474
+ / "anonym_videos"
475
+ / f"{video.uuid}_cleaned.mp4"
476
+ )
453
477
  output_path.parent.mkdir(parents=True, exist_ok=True)
454
-
478
+
455
479
  # Remove frames (uses existing FrameCleaner.remove_frames_from_video)
456
480
  import time
481
+
457
482
  start_time = time.time()
458
-
483
+
459
484
  success = frame_cleaner.remove_frames_from_video(
460
485
  original_video=video_path,
461
486
  frames_to_remove=frames_to_remove,
462
- output_video=output_path
487
+ output_video=output_path,
463
488
  )
464
-
489
+
465
490
  processing_time = time.time() - start_time
466
-
491
+
467
492
  if success:
468
493
  # Update video record
469
494
  update_processed_file(video, output_path)
470
495
 
471
-
472
496
  # Phase 1.4: Update LabelVideoSegments (shift frame numbers)
473
- segment_update_result = update_segments_after_frame_removal(video, frames_to_remove)
474
-
497
+ segment_update_result = update_segments_after_frame_removal(
498
+ video, frames_to_remove
499
+ )
500
+
475
501
  # Mark history as success with segment update info
476
502
  details_parts = [
477
503
  f"Removed {len(frames_to_remove)} frames in {processing_time:.1f}s"
478
504
  ]
479
- if segment_update_result['segments_updated'] > 0:
505
+ if segment_update_result["segments_updated"] > 0:
480
506
  details_parts.append(
481
507
  f"Updated {segment_update_result['segments_updated']} segments"
482
508
  )
483
- if segment_update_result['segments_deleted'] > 0:
509
+ if segment_update_result["segments_deleted"] > 0:
484
510
  details_parts.append(
485
511
  f"Deleted {segment_update_result['segments_deleted']} segments (all frames removed)"
486
512
  )
487
-
513
+
488
514
  history.mark_success(
489
- output_file=str(output_path),
490
- details="; ".join(details_parts)
515
+ output_file=str(output_path), details="; ".join(details_parts)
516
+ )
517
+
518
+ logger.info(
519
+ f"Video {pk} cleaned: removed {len(frames_to_remove)} frames"
520
+ )
521
+
522
+ return Response(
523
+ {
524
+ "task_id": None, # Will be Celery task ID in Phase 1.2
525
+ "output_file": str(output_path),
526
+ "frames_removed": len(frames_to_remove),
527
+ "segment_updates": segment_update_result,
528
+ "message": "Frame removal complete",
529
+ "processing_time": processing_time,
530
+ }
491
531
  )
492
-
493
- logger.info(f"Video {pk} cleaned: removed {len(frames_to_remove)} frames")
494
-
495
- return Response({
496
- 'task_id': None, # Will be Celery task ID in Phase 1.2
497
- 'output_file': str(output_path),
498
- 'frames_removed': len(frames_to_remove),
499
- 'segment_updates': segment_update_result,
500
- 'message': 'Frame removal complete',
501
- 'processing_time': processing_time
502
- })
503
532
  else:
504
533
  raise Exception("Frame removal failed - check FFmpeg logs")
505
-
534
+
506
535
  except Exception as e:
507
536
  logger.error(f"Frame removal failed for {pk}: {str(e)}", exc_info=True)
508
-
537
+
509
538
  history.mark_failure(str(e))
510
-
539
+
511
540
  return Response(
512
- {'error': f'Frame removal failed: {str(e)}'},
513
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
541
+ {"error": f"Frame removal failed: {str(e)}"},
542
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
514
543
  )
515
-
544
+
516
545
  def _parse_frame_ranges(self, ranges_str: str) -> list:
517
546
  """
518
547
  Parse frame ranges string to list of frame numbers.
519
-
548
+
520
549
  Example: "10-20,30,45-50" -> [10,11,...,20,30,45,...,50]
521
550
  """
522
551
  frames = []
523
- for part in ranges_str.split(','):
552
+ for part in ranges_str.split(","):
524
553
  part = part.strip()
525
- if '-' in part:
526
- start, end = map(int, part.split('-'))
554
+ if "-" in part:
555
+ start, end = map(int, part.split("-"))
527
556
  frames.extend(range(start, end + 1))
528
557
  else:
529
558
  frames.append(int(part))
530
- return sorted(set(frames)) # Remove duplicates and sort
559
+ return sorted(set(frames)) # Remove duplicates and sort