endoreg-db 0.8.6.1__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 (360) 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_center_data.py +12 -12
  117. endoreg_db/management/commands/load_requirement_data.py +60 -31
  118. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  119. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  120. endoreg_db/management/commands/storage_management.py +271 -203
  121. endoreg_db/migrations/0001_initial.py +1799 -1300
  122. endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
  123. endoreg_db/migrations/_old/0001_initial.py +1857 -0
  124. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
  125. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
  126. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
  127. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
  128. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
  129. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
  130. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
  131. endoreg_db/models/__init__.py +78 -123
  132. endoreg_db/models/administration/__init__.py +21 -42
  133. endoreg_db/models/administration/ai/active_model.py +2 -2
  134. endoreg_db/models/administration/ai/ai_model.py +7 -6
  135. endoreg_db/models/administration/case/__init__.py +1 -15
  136. endoreg_db/models/administration/case/case.py +3 -3
  137. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  138. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  139. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  140. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  141. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  142. endoreg_db/models/administration/center/center.py +33 -19
  143. endoreg_db/models/administration/center/center_product.py +12 -9
  144. endoreg_db/models/administration/center/center_resource.py +25 -19
  145. endoreg_db/models/administration/center/center_shift.py +21 -17
  146. endoreg_db/models/administration/center/center_waste.py +16 -8
  147. endoreg_db/models/administration/person/__init__.py +2 -0
  148. endoreg_db/models/administration/person/employee/employee.py +10 -5
  149. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  150. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  151. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  152. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  153. endoreg_db/models/administration/person/patient/patient.py +103 -100
  154. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  155. endoreg_db/models/administration/person/person.py +4 -0
  156. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  157. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  158. endoreg_db/models/administration/product/product.py +20 -15
  159. endoreg_db/models/administration/product/product_material.py +17 -18
  160. endoreg_db/models/administration/product/product_weight.py +12 -8
  161. endoreg_db/models/administration/product/reference_product.py +23 -55
  162. endoreg_db/models/administration/qualification/qualification.py +7 -3
  163. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  164. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  165. endoreg_db/models/administration/shift/shift.py +16 -12
  166. endoreg_db/models/administration/shift/shift_type.py +23 -31
  167. endoreg_db/models/label/__init__.py +7 -8
  168. endoreg_db/models/label/annotation/image_classification.py +10 -9
  169. endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
  170. endoreg_db/models/label/label.py +15 -15
  171. endoreg_db/models/label/label_set.py +19 -6
  172. endoreg_db/models/label/label_type.py +1 -1
  173. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  174. endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
  175. endoreg_db/models/label/video_segmentation_label.py +4 -0
  176. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  177. endoreg_db/models/media/frame/frame.py +22 -22
  178. endoreg_db/models/media/pdf/raw_pdf.py +110 -182
  179. endoreg_db/models/media/pdf/report_file.py +25 -29
  180. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
  181. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  182. endoreg_db/models/media/video/__init__.py +1 -0
  183. endoreg_db/models/media/video/create_from_file.py +48 -56
  184. endoreg_db/models/media/video/pipe_2.py +8 -9
  185. endoreg_db/models/media/video/video_file.py +150 -108
  186. endoreg_db/models/media/video/video_file_ai.py +288 -74
  187. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  188. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  189. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  190. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  191. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  192. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  193. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  194. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  195. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  196. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  197. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  198. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  199. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  200. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  201. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  202. endoreg_db/models/media/video/video_file_io.py +109 -62
  203. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  204. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  205. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  206. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  207. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  208. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  209. endoreg_db/models/media/video/video_file_segments.py +24 -17
  210. endoreg_db/models/media/video/video_metadata.py +19 -35
  211. endoreg_db/models/media/video/video_processing.py +96 -95
  212. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  213. endoreg_db/models/medical/disease.py +22 -16
  214. endoreg_db/models/medical/event.py +31 -18
  215. endoreg_db/models/medical/examination/__init__.py +13 -6
  216. endoreg_db/models/medical/examination/examination.py +17 -18
  217. endoreg_db/models/medical/examination/examination_indication.py +26 -25
  218. endoreg_db/models/medical/examination/examination_time.py +16 -6
  219. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  220. endoreg_db/models/medical/examination/examination_type.py +3 -4
  221. endoreg_db/models/medical/finding/finding.py +38 -39
  222. endoreg_db/models/medical/finding/finding_classification.py +37 -48
  223. endoreg_db/models/medical/finding/finding_intervention.py +27 -22
  224. endoreg_db/models/medical/finding/finding_type.py +13 -12
  225. endoreg_db/models/medical/hardware/endoscope.py +20 -26
  226. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  227. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  228. endoreg_db/models/medical/medication/medication.py +22 -10
  229. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  230. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  231. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  232. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  233. endoreg_db/models/medical/organ/__init__.py +15 -12
  234. endoreg_db/models/medical/patient/medication_examples.py +1 -5
  235. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  236. endoreg_db/models/medical/patient/patient_event.py +19 -22
  237. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  238. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  239. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  240. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  241. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  242. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  243. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  244. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  245. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  246. endoreg_db/models/medical/risk/risk.py +7 -6
  247. endoreg_db/models/medical/risk/risk_type.py +8 -5
  248. endoreg_db/models/metadata/model_meta.py +60 -29
  249. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  250. endoreg_db/models/metadata/pdf_meta.py +19 -24
  251. endoreg_db/models/metadata/sensitive_meta.py +102 -85
  252. endoreg_db/models/metadata/sensitive_meta_logic.py +192 -173
  253. endoreg_db/models/metadata/video_meta.py +51 -31
  254. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  255. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  256. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  257. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  258. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  259. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  260. endoreg_db/models/other/emission/emission_factor.py +18 -8
  261. endoreg_db/models/other/gender.py +10 -5
  262. endoreg_db/models/other/information_source.py +25 -25
  263. endoreg_db/models/other/material.py +9 -5
  264. endoreg_db/models/other/resource.py +6 -4
  265. endoreg_db/models/other/tag.py +10 -5
  266. endoreg_db/models/other/transport_route.py +13 -8
  267. endoreg_db/models/other/unit.py +10 -6
  268. endoreg_db/models/other/waste.py +6 -5
  269. endoreg_db/models/requirement/requirement.py +580 -272
  270. endoreg_db/models/requirement/requirement_error.py +85 -0
  271. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  272. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  273. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  274. endoreg_db/models/requirement/requirement_operator.py +36 -33
  275. endoreg_db/models/requirement/requirement_set.py +74 -57
  276. endoreg_db/models/state/__init__.py +4 -4
  277. endoreg_db/models/state/abstract.py +2 -2
  278. endoreg_db/models/state/anonymization.py +12 -0
  279. endoreg_db/models/state/audit_ledger.py +46 -47
  280. endoreg_db/models/state/label_video_segment.py +9 -0
  281. endoreg_db/models/state/raw_pdf.py +40 -46
  282. endoreg_db/models/state/sensitive_meta.py +6 -2
  283. endoreg_db/models/state/video.py +58 -53
  284. endoreg_db/models/upload_job.py +32 -55
  285. endoreg_db/models/utils.py +1 -2
  286. endoreg_db/root_urls.py +21 -2
  287. endoreg_db/serializers/__init__.py +0 -2
  288. endoreg_db/serializers/anonymization.py +18 -10
  289. endoreg_db/serializers/meta/report_meta.py +1 -1
  290. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  291. endoreg_db/serializers/misc/file_overview.py +11 -99
  292. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  293. endoreg_db/serializers/video/segmentation.py +2 -1
  294. endoreg_db/serializers/video/video_processing_history.py +20 -5
  295. endoreg_db/services/anonymization.py +75 -73
  296. endoreg_db/services/lookup_service.py +37 -24
  297. endoreg_db/services/pdf_import.py +166 -68
  298. endoreg_db/services/storage_aware_video_processor.py +140 -114
  299. endoreg_db/services/video_import.py +193 -283
  300. endoreg_db/urls/__init__.py +7 -20
  301. endoreg_db/urls/media.py +108 -67
  302. endoreg_db/urls/root_urls.py +29 -0
  303. endoreg_db/utils/__init__.py +15 -5
  304. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  305. endoreg_db/utils/case_generator/__init__.py +3 -0
  306. endoreg_db/utils/dataloader.py +88 -16
  307. endoreg_db/utils/defaults/set_default_center.py +32 -0
  308. endoreg_db/utils/names.py +22 -16
  309. endoreg_db/utils/permissions.py +2 -1
  310. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  311. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
  312. endoreg_db/utils/setup_config.py +8 -5
  313. endoreg_db/utils/storage.py +115 -0
  314. endoreg_db/utils/validate_endo_roi.py +8 -2
  315. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  316. endoreg_db/views/__init__.py +0 -10
  317. endoreg_db/views/anonymization/media_management.py +198 -163
  318. endoreg_db/views/anonymization/overview.py +4 -1
  319. endoreg_db/views/anonymization/validate.py +174 -40
  320. endoreg_db/views/media/__init__.py +2 -0
  321. endoreg_db/views/media/pdf_media.py +131 -152
  322. endoreg_db/views/media/sensitive_metadata.py +46 -6
  323. endoreg_db/views/media/video_media.py +89 -82
  324. endoreg_db/views/media/video_segments.py +2 -3
  325. endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
  326. endoreg_db/views/patient/patient.py +5 -4
  327. endoreg_db/views/pdf/pdf_stream.py +20 -21
  328. endoreg_db/views/pdf/reimport.py +11 -32
  329. endoreg_db/views/requirement/evaluate.py +188 -187
  330. endoreg_db/views/requirement/lookup.py +17 -3
  331. endoreg_db/views/requirement/requirement_utils.py +89 -0
  332. endoreg_db/views/video/__init__.py +0 -2
  333. endoreg_db/views/video/correction.py +2 -2
  334. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
  335. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +341 -245
  336. endoreg_db/models/administration/permissions/__init__.py +0 -44
  337. endoreg_db/models/media/video/video_file_frames.py +0 -0
  338. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  339. endoreg_db/models/rule/__init__.py +0 -13
  340. endoreg_db/models/rule/rule.py +0 -27
  341. endoreg_db/models/rule/rule_applicator.py +0 -224
  342. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  343. endoreg_db/models/rule/rule_type.py +0 -20
  344. endoreg_db/models/rule/ruleset.py +0 -17
  345. endoreg_db/serializers/video/video_metadata.py +0 -105
  346. endoreg_db/urls/report.py +0 -48
  347. endoreg_db/urls/video.py +0 -61
  348. endoreg_db/utils/case_generator/case_generator.py +0 -159
  349. endoreg_db/utils/case_generator/utils.py +0 -30
  350. endoreg_db/views/report/__init__.py +0 -9
  351. endoreg_db/views/report/report_list.py +0 -112
  352. endoreg_db/views/report/report_with_secure_url.py +0 -28
  353. endoreg_db/views/report/start_examination.py +0 -7
  354. endoreg_db/views.py +0 -0
  355. /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
  356. /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
  357. /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
  358. /endoreg_db/{models/media/video/refactor_plan.md → views/pdf/pdf_stream_views.py} +0 -0
  359. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
  360. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -9,15 +9,18 @@ This is separate from the existing pdf.PDFMediaView which handles legacy workflo
