endoreg-db 0.8.4.4__py3-none-any.whl → 0.8.8.0__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 (372) hide show
  1. endoreg_db/authz/auth.py +74 -0
  2. endoreg_db/authz/backends.py +168 -0
  3. endoreg_db/authz/management/commands/list_routes.py +18 -0
  4. endoreg_db/authz/middleware.py +83 -0
  5. endoreg_db/authz/permissions.py +127 -0
  6. endoreg_db/authz/policy.py +218 -0
  7. endoreg_db/authz/views_auth.py +66 -0
  8. endoreg_db/config/env.py +13 -8
  9. endoreg_db/data/__init__.py +8 -31
  10. endoreg_db/data/_examples/disease.yaml +55 -0
  11. endoreg_db/data/_examples/disease_classification.yaml +13 -0
  12. endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
  13. endoreg_db/data/_examples/event.yaml +64 -0
  14. endoreg_db/data/_examples/examination.yaml +72 -0
  15. endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
  16. endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
  17. endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
  18. endoreg_db/data/_examples/finding/complication.yaml +16 -0
  19. endoreg_db/data/_examples/finding/data.yaml +105 -0
  20. endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
  21. endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
  22. endoreg_db/data/_examples/finding/outcome.yaml +12 -0
  23. endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
  24. endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
  25. endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
  26. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
  27. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
  28. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
  29. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
  30. endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
  31. endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
  32. endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
  33. endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
  34. endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
  35. endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
  36. endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
  37. endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
  38. endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
  39. endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
  40. endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
  41. endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
  42. endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
  43. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
  44. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
  45. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
  46. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
  47. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
  48. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
  49. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
  50. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
  51. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
  52. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
  53. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
  54. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
  55. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
  56. endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
  57. endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
  58. endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
  59. endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
  60. endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
  61. endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
  62. endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
  63. endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
  64. endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
  65. endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
  66. endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
  67. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  68. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  69. endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
  70. endoreg_db/data/_examples/finding_type/data.yaml +43 -0
  71. endoreg_db/data/_examples/requirement/age.yaml +26 -0
  72. endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
  73. endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
  74. endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
  75. endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
  76. endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
  77. endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
  78. endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
  79. endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
  80. endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
  81. endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
  82. endoreg_db/data/_examples/requirement/gender.yaml +25 -0
  83. endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
  84. endoreg_db/data/_examples/requirement/medication.yaml +93 -0
  85. endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
  86. endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
  87. endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
  88. endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
  89. endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
  90. endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
  91. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
  92. endoreg_db/data/event_classification/data.yaml +4 -0
  93. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  94. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
  95. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
  96. endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
  97. endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
  98. endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
  99. endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
  100. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
  101. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  102. endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
  103. endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
  104. endoreg_db/data/requirement_set/_old_ +109 -0
  105. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  106. endoreg_db/data/setup_config.yaml +4 -4
  107. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  108. endoreg_db/exceptions.py +5 -2
  109. endoreg_db/helpers/data_loader.py +1 -1
  110. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  111. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  112. endoreg_db/management/commands/import_video.py +9 -10
  113. endoreg_db/management/commands/import_video_with_classification.py +1 -1
  114. endoreg_db/management/commands/init_default_ai_model.py +1 -1
  115. endoreg_db/management/commands/list_routes.py +18 -0
  116. endoreg_db/management/commands/load_ai_model_data.py +2 -1
  117. endoreg_db/management/commands/load_center_data.py +12 -12
  118. endoreg_db/management/commands/load_requirement_data.py +60 -31
  119. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  120. endoreg_db/management/commands/setup_endoreg_db.py +14 -10
  121. endoreg_db/management/commands/storage_management.py +271 -203
  122. endoreg_db/migrations/0001_initial.py +1799 -1300
  123. endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
  124. endoreg_db/migrations/_old/0001_initial.py +1857 -0
  125. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
  126. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
  127. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
  128. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
  129. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
  130. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
  131. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
  132. endoreg_db/models/__init__.py +78 -123
  133. endoreg_db/models/administration/__init__.py +21 -42
  134. endoreg_db/models/administration/ai/active_model.py +2 -2
  135. endoreg_db/models/administration/ai/ai_model.py +7 -6
  136. endoreg_db/models/administration/case/__init__.py +1 -15
  137. endoreg_db/models/administration/case/case.py +3 -3
  138. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  139. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  140. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  141. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  142. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  143. endoreg_db/models/administration/center/center.py +33 -19
  144. endoreg_db/models/administration/center/center_product.py +12 -9
  145. endoreg_db/models/administration/center/center_resource.py +25 -19
  146. endoreg_db/models/administration/center/center_shift.py +21 -17
  147. endoreg_db/models/administration/center/center_waste.py +16 -8
  148. endoreg_db/models/administration/person/__init__.py +2 -0
  149. endoreg_db/models/administration/person/employee/employee.py +10 -5
  150. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  151. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  152. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  153. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  154. endoreg_db/models/administration/person/patient/patient.py +103 -100
  155. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  156. endoreg_db/models/administration/person/person.py +4 -0
  157. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  158. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  159. endoreg_db/models/administration/product/product.py +20 -15
  160. endoreg_db/models/administration/product/product_material.py +17 -18
  161. endoreg_db/models/administration/product/product_weight.py +12 -8
  162. endoreg_db/models/administration/product/reference_product.py +23 -55
  163. endoreg_db/models/administration/qualification/qualification.py +7 -3
  164. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  165. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  166. endoreg_db/models/administration/shift/shift.py +16 -12
  167. endoreg_db/models/administration/shift/shift_type.py +23 -31
  168. endoreg_db/models/label/__init__.py +7 -8
  169. endoreg_db/models/label/annotation/image_classification.py +10 -9
  170. endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
  171. endoreg_db/models/label/label.py +15 -15
  172. endoreg_db/models/label/label_set.py +19 -6
  173. endoreg_db/models/label/label_type.py +1 -1
  174. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  175. endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
  176. endoreg_db/models/label/video_segmentation_label.py +4 -0
  177. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  178. endoreg_db/models/media/frame/frame.py +22 -22
  179. endoreg_db/models/media/pdf/raw_pdf.py +249 -177
  180. endoreg_db/models/media/pdf/report_file.py +25 -29
  181. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
  182. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  183. endoreg_db/models/media/video/__init__.py +1 -0
  184. endoreg_db/models/media/video/create_from_file.py +48 -56
  185. endoreg_db/models/media/video/pipe_1.py +30 -33
  186. endoreg_db/models/media/video/pipe_2.py +8 -9
  187. endoreg_db/models/media/video/video_file.py +359 -204
  188. endoreg_db/models/media/video/video_file_ai.py +288 -74
  189. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  190. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  191. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  192. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  193. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  194. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  195. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  196. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  197. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  198. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  199. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  200. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  201. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  202. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  203. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  204. endoreg_db/models/media/video/video_file_io.py +109 -62
  205. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  206. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  207. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  208. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  209. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  210. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  211. endoreg_db/models/media/video/video_file_segments.py +24 -17
  212. endoreg_db/models/media/video/video_metadata.py +19 -35
  213. endoreg_db/models/media/video/video_processing.py +96 -95
  214. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  215. endoreg_db/models/medical/disease.py +22 -16
  216. endoreg_db/models/medical/event.py +31 -18
  217. endoreg_db/models/medical/examination/__init__.py +13 -6
  218. endoreg_db/models/medical/examination/examination.py +17 -18
  219. endoreg_db/models/medical/examination/examination_indication.py +26 -25
  220. endoreg_db/models/medical/examination/examination_time.py +16 -6
  221. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  222. endoreg_db/models/medical/examination/examination_type.py +3 -4
  223. endoreg_db/models/medical/finding/finding.py +38 -39
  224. endoreg_db/models/medical/finding/finding_classification.py +37 -48
  225. endoreg_db/models/medical/finding/finding_intervention.py +27 -22
  226. endoreg_db/models/medical/finding/finding_type.py +13 -12
  227. endoreg_db/models/medical/hardware/endoscope.py +20 -26
  228. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  229. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  230. endoreg_db/models/medical/medication/medication.py +22 -10
  231. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  232. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  233. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  234. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  235. endoreg_db/models/medical/organ/__init__.py +15 -12
  236. endoreg_db/models/medical/patient/medication_examples.py +1 -5
  237. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  238. endoreg_db/models/medical/patient/patient_event.py +19 -22
  239. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  240. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  241. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  242. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  243. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  244. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  245. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  246. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  247. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  248. endoreg_db/models/medical/risk/risk.py +7 -6
  249. endoreg_db/models/medical/risk/risk_type.py +8 -5
  250. endoreg_db/models/metadata/model_meta.py +60 -29
  251. endoreg_db/models/metadata/model_meta_logic.py +139 -18
  252. endoreg_db/models/metadata/pdf_meta.py +19 -24
  253. endoreg_db/models/metadata/sensitive_meta.py +102 -85
  254. endoreg_db/models/metadata/sensitive_meta_logic.py +383 -43
  255. endoreg_db/models/metadata/video_meta.py +51 -31
  256. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  257. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  258. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  259. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  260. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  261. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  262. endoreg_db/models/other/emission/emission_factor.py +18 -8
  263. endoreg_db/models/other/gender.py +10 -5
  264. endoreg_db/models/other/information_source.py +25 -25
  265. endoreg_db/models/other/material.py +9 -5
  266. endoreg_db/models/other/resource.py +6 -4
  267. endoreg_db/models/other/tag.py +10 -5
  268. endoreg_db/models/other/transport_route.py +13 -8
  269. endoreg_db/models/other/unit.py +10 -6
  270. endoreg_db/models/other/waste.py +6 -5
  271. endoreg_db/models/requirement/requirement.py +580 -272
  272. endoreg_db/models/requirement/requirement_error.py +85 -0
  273. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  274. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  275. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  276. endoreg_db/models/requirement/requirement_operator.py +36 -33
  277. endoreg_db/models/requirement/requirement_set.py +74 -57
  278. endoreg_db/models/state/__init__.py +4 -4
  279. endoreg_db/models/state/abstract.py +2 -2
  280. endoreg_db/models/state/anonymization.py +12 -0
  281. endoreg_db/models/state/audit_ledger.py +46 -47
  282. endoreg_db/models/state/label_video_segment.py +9 -0
  283. endoreg_db/models/state/raw_pdf.py +40 -46
  284. endoreg_db/models/state/sensitive_meta.py +6 -2
  285. endoreg_db/models/state/video.py +58 -53
  286. endoreg_db/models/upload_job.py +32 -55
  287. endoreg_db/models/utils.py +1 -2
  288. endoreg_db/root_urls.py +21 -2
  289. endoreg_db/serializers/__init__.py +26 -57
  290. endoreg_db/serializers/anonymization.py +18 -10
  291. endoreg_db/serializers/meta/report_meta.py +1 -1
  292. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  293. endoreg_db/serializers/misc/__init__.py +1 -1
  294. endoreg_db/serializers/misc/file_overview.py +33 -91
  295. endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
  296. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  297. endoreg_db/serializers/video/segmentation.py +2 -1
  298. endoreg_db/serializers/video/video_processing_history.py +20 -5
  299. endoreg_db/serializers/video_examination.py +198 -0
  300. endoreg_db/services/anonymization.py +75 -73
  301. endoreg_db/services/lookup_service.py +256 -73
  302. endoreg_db/services/lookup_store.py +174 -30
  303. endoreg_db/services/pdf_import.py +711 -310
  304. endoreg_db/services/storage_aware_video_processor.py +140 -114
  305. endoreg_db/services/video_import.py +266 -117
  306. endoreg_db/urls/__init__.py +27 -27
  307. endoreg_db/urls/label_video_segments.py +2 -0
  308. endoreg_db/urls/media.py +108 -66
  309. endoreg_db/urls/root_urls.py +29 -0
  310. endoreg_db/utils/__init__.py +15 -5
  311. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  312. endoreg_db/utils/case_generator/__init__.py +3 -0
  313. endoreg_db/utils/dataloader.py +88 -16
  314. endoreg_db/utils/defaults/set_default_center.py +32 -0
  315. endoreg_db/utils/names.py +22 -16
  316. endoreg_db/utils/permissions.py +2 -1
  317. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  318. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
  319. endoreg_db/utils/setup_config.py +8 -5
  320. endoreg_db/utils/storage.py +115 -0
  321. endoreg_db/utils/validate_endo_roi.py +8 -2
  322. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  323. endoreg_db/views/__init__.py +5 -12
  324. endoreg_db/views/anonymization/media_management.py +198 -163
  325. endoreg_db/views/anonymization/overview.py +4 -1
  326. endoreg_db/views/anonymization/validate.py +174 -40
  327. endoreg_db/views/media/__init__.py +2 -0
  328. endoreg_db/views/media/pdf_media.py +131 -150
  329. endoreg_db/views/media/sensitive_metadata.py +46 -6
  330. endoreg_db/views/media/video_media.py +89 -82
  331. endoreg_db/views/media/video_segments.py +187 -260
  332. endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
  333. endoreg_db/views/patient/patient.py +5 -4
  334. endoreg_db/views/pdf/__init__.py +5 -8
  335. endoreg_db/views/pdf/pdf_stream.py +186 -0
  336. endoreg_db/views/pdf/pdf_stream_views.py +0 -127
  337. endoreg_db/views/pdf/reimport.py +86 -91
  338. endoreg_db/views/requirement/evaluate.py +188 -187
  339. endoreg_db/views/requirement/lookup.py +186 -288
  340. endoreg_db/views/requirement/requirement_utils.py +89 -0
  341. endoreg_db/views/video/__init__.py +0 -4
  342. endoreg_db/views/video/correction.py +2 -2
  343. endoreg_db/views/video/video_examination_viewset.py +202 -289
  344. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
  345. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +350 -255
  346. endoreg_db/models/administration/permissions/__init__.py +0 -44
  347. endoreg_db/models/media/video/refactor_plan.md +0 -0
  348. endoreg_db/models/media/video/video_file_frames.py +0 -0
  349. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  350. endoreg_db/models/rule/__init__.py +0 -13
  351. endoreg_db/models/rule/rule.py +0 -27
  352. endoreg_db/models/rule/rule_applicator.py +0 -224
  353. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  354. endoreg_db/models/rule/rule_type.py +0 -20
  355. endoreg_db/models/rule/ruleset.py +0 -17
  356. endoreg_db/serializers/video/video_metadata.py +0 -105
  357. endoreg_db/urls/report.py +0 -48
  358. endoreg_db/urls/video.py +0 -61
  359. endoreg_db/utils/case_generator/case_generator.py +0 -159
  360. endoreg_db/utils/case_generator/utils.py +0 -30
  361. endoreg_db/views/pdf/pdf_media.py +0 -239
  362. endoreg_db/views/report/__init__.py +0 -9
  363. endoreg_db/views/report/report_list.py +0 -112
  364. endoreg_db/views/report/report_with_secure_url.py +0 -28
  365. endoreg_db/views/report/start_examination.py +0 -7
  366. endoreg_db/views/video/video_media.py +0 -158
  367. endoreg_db/views.py +0 -0
  368. /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
  369. /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
  370. /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
  371. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
  372. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -108,16 +108,9 @@ from .patient_finding_classification import (
108
108
  )