9
9
 
10
10
  import logging
11
11
  import os
12
- from django.http import Http404, FileResponse
12
+ from pathlib import Path
13
+
14
+ from django.db.models import Q
15
+ from django.http import FileResponse, Http404
16
+ from django.views.decorators.clickjacking import xframe_options_exempt
13
17
  from rest_framework import status
14
18
  from rest_framework.response import Response
15
19
  from rest_framework.views import APIView
16
- from django.views.decorators.clickjacking import xframe_options_exempt
17
- from django.db.models import Q
18
20
 
19
21
  from endoreg_db.models import RawPdfFile
20
22
  from endoreg_db.utils.permissions import EnvironmentAwarePermission
23
+ from endoreg_db.utils.storage import file_exists
21
24
 
22
25
  logger = logging.getLogger(__name__)
23
26
 
@@ -25,25 +28,25 @@ logger = logging.getLogger(__name__)
25
28
  class PdfMediaView(APIView):
26
29
  """
27
30
  PDF Media Management API for CRUD operations on PDF files.
28
-
31
+
29
32
  Endpoints:
30
33
  - GET /api/media/pdfs/ - List all PDFs with filtering
31
34
  - GET /api/media/pdfs/{id}/ - Get PDF details
32
35
  - GET /api/media/pdfs/{id}/stream/ - Stream PDF file (same as detail for PDFs)
33
36
  - PATCH /api/media/pdfs/{id}/ - Update PDF metadata (future)
34
37
  - DELETE /api/media/pdfs/{id}/ - Delete PDF (future)
35
-
38
+
36
39
  Query Parameters:
37
40
  - status: Filter by processing status (not_started, done, validated)
38
41
  - search: Search in filename
39
42
  - limit: Limit results (default: 50)
40
43
  - offset: Pagination offset
41
-
44
+
42
45
  Examples:
43
46
  - GET /api/media/pdfs/?status=done&search=exam
44
47
  - GET /api/media/pdfs/123/
45
48
  - GET /api/media/pdfs/123/stream/
46
-
49
+
47
50
  Phase 1.2 Implementation:
48
51
  - List and detail views implemented
49
52
  - PDF streaming functionality
@@ -51,25 +54,26 @@ class PdfMediaView(APIView):
51
54
  - Pagination support
52
55
  - Error handling with proper HTTP status codes
53
56
  """
57
+
54
58
  permission_classes = [EnvironmentAwarePermission]
55
59
 
56
60
  def get(self, request, pk=None):
57
61
  """
58
62
  Handle GET requests for PDF listing, detail retrieval, or streaming.
59
-
63
+
60
64
  Args:
61
65
  request: HTTP request object
62
66
  pk: Optional PDF ID for detail view or streaming
63
-
67
+
64
68
  Returns:
65
69
  Response or FileResponse: JSON response with PDF data or PDF file stream
66
-
70
+
67
71
  Raises:
68
72
  Http404: If specific PDF not found
69
73
  """
70
74
  if pk is not None:
71
75
  # Check if this is a streaming request
72
- if request.path.endswith('/stream/'):
76
+ if request.path.endswith("/stream/"):
73
77
  return self._stream_pdf(pk)
74
78
  else:
75
79
  # Detail view
@@ -81,13 +85,13 @@ class PdfMediaView(APIView):
81
85
  def _get_pdf_detail(self, pk):
82
86
  """
83
87
  Get detailed information for a specific PDF.
84
-
88
+
85
89
  Args:
86
90
  pk: PDF primary key
87
-
91
+
88
92
  Returns:
89
93
  Response: JSON response with PDF details
90
-
94
+
91
95
  Raises:
92
96
  Http404: If PDF not found
93
97
  """