109
109
 
110
110
  from .pdf import (
111
- ClosingFileWrapper,
112
- PDFMediaView,
111
+ PdfReimportView,
112
+ PdfStreamView,
113
113
  )
114
-
115
- from .report import (
116
- ReportListView,
117
- ReportWithSecureUrlView,
118
- start_examination,
119
- )
120
-
121
114
  from .requirement import (
122
115
  evaluate_requirements,
123
116
  LookupViewSet,
@@ -125,7 +118,6 @@ from .requirement import (
125
118
 
126
119
  from .video import (
127
120
  # Video Correction (Phase 1.1) - Implemented
128
- VideoMetadataView,
129
121
  VideoProcessingHistoryView,
130
122
  VideoApplyMaskView,
131
123
  VideoRemoveFramesView,
@@ -137,7 +129,6 @@ from .video import (
137
129
  VideoLabelView,
138
130
  UpdateLabelSegmentsView,
139
131
  rerun_segmentation,
140
- video_timeline_view,
141
132
  VideoExaminationViewSet,
142
133
  VideoCorrectionView,
143
134
  )
@@ -240,7 +231,9 @@ __all__ = [
240
231
  "create_patient_finding_classification",
241
232
 
242
233
  # PDF
243
- "ClosingFileWrapper",
234
+ "PdfMediaView",
235
+ "PdfReimportView",
236
+ "PdfStreamView",
244
237
 
245
238
  # Report
246
239
  "ReportListView",
@@ -1,18 +1,19 @@
1
1
  # endoreg_db/views/media_management.py
2
2
 
3
3
  import logging
4
- from typing import Dict, Any
4
+ from datetime import timedelta
5
+ from typing import Any, Dict
6
+
5
7
  from django.db import transaction
6
8
  from django.db.models import Q
9
+ from django.utils import timezone
7
10
  from numpy import delete
8
11
  from rest_framework import status
9
12
  from rest_framework.decorators import api_view, permission_classes
10
13
  from rest_framework.response import Response
11
14
  from rest_framework.views import APIView
12
- from django.utils import timezone
13
- from datetime import timedelta
14
15
 
15
- from endoreg_db.models import VideoFile, RawPdfFile, VideoState
16
+ from endoreg_db.models import RawPdfFile, VideoFile, VideoState
16
17
  from endoreg_db.utils.permissions import DEBUG_PERMISSIONS
17
18
 
18
19
  logger = logging.getLogger(__name__)
@@ -21,10 +22,12 @@ logger = logging.getLogger(__name__)
21
22
  # Media Cleanup and Management API
22
23
  # ---------------------------------------------------------------------------
23
24
 
25
+
24
26
  class MediaManagementView(APIView):
25
27
  """
26
28
  Comprehensive Media Management API for cleanup and maintenance operations
27
29
  """
30
+
28
31
  permission_classes = DEBUG_PERMISSIONS
29
32
 
30
33
  def get(self, request):
@@ -38,8 +41,8 @@ class MediaManagementView(APIView):
38
41
  except Exception as e:
39
42
  logger.error(f"Error getting media status overview: {e}")
40
43
  return Response(
41
- {"error": "Failed to get status overview"},
42
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
44
+ {"error": "Failed to get status overview"},
45
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
43
46
  )
44
47
 
45
48
  def delete(self, request):
@@ -47,10 +50,10 @@ class MediaManagementView(APIView):
47
50
  DELETE /api/media-management/cleanup/
48
51
  Cleanup unfinished, failed, or stale media processing entries
49
52
  """
50
- cleanup_type = request.query_params.get('type', 'unfinished')
51
- force = request.query_params.get('force', 'false').lower() == 'true'
52
- media_type = request.query_params.get('file_type', 'all')
53
- file_id = request.query_params.get('file_id', None)
53
+ cleanup_type = request.query_params.get("type", "unfinished")
54
+ force = request.query_params.get("force", "false").lower() == "true"
55
+ media_type = request.query_params.get("file_type", "all")
56
+ file_id = request.query_params.get("file_id", None)
54
57
 
55
58
  try:
56
59
  result = self._perform_cleanup(cleanup_type, force, media_type, file_id)
@@ -59,40 +62,40 @@ class MediaManagementView(APIView):
59
62
  except Exception as e:
60
63
  logger.error(f"Error during media cleanup: {e}")
61
64
  return Response(
62
- {"error": "Cleanup operation failed"},
63
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
65
+ {"error": "Cleanup operation failed"},
66
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
64
67
  )
65
68
 
66
69
  def _get_status_overview(self) -> Dict[str, Any]:
67
70
  """Get comprehensive status overview"""
68
71
  video_stats = self._get_video_stats()
69
72
  pdf_stats = self._get_pdf_stats()
70
-
73
+
71
74
  # Stale processing detection (older than 2 hours)
72
75
  stale_threshold = timezone.now() - timedelta(hours=2)
73
76
  # Use VideoState boolean fields instead of non-existent name field
74
77
  stale_videos = VideoFile.objects.filter(
75
78
  uploaded_at__lt=stale_threshold,
76
79
  state__frames_extracted=True,
77
- state__sensitive_meta_processed=False
80
+ state__sensitive_meta_processed=False,
78
81
  ).count()
79
-
82
+
80
83
  return {
81
84
  "videos": video_stats,
82
85
  "pdfs": pdf_stats,
83
86
  "cleanup_opportunities": {
84
87
  "stale_processing": stale_videos,
85
88
  "failed_videos": video_stats["failed"],
86
- "unfinished_total": video_stats["unfinished"] + pdf_stats["unfinished"]
89
+ "unfinished_total": video_stats["unfinished"] + pdf_stats["unfinished"],
87
90
  },
88
91
  "total_files": video_stats["total"] + pdf_stats["total"],
89
- "timestamp": timezone.now().isoformat()
92
+ "timestamp": timezone.now().isoformat(),
90
93
  }
91
94
 
92
95
  def _get_video_stats(self) -> Dict[str, int]:
93
96
  """Get video file statistics using VideoState boolean fields"""
94
- videos = VideoFile.objects.select_related('state').all()
95
-
97
+ videos = VideoFile.objects.select_related("state").all()
98
+
96
99
  stats = {
97
100
  "total": videos.count(),
98
101
  "not_started": 0,
@@ -100,30 +103,33 @@ class MediaManagementView(APIView):
100
103
  "done": 0,
101
104
  "failed": 0,
102
105
  "validated": 0,
103
- "unfinished": 0
106
+ "unfinished": 0,
104
107
  }
105
-
108
+
106
109
  for video in videos:
107
110
  if not video.state:
108
111
  stats["not_started"] += 1
109
112
  stats["unfinished"] += 1
110
113
  continue
111
-
114
+
112
115
  # Use the anonymization_status property
113
116
  video_status = video.state.anonymization_status
114
-
115
- if video_status.value == 'not_started':
117
+
118
+ if video_status.value == "not_started":
116
119
  stats["not_started"] += 1
117
120
  stats["unfinished"] += 1
118
- elif video_status.value in ['extracting_frames', 'processing_anonymization']:
121
+ elif video_status.value in [
122
+ "extracting_frames",
123
+ "processing_anonymization",
124
+ ]:
119
125
  stats["processing"] += 1
120
126
  stats["unfinished"] += 1
121
- elif video_status.value == 'done':
127
+ elif video_status.value == "done_processing_anonymization":
122
128
  stats["done"] += 1
123
- elif video_status.value == 'failed':
129
+ elif video_status.value == "failed":
124
130
  stats["failed"] += 1
125
131
  stats["unfinished"] += 1
126
- elif video_status.value == 'validated':
132
+ elif video_status.value == "validated":
127
133
  stats["validated"] += 1
128
134
  else:
129
135
  stats["unfinished"] += 1
@@ -132,7 +138,7 @@ class MediaManagementView(APIView):
132
138
  def _get_pdf_stats(self) -> Dict[str, int]:
133
139
  """Get PDF file statistics"""
134
140
  pdfs = RawPdfFile.objects.all()
135
-
141
+
136
142
  stats = {
137
143
  "total": pdfs.count(),
138
144
  "not_started": 0,
@@ -140,14 +146,18 @@ class MediaManagementView(APIView):
140
146
  "done": 0,
141
147
  "failed": 0,
142
148
  "validated": 0,
143
- "unfinished": 0
149
+ "unfinished": 0,
144
150
  }
145
-
151
+
146
152
  for pdf in pdfs:
147
153
  # PDF status logic based on anonymized_text presence and validation
148
154
  has_anonymized = bool(pdf.anonymized_text and pdf.anonymized_text.strip())
149
- is_validated = getattr(pdf.sensitive_meta, 'is_verified', False) if pdf.sensitive_meta else False
150
-
155
+ is_validated = (
156
+ getattr(pdf.sensitive_meta, "is_verified", False)
157
+ if pdf.sensitive_meta
158
+ else False
159
+ )
160
+
151
161
  if not has_anonymized:
152
162
  stats["not_started"] += 1
153
163
  stats["unfinished"] += 1
@@ -157,22 +167,24 @@ class MediaManagementView(APIView):
157
167
  stats["done"] += 1
158
168
  else:
159
169
  stats["unfinished"] += 1
160
-
170
+
161
171
  return stats
162
172
 
163
- def _perform_cleanup(self, cleanup_type: str, force: bool, media_type: str, file_id: int) -> Dict[str, Any]:
173
+ def _perform_cleanup(
174
+ self, cleanup_type: str, force: bool, media_type: str, file_id: int
175
+ ) -> Dict[str, Any]:
164
176
  """Perform the actual cleanup operations"""
165
-
177
+
166
178
  result = {
167
179
  "cleanup_type": cleanup_type,
168
180
  "force": force,
169
181
  "removed_items": [],
170
- "summary": {}
182
+ "summary": {},
171
183
  }
172
-
173
- if media_type not in ['video', 'pdf', 'all']:
184
+
185
+ if media_type not in ["video", "pdf", "all"]:
174
186
  raise ValueError(f"Unknown media type: {media_type}")
175
-
187
+
176
188
  if file_id is not None:
177
189
  try:
178
190
  file_id = int(file_id)
@@ -181,81 +193,91 @@ class MediaManagementView(APIView):
181
193
 
182
194
  video_file_obj = None
183
195
  pdf_file_obj = None
184
-
185
- if media_type == 'video':
186
- video_file_obj = VideoFile.get_video_by_id(self, video_id=file_id) if file_id else None
187
- elif media_type == 'pdf':
188
- pdf_file_obj = RawPdfFile.get_pdf_by_id(file_id) if file_id else None
189
196
 
197
+ if media_type == "video":
198
+ video_file_obj = (
199
+ VideoFile.get_video_by_id(self, video_id=file_id) if file_id else None
200
+ )
201
+ elif media_type == "pdf":
202
+ pdf_file_obj = RawPdfFile.get_pdf_by_id(file_id) if file_id else None
190
203
 
191
204
  with transaction.atomic():
192
205
  if video_file_obj:
193
206
  video_file_obj.delete()
194
207
  if pdf_file_obj:
195
208
  pdf_file_obj.delete()
196
-
197
- if cleanup_type == 'unfinished':
209
+
210
+ if cleanup_type == "unfinished":
198
211
  result.update(self._cleanup_unfinished_media(force))
199
- elif cleanup_type == 'failed':
212
+ elif cleanup_type == "failed":
200
213
  result.update(self._cleanup_failed_media(force))
201
- elif cleanup_type == 'stale':
214
+ elif cleanup_type == "stale":
202
215
  result.update(self._cleanup_stale_processing(force))
203
- elif cleanup_type == 'all':
216
+ elif cleanup_type == "all":
204
217
  unfinished = self._cleanup_unfinished_media(force)
205
218
  failed = self._cleanup_failed_media(force)
206
219
  stale = self._cleanup_stale_processing(force)
207
-
220
+
208
221
  result["removed_items"] = (
209
- unfinished.get("removed_items", []) +
210
- failed.get("removed_items", []) +
211
- stale.get("removed_items", [])
222
+ unfinished.get("removed_items", [])
223
+ + failed.get("removed_items", [])
224
+ + stale.get("removed_items", [])
212
225
  )
213
226
  result["summary"] = {
214
227
  "unfinished": unfinished.get("summary", {}),
215
228
  "failed": failed.get("summary", {}),
216
- "stale": stale.get("summary", {})
229
+ "stale": stale.get("summary", {}),
217
230
  }
218
231
  else:
219
232
  raise ValueError(f"Unknown cleanup type: {cleanup_type}")
220
-
233
+
221
234
  return result
222
235
 
223
236
  def _cleanup_unfinished_media(self, force: bool) -> Dict[str, Any]:
224
237
  """Remove unfinished media processing entries"""
225
238
  removed_videos = []
226
239
  removed_pdfs = []
227
-
240
+
228
241
  # Find unfinished videos using VideoState boolean fields
229
- unfinished_videos = VideoFile.objects.select_related('state').all()
230
-
242
+ unfinished_videos = VideoFile.objects.select_related("state").all()
243
+
231
244
  for video in unfinished_videos:
232
245
  if not video.state:
233
246
  if force: # Only remove videos without state if force=True
234
- removed_videos.append({
235
- "id": video.id,
236
- "type": "video",
237
- "filename": video.original_file_name,
238
- "status": "no_state",
239
- "uploaded_at": video.uploaded_at.isoformat()
240
- })
247
+ removed_videos.append(
248
+ {
249
+ "id": video.id,
250
+ "type": "video",
251
+ "filename": video.original_file_name,
252
+ "status": "no_state",
253
+ "uploaded_at": video.uploaded_at.isoformat(),
254
+ }
255
+ )
241
256
  video.delete()
242
257
  continue
243
-
258
+
244
259
  video_status = video.state.anonymization_status
245
- is_unfinished = video_status.value in ['not_started', 'extracting_frames', 'processing_anonymization', 'failed']
246
-
260
+ is_unfinished = video_status.value in [
261
+ "not_started",
262
+ "extracting_frames",
263
+ "processing_anonymization",
264
+ "failed",
265
+ ]
266
+
247
267
  # Remove unfinished videos
248
- if is_unfinished and (force or video_status.value != 'not_started'):
249
- removed_videos.append({
250
- "id": video.id,
251
- "type": "video",
252
- "filename": video.original_file_name,
253
- "status": video_status.value,
254
- "uploaded_at": video.uploaded_at.isoformat()
255
- })
268
+ if is_unfinished and (force or video_status.value != "not_started"):
269
+ removed_videos.append(
270
+ {
271
+ "id": video.id,
272
+ "type": "video",
273
+ "filename": video.original_file_name,
274
+ "status": video_status.value,
275
+ "uploaded_at": video.uploaded_at.isoformat(),
276
+ }
277
+ )
256
278
  if force:
257
279
  video.delete()
258
-
280
+
259
281
  # Return the results
260
282
  return {
261
283
  "removed_items": removed_videos + removed_pdfs,
@@ -263,29 +285,31 @@ class MediaManagementView(APIView):
263
285
  "videos_removed": len(removed_videos),
264
286
  "pdfs_removed": len(removed_pdfs),
265
287
  "total_removed": len(removed_videos) + len(removed_pdfs),
266
- "dry_run": not force
267
- }
288
+ "dry_run": not force,
289
+ },
268
290
  }
269
291
 
270
292
  def _cleanup_failed_media(self, force: bool) -> Dict[str, Any]:
271
293
  """Remove failed media processing entries"""
272
294
  removed_items = []
273
-
295
+
274
296
  # Find failed videos using VideoState boolean fields
275
- failed_videos = VideoFile.objects.select_related('state').all()
276
-
297
+ failed_videos = VideoFile.objects.select_related("state").all()
298
+
277
299
  for video in failed_videos:
278
- if video.state and video.state.anonymization_status.value == 'failed':
279
- removed_items.append({
280
- "id": video.id,
281
- "type": "video",
282
- "filename": video.original_file_name,
283
- "status": "failed",
284
- "uploaded_at": video.uploaded_at.isoformat()
285
- })
300
+ if video.state and video.state.anonymization_status.value == "failed":
301
+ removed_items.append(
302
+ {
303
+ "id": video.id,
304
+ "type": "video",
305
+ "filename": video.original_file_name,
306
+ "status": "failed",
307
+ "uploaded_at": video.uploaded_at.isoformat(),
308
+ }
309
+ )
286
310
  if force:
287
311
  video.delete()
288
-
312
+
289
313
  if force:
290
314
  # Count actual deletions
291
315
  videos_deleted = len([v for v in removed_items if v["type"] == "video"])
@@ -294,8 +318,8 @@ class MediaManagementView(APIView):
294
318
  "summary": {
295
319
  "videos_removed": videos_deleted,
296
320
  "total_removed": videos_deleted,
297
- "dry_run": False
298
- }
321
+ "dry_run": False,
322
+ },
299
323
  }
300
324
  else:
301
325
  return {
@@ -303,33 +327,40 @@ class MediaManagementView(APIView):
303
327
  "summary": {
304
328
  "videos_removed": len(removed_items),
305
329
  "total_removed": len(removed_items),
306
- "dry_run": True
307
- }
330
+ "dry_run": True,
331
+ },
308
332
  }
309
333
 
310
334
  def _cleanup_stale_processing(self, force: bool) -> Dict[str, Any]:
311
335
  """Remove stale processing entries (older than 2 hours)"""
312
336
  stale_threshold = timezone.now() - timedelta(hours=2)
313
337
  removed_items = []
314
-
338
+
315
339
  # Find stale videos using VideoState boolean fields
316
340
  stale_videos = VideoFile.objects.filter(
317
341
  uploaded_at__lt=stale_threshold,
318
342
  state__frames_extracted=True,
319
- state__sensitive_meta_processed=False
320
- ).select_related('state')
321
-
343
+ state__sensitive_meta_processed=False,
344
+ ).select_related("state")
345
+
322
346
  for video in stale_videos:
323
- video_status = video.state.anonymization_status if video.state else "no_state"
324
- removed_items.append({
325
- "id": video.id,
326
- "type": "video",
327
- "filename": video.original_file_name,
328
- "status": f"stale_{video_status.value if hasattr(video_status, 'value') else video_status}",
329
- "uploaded_at": video.uploaded_at.isoformat(),
330
- "stale_duration_hours": (timezone.now() - video.uploaded_at).total_seconds() / 3600
331
- })
332
-
347
+ video_status = (
348
+ video.state.anonymization_status if video.state else "no_state"
349
+ )
350
+ removed_items.append(
351
+ {
352
+ "id": video.id,
353
+ "type": "video",
354
+ "filename": video.original_file_name,
355
+ "status": f"stale_{video_status.value if hasattr(video_status, 'value') else video_status}",
356
+ "uploaded_at": video.uploaded_at.isoformat(),
357
+ "stale_duration_hours": (
358
+ timezone.now() - video.uploaded_at
359
+ ).total_seconds()
360
+ / 3600,
361
+ }
362
+ )
363
+
333
364
  if force:
334
365
  videos_deleted = stale_videos.delete()[0]
335
366
  return {
@@ -337,8 +368,8 @@ class MediaManagementView(APIView):
337
368
  "summary": {
338
369
  "stale_videos_removed": videos_deleted,
339
370
  "total_removed": videos_deleted,
340
- "dry_run": False
341
- }
371
+ "dry_run": False,
372
+ },
342
373
  }
343
374
  else:
344
375
  return {
@@ -346,12 +377,12 @@ class MediaManagementView(APIView):
346
377
  "summary": {
347
378
  "stale_videos_removed": len(removed_items),
348
379
  "total_removed": len(removed_items),
349
- "dry_run": True
350
- }
380
+ "dry_run": True,
381
+ },
351
382
  }
352
383
 
353
384
 
354
- @api_view(['DELETE'])
385
+ @api_view(["DELETE"])
355
386
  @permission_classes(DEBUG_PERMISSIONS)
356
387
  def force_remove_media(request, file_id: int):
357
388
  """
@@ -364,43 +395,44 @@ def force_remove_media(request, file_id: int):
364
395
  video = VideoFile.objects.get(id=file_id)
365
396
  filename = video.original_file_name
366
397
  video.delete()
367
-
368
- return Response({
369
- "detail": f"Video file '{filename}' (ID: {file_id}) removed successfully",
370
- "file_type": "video",
371
- "file_id": file_id
372
- })
398
+
399
+ return Response(
400
+ {
401
+ "detail": f"Video file '{filename}' (ID: {file_id}) removed successfully",
402
+ "file_type": "video",
403
+ "file_id": file_id,
404
+ }
405
+ )
373
406
  except VideoFile.DoesNotExist:
374
407
  pass
375
-
408
+
376
409
  # Try to find and delete from RawPdfFile
377
410
  try:
378
411
  pdf = RawPdfFile.objects.get(id=file_id)
379
- filename = getattr(pdf.file, 'name', 'Unknown')
412
+ filename = getattr(pdf.file, "name", "Unknown")
380
413
  pdf.delete()
381
-
382
- return Response({
383
- "detail": f"PDF file '{filename}' (ID: {file_id}) removed successfully",
384
- "file_type": "pdf",
385
- "file_id": file_id
386
- })
414
+
415
+ return Response(
416
+ {
417
+ "detail": f"PDF file '{filename}' (ID: {file_id}) removed successfully",
418
+ "file_type": "pdf",
419
+ "file_id": file_id,
420
+ }
421
+ )
387
422
  except RawPdfFile.DoesNotExist:
388
423
  pass
389
-
390
- return Response(
391
- {"detail": "File not found"},
392
- status=status.HTTP_404_NOT_FOUND
393
- )
394
-
424
+
425
+ return Response({"detail": "File not found"}, status=status.HTTP_404_NOT_FOUND)
426
+
395
427
  except Exception as e:
396
428
  logger.error(f"Error force removing media {file_id}: {e}")
397
429
  return Response(
398
- {"error": "Force removal failed"},
399
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
430
+ {"error": "Force removal failed"},
431
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
400
432
  )
401
433
 
402
434
 
403
- @api_view(['POST'])
435
+ @api_view(["POST"])
404
436
  @permission_classes(DEBUG_PERMISSIONS)
405
437
  def reset_processing_status(request, file_id: int):
406
438
  """
@@ -411,44 +443,47 @@ def reset_processing_status(request, file_id: int):
411
443
  # Try VideoFile first
412
444
  try:
413
445
  video = VideoFile.objects.get(id=file_id)
414
-
446
+
415
447
  # Reset to 'not_started' state
416
- not_started_state, created = VideoState.objects.get_or_create(name='not_started')
448
+ not_started_state, created = VideoState.objects.get_or_create(
449
+ name="not_started"
450
+ )
417
451
  video.state = not_started_state
418
452
  video.save()
419
-
420
- return Response({
421
- "detail": "Video file status reset to 'not_started'",
422
- "file_type": "video",
423
- "file_id": file_id,
424
- "new_status": "not_started"
425
- })
453
+
454
+ return Response(
455
+ {
456
+ "detail": "Video file status reset to 'not_started'",
457
+ "file_type": "video",
458
+ "file_id": file_id,
459
+ "new_status": "not_started",
460
+ }
461
+ )
426
462
  except VideoFile.DoesNotExist:
427
463
  pass
428
-
464
+
429
465
  # PDF files don't have state, but we can clear anonymized_text
430
466
  try:
431
467
  pdf = RawPdfFile.objects.get(id=file_id)
432
468
  pdf.anonymized_text = ""
433
469
  pdf.save()
434
-
435
- return Response({
436
- "detail": "PDF file processing reset",
437
- "file_type": "pdf",
438
- "file_id": file_id,
439
- "new_status": "not_started"
440
- })
470
+
471
+ return Response(
472
+ {
473
+ "detail": "PDF file processing reset",
474
+ "file_type": "pdf",
475
+ "file_id": file_id,
476
+ "new_status": "not_started",
477
+ }
478
+ )
441
479
  except RawPdfFile.DoesNotExist:
442
480
  pass
443
-
444
- return Response(
445
- {"detail": "File not found"},
446
- status=status.HTTP_404_NOT_FOUND
447
- )
448
-
481
+
482
+ return Response({"detail": "File not found"}, status=status.HTTP_404_NOT_FOUND)
483
+
449
484
  except Exception as e:
450
485
  logger.error(f"Error resetting status for media {file_id}: {e}")
451
486
  return Response(
452
- {"error": "Status reset failed"},
453
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
487
+ {"error": "Status reset failed"},
488
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
454
489
  )