@@ -97,55 +101,54 @@ class PdfMediaView(APIView):
97
101
  pdf_id_int = int(pk)
98
102
  except (ValueError, TypeError):
99
103
  raise Http404("Invalid PDF ID format")
100
-
104
+
101
105
  # Fetch PDF with related data
102
- pdf = RawPdfFile.objects.select_related('sensitive_meta').get(pk=pdf_id_int)
103
-
106
+ pdf = RawPdfFile.objects.select_related("sensitive_meta").get(pk=pdf_id_int)
107
+
104
108
  # Build PDF details
105
109
  pdf_data = {
106
- "id": pdf.id,
107
- "filename": getattr(pdf.file, 'name', 'Unknown'),
108
- "file_size": getattr(pdf.file, 'size', 0),
110
+ "id": pdf.pk,
111
+ "filename": getattr(pdf.file, "name", "Unknown"),
112
+ "file_size": getattr(pdf.file, "size", 0),
109
113
  "pdf_hash": pdf.pdf_hash,
110
- "uploaded_at": pdf.uploaded_at.isoformat() if hasattr(pdf, 'uploaded_at') else None,
114
+ "uploaded_at": pdf.date_created.isoformat() if getattr(pdf, "date_created", None) else None,
111
115
  "anonymized_text": pdf.anonymized_text,
112
116
  "has_anonymized_text": bool(pdf.anonymized_text and pdf.anonymized_text.strip()),
113
- "is_validated": getattr(pdf.sensitive_meta, 'is_verified', False) if pdf.sensitive_meta else False,
114
- "stream_url": self.request.build_absolute_uri(f"/api/media/pdfs/{pdf.id}/stream/"),
117
+ "is_validated": getattr(pdf.sensitive_meta, "is_verified", False) if pdf.sensitive_meta else False,
118
+ "stream_url": self.request.build_absolute_uri(f"/api/media/pdfs/{pdf.pk}/stream/"),
115
119
  }
116
-
120
+
117
121
  # Add patient metadata if available
118
122
  if pdf.sensitive_meta:
119
- pdf_data.update({
120
- "patient_first_name": pdf.sensitive_meta.patient_first_name,
121
- "patient_last_name": pdf.sensitive_meta.patient_last_name,
122
- "patient_dob": pdf.sensitive_meta.patient_dob.strftime("%d.%m.%Y") if pdf.sensitive_meta.patient_dob else None,
123
- "examination_date": pdf.sensitive_meta.examination_date.strftime("%d.%m.%Y") if pdf.sensitive_meta.examination_date else None,
124
- })
125
-
123
+ pdf_data.update(
124
+ {
125
+ "patient_first_name": pdf.sensitive_meta.patient_first_name,
126
+ "patient_last_name": pdf.sensitive_meta.patient_last_name,
127
+ "patient_dob": pdf.sensitive_meta.patient_dob.strftime("%d.%m.%Y") if pdf.sensitive_meta.patient_dob else None,
128
+ "examination_date": pdf.sensitive_meta.examination_date.strftime("%d.%m.%Y") if pdf.sensitive_meta.examination_date else None,
129
+ }
130
+ )
131
+
126
132
  return Response(pdf_data)
127
-
133
+
128
134
  except RawPdfFile.DoesNotExist:
129
135
  raise Http404(f"PDF with ID {pk} not found")
130
-
136
+
131
137
  except Exception as e:
132
138
  logger.error(f"Unexpected error in PDF detail view for ID {pk}: {str(e)}")
133
- return Response(
134
- {"error": "Failed to retrieve PDF details"},
135
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
136
- )
137
-
139
+ return Response({"error": "Failed to retrieve PDF details"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
140
+
138
141
  @xframe_options_exempt
139
142
  def _stream_pdf(self, pk):
140
143
  """
141
144
  Stream PDF file content for viewing/download.
142
-
145
+
143
146
  Args:
144
147
  pk: PDF primary key
145
-
148
+
146
149
  Returns:
147
150
  FileResponse: PDF file stream
148
-
151
+
149
152
  Raises:
150
153
  Http404: If PDF not found or file cannot be accessed
151
154
  """
@@ -155,45 +158,41 @@ class PdfMediaView(APIView):
155
158
  pdf_id_int = int(pk)
156
159
  except (ValueError, TypeError):
157
160
  raise Http404("Invalid PDF ID format")
158
-
161
+
159
162
  # Fetch PDF
160
163
  pdf = RawPdfFile.objects.get(pk=pdf_id_int)
161
-
162
- # Check if file exists
163
- if not pdf.file or not pdf.file.name:
164
+
165
+ file_field = pdf.file
166
+
167
+ if not file_field or not file_field.name:
164
168
  raise Http404("PDF file not found")
165
-
169
+ if not file_exists(file_field):
170
+ raise Http404("PDF file does not exist in storage")
171
+
166
172
  try:
167
- # Open file for streaming
168
- file_path = pdf.file.path
169
- if not os.path.exists(file_path):
170
- raise Http404("PDF file does not exist on disk")
171
-
172
- # Create file response
173
- response = FileResponse(
174
- open(file_path, 'rb'),
175
- content_type='application/pdf',
176
- as_attachment=False # View in browser, not download
177
- )
178
-
179
- # Set filename for browser
180
- filename = os.path.basename(pdf.file.name)
181
- response['Content-Disposition'] = f'inline; filename="{filename}"'
182
-
183
- # CORS headers for frontend access
184
- frontend_origin = os.environ.get('FRONTEND_ORIGIN', 'http://localhost:8000')
185
- response['Access-Control-Allow-Origin'] = frontend_origin
186
- response['Access-Control-Allow-Credentials'] = 'true'
187
-
188
- return response
189
-
190
- except (OSError, IOError) as e:
191
- logger.error(f"File access error for PDF {pk}: {str(e)}")
173
+ file_field.open("rb")
174
+ except Exception as exc: # pragma: no cover - backend-specific failure
175
+ logger.error("File access error for PDF %s: %s", pk, exc)
192
176
  raise Http404("PDF file cannot be accessed")
193
-
177
+
178
+ response = FileResponse(
179
+ file_field,
180
+ content_type="application/pdf",
181
+ as_attachment=False,
182
+ )
183
+
184
+ filename = Path(file_field.name).name
185
+ response["Content-Disposition"] = f'inline; filename="{filename}"'
186
+
187
+ frontend_origin = os.environ.get("FRONTEND_ORIGIN", "http://localhost:8000")
188
+ response["Access-Control-Allow-Origin"] = frontend_origin
189
+ response["Access-Control-Allow-Credentials"] = "true"
190
+
191
+ return response
192
+
194
193
  except RawPdfFile.DoesNotExist:
195
194
  raise Http404(f"PDF with ID {pk} not found")
196
-
195
+
197
196
  except Exception as e:
198
197
  logger.error(f"Unexpected error in PDF streaming for ID {pk}: {str(e)}")
199
198
  raise Http404("PDF file cannot be streamed")
@@ -201,53 +200,51 @@ class PdfMediaView(APIView):
201
200
  def _list_pdfs(self, request):
202
201
  """
203
202
  List PDFs with filtering, search, and pagination.
204
-
203
+
205
204
  Args:
206
205
  request: HTTP request with query parameters
207
-
206
+
208
207
  Returns:
209
208
  Response: JSON response with paginated PDF list
210
209
  """
211
210
  try:
212
211
  # Start with all PDFs
213
- queryset = RawPdfFile.objects.select_related('sensitive_meta').all()
214
-
212
+ queryset = RawPdfFile.objects.select_related("sensitive_meta").all()
213
+
215
214
  # Apply filters
216
215
  queryset = self._apply_filters(queryset, request.query_params)
217
-
216
+
218
217
  # Apply search
219
- search = request.query_params.get('search', '').strip()
218
+ search = request.query_params.get("search", "").strip()
220
219
  if search:
221
- queryset = queryset.filter(
222
- Q(file__icontains=search)
223
- )
224
-
220
+ queryset = queryset.filter(Q(file__icontains=search))
221
+
225
222
  # Order by upload date (newest first) or id if no upload date
226
- if hasattr(queryset.model, 'uploaded_at'):
227
- queryset = queryset.order_by('-uploaded_at')
223
+ if hasattr(queryset.model, "date_created"):
224
+ queryset = queryset.order_by("-date_created")
228
225
  else:
229
- queryset = queryset.order_by('-id')
230
-
226
+ queryset = queryset.order_by("-pk")
227
+
231
228
  # Apply pagination
232
- limit = min(int(request.query_params.get('limit', 50)), 100)
233
- offset = int(request.query_params.get('offset', 0))
234
-
229
+ limit = min(int(request.query_params.get("limit", 50)), 100)
230
+ offset = int(request.query_params.get("offset", 0))
231
+
235
232
  total_count = queryset.count()
236
- pdfs = queryset[offset:offset + limit]
237
-
233
+ pdfs = queryset[offset : offset + limit]
234
+
238
235
  # Serialize PDFs manually (no dedicated serializer yet)
239
236
  results = []
240
237
  for pdf in pdfs:
241
238
  pdf_item = {
242
- "id": pdf.id,
243
- "filename": getattr(pdf.file, 'name', 'Unknown'),
239
+ "id": pdf.pk,
240
+ "filename": getattr(pdf.file, "name", "Unknown"),
244
241
  "file_size": self._safe_get_file_size(pdf.file),
245
242
  "pdf_hash": pdf.pdf_hash,
246
243
  "has_anonymized_text": bool(pdf.anonymized_text and pdf.anonymized_text.strip()),
247
- "is_validated": getattr(pdf.sensitive_meta, 'is_verified', False) if pdf.sensitive_meta else False,
248
- "stream_url": request.build_absolute_uri(f"/api/media/pdfs/{pdf.id}/stream/"),
244
+ "is_validated": getattr(pdf.sensitive_meta, "is_verified", False) if pdf.sensitive_meta else False,
245
+ "stream_url": request.build_absolute_uri(f"/api/media/pdfs/{pdf.pk}/stream/"),
249
246
  }
250
-
247
+
251
248
  # Determine status based on anonymization and validation
252
249
  if not pdf.anonymized_text or not pdf.anonymized_text.strip():
253
250
  pdf_item["status"] = "not_started"
@@ -255,42 +252,38 @@ class PdfMediaView(APIView):
255
252
  pdf_item["status"] = "validated"
256
253
  else:
257
254
  pdf_item["status"] = "done"
258
-
255
+
259
256
  results.append(pdf_item)
260
-
261
- return Response({
262
- "count": total_count,
263
- "next": self._get_next_url(request, offset, limit, total_count),
264
- "previous": self._get_previous_url(request, offset, limit),
265
- "results": results
266
- })
267
-
268
- except ValueError as e:
257
+
269
258
  return Response(
270
- {"error": f"Invalid query parameter: {str(e)}"},
271
- status=status.HTTP_400_BAD_REQUEST
259
+ {
260
+ "count": total_count,
261
+ "next": self._get_next_url(request, offset, limit, total_count),
262
+ "previous": self._get_previous_url(request, offset, limit),
263
+ "results": results,
264
+ }
272
265
  )
273
-
266
+
267
+ except ValueError as e:
268
+ return Response({"error": f"Invalid query parameter: {str(e)}"}, status=status.HTTP_400_BAD_REQUEST)
269
+
274
270
  except Exception as e:
275
271
  logger.error(f"Unexpected error in PDF list view: {str(e)}")
276
- return Response(
277
- {"error": "Failed to retrieve PDF list"},
278
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
279
- )
272
+ return Response({"error": "Failed to retrieve PDF list"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
280
273
 
281
274
  def _safe_get_file_size(self, file_field):
282
275
  """
283
276
  Safely get file size without causing errors if file doesn't exist.
284
-
277
+
285
278
  Args:
286
279
  file_field: Django FileField
287
-
280
+
288
281
  Returns:
289
282
  int: File size in bytes, or 0 if file doesn't exist
290
283
  """
291
284
  if not file_field or not file_field.name:
292
285
  return 0
293
-
286
+
294
287
  try:
295
288
  return file_field.size
296
289
  except (OSError, IOError, ValueError):
@@ -300,44 +293,36 @@ class PdfMediaView(APIView):
300
293
  def _apply_filters(self, queryset, query_params):
301
294
  """
302
295
  Apply status and other filters to PDF queryset.
303
-
296
+
304
297
  Args:
305
298
  queryset: Base queryset to filter
306
299
  query_params: Request query parameters
307
-
300
+
308
301
  Returns:
309
302
  QuerySet: Filtered queryset
310
303
  """
311
- status_filter = query_params.get('status', '').strip().lower()
312
-
304
+ status_filter = query_params.get("status", "").strip().lower()
305
+
313
306
  if status_filter:
314
- if status_filter == 'not_started':
307
+ if status_filter == "not_started":
315
308
  # PDFs without anonymized text
316
- queryset = queryset.filter(
317
- Q(anonymized_text__isnull=True) | Q(anonymized_text__exact='')
318
- )
319
- elif status_filter == 'done':
309
+ queryset = queryset.filter(Q(anonymized_text__isnull=True) | Q(anonymized_text__exact=""))
310
+ elif status_filter == "done":
320
311
  # PDFs with anonymized text but not validated
321
312
  queryset = queryset.filter(
322
- ~Q(anonymized_text__isnull=True),
323
- ~Q(anonymized_text__exact=''),
324
- Q(sensitive_meta__is_verified=False) | Q(sensitive_meta__isnull=True)
313
+ ~Q(anonymized_text__isnull=True), ~Q(anonymized_text__exact=""), Q(sensitive_meta__is_verified=False) | Q(sensitive_meta__isnull=True)
325
314
  )
326
- elif status_filter == 'validated':
315
+ elif status_filter == "validated":
327
316
  # PDFs with anonymized text and validated
328
- queryset = queryset.filter(
329
- ~Q(anonymized_text__isnull=True),
330
- ~Q(anonymized_text__exact=''),
331
- sensitive_meta__is_verified=True
332
- )
333
-
317
+ queryset = queryset.filter(~Q(anonymized_text__isnull=True), ~Q(anonymized_text__exact=""), sensitive_meta__is_verified=True)
318
+
334
319
  return queryset
335
320
 
336
321
  def _get_next_url(self, request, offset, limit, total_count):
337
322
  """Generate next page URL for pagination."""
338
323
  if offset + limit >= total_count:
339
324
  return None
340
-
325
+
341
326
  next_offset = offset + limit
342
327
  return self._build_paginated_url(request, next_offset, limit)
343
328
 
@@ -345,16 +330,16 @@ class PdfMediaView(APIView):
345
330
  """Generate previous page URL for pagination."""
346
331
  if offset <= 0:
347
332
  return None
348
-
333
+
349
334
  prev_offset = max(0, offset - limit)
350
335
  return self._build_paginated_url(request, prev_offset, limit)
351
336
 
352
337
  def _build_paginated_url(self, request, offset, limit):
353
338
  """Build URL with pagination parameters."""
354
339
  params = request.query_params.copy()
355
- params['offset'] = offset
356
- params['limit'] = limit
357
-
340
+ params["offset"] = offset
341
+ params["limit"] = limit
342
+
358
343
  base_url = request.build_absolute_uri(request.path)
359
344
  if params:
360
345
  return f"{base_url}?{params.urlencode()}"
@@ -364,25 +349,19 @@ class PdfMediaView(APIView):
364
349
  def patch(self, request, pk):
365
350
  """
366
351
  Update PDF metadata (Phase 1.2+ future enhancement).
367
-
352
+
368
353
  Currently returns 501 Not Implemented.
369
354
  """
370
- return Response(
371
- {"error": "PDF metadata updates not yet implemented"},
372
- status=status.HTTP_501_NOT_IMPLEMENTED
373
- )
355
+ return Response({"error": "PDF metadata updates not yet implemented"}, status=status.HTTP_501_NOT_IMPLEMENTED)
374
356
 
375
357
  def delete(self, request, pk):
376
358
  """
377
359
  Delete PDF file (Phase 1.2+ future enhancement).
378
-
360
+
379
361
  Currently returns 501 Not Implemented.
380
362
  Use /api/media-management/force-remove/{id}/ instead.
381
363
  """
382
364
  return Response(
383
- {
384
- "error": "PDF deletion not yet implemented",
385
- "alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead"
386
- },
387
- status=status.HTTP_501_NOT_IMPLEMENTED
365
+ {"error": "PDF deletion not yet implemented", "alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead"},
366
+ status=status.HTTP_501_NOT_IMPLEMENTED,
388
367
  )
@@ -1,4 +1,6 @@
1
1
  # Modern Media Framework: Sensitive Metadata Management
2
+ import string
3
+ from numpy import number
2
4
  from rest_framework.decorators import api_view, permission_classes
3
5
  from rest_framework.response import Response
4
6
  from rest_framework import status
@@ -14,6 +16,46 @@ from endoreg_db.serializers.meta import (
14
16
 
15
17
  # === VIDEO SENSITIVE METADATA ===
16
18
 
19
+ @api_view(['GET'])
20
+ @permission_classes([EnvironmentAwarePermission])
21
+ def get_sensitive_metadata_pk(request, pk: number, mediaType: str) -> Response | None:
22
+ """
23
+ A route to get the sensitive meta pk for a media type quickly.
24
+
25
+ GET api/media/sensitive-media-id/<pk>/<str:mediaType>
26
+
27
+ Args:
28
+ request (_type_): _description_
29
+ id (_type_): _description_
30
+
31
+ Returns:
32
+ Response | None: _description_
33
+ """
34
+
35
+ if mediaType == 'video':
36
+ video = get_object_or_404(VideoFile, pk=pk)
37
+ if not video.sensitive_meta:
38
+ return Response(
39
+ {"error": f"No sensitive metadata found for video {pk}"},
40
+ status=status.HTTP_404_NOT_FOUND
41
+ )
42
+ sm_id = video.sensitive_meta.pk
43
+ return Response({
44
+ "sm": sm_id
45
+ })
46
+ if mediaType == 'pdf':
47
+ pdf = get_object_or_404(RawPdfFile, pk=pk)
48
+ if not pdf.sensitive_meta:
49
+ return Response(
50
+ {"error": f"No sensitive metadata found for PDF {pk}"},
51
+ status=status.HTTP_404_NOT_FOUND
52
+ )
53
+ sm_id = pdf.sensitive_meta.pk
54
+ return Response({
55
+ "sm": sm_id
56
+ })
57
+
58
+
17
59
  @api_view(['GET', 'PATCH'])
18
60
  @permission_classes([EnvironmentAwarePermission])
19
61
  def video_sensitive_metadata(request, pk):
@@ -24,16 +66,15 @@ def video_sensitive_metadata(request, pk):
24
66
  Get or update sensitive metadata for a video.
25
67
  Video-scoped: Uses video ID to locate related sensitive metadata.
26
68
  """
27
- video = get_object_or_404(VideoFile, pk=pk)
69
+ sensitive_meta = get_object_or_404(SensitiveMeta, pk=pk)
28
70
 
29
71
  # Get related sensitive metadata
30
- if not video.sensitive_meta:
72
+ if not sensitive_meta:
31
73
  return Response(
32
74
  {"error": f"No sensitive metadata found for video {pk}"},
33
75
  status=status.HTTP_404_NOT_FOUND
34
76
  )
35
77
 
36
- sensitive_meta = video.sensitive_meta
37
78
 
38
79
  if request.method == 'GET':
39
80
  serializer = SensitiveMetaDetailSerializer(sensitive_meta)
@@ -123,16 +164,15 @@ def pdf_sensitive_metadata(request, pk):
123
164
  Get or update sensitive metadata for a PDF.
124
165
  PDF-scoped: Uses PDF ID to locate related sensitive metadata.
125
166
  """
126
- pdf = get_object_or_404(RawPdfFile, pk=pk)
167
+ sensitive_meta = get_object_or_404(SensitiveMeta, pk=pk)
127
168
 
128
169
  # Get related sensitive metadata
129
- if not pdf.sensitive_meta:
170
+ if not sensitive_meta:
130
171
  return Response(
131
172
  {"error": f"No sensitive metadata found for PDF {pk}"},
132
173
  status=status.HTTP_404_NOT_FOUND
133
174
  )
134
175
 
135
- sensitive_meta = pdf.sensitive_meta
136
176
 
137
177
  if request.method == 'GET':
138
178
  serializer = SensitiveMetaDetailSerializer(sensitive_meta)