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

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

Potentially problematic release.


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

Files changed (503) 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 +2 -11
  10. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +3 -3
  11. endoreg_db/data/event_classification/data.yaml +4 -0
  12. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  13. endoreg_db/data/examination/examinations/data.yaml +114 -14
  14. endoreg_db/data/examination/time-type/data.yaml +0 -3
  15. endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
  16. endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
  17. endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
  18. endoreg_db/data/finding/00_generic.yaml +35 -0
  19. endoreg_db/data/finding/00_generic_complication.yaml +9 -0
  20. endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
  21. endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
  22. endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
  23. endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
  24. endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
  25. endoreg_db/data/finding_classification/00_generic.yaml +44 -0
  26. endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
  27. endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
  28. endoreg_db/data/finding_classification/02_colonoscopy_baseline.yaml +83 -0
  29. endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
  30. endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
  31. endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
  32. endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
  33. endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
  34. endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
  35. endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
  36. endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
  37. endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
  38. endoreg_db/data/finding_classification_choice/{colonoscopy_not_complete_reason.yaml → 02_colonoscopy_generic.yaml} +1 -1
  39. endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
  40. endoreg_db/data/finding_classification_choice/{colonoscopy_location.yaml → 02_colonoscopy_location.yaml} +23 -4
  41. endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
  42. endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
  43. endoreg_db/data/finding_classification_choice/{colon_lesion_paris.yaml → 02_colonoscopy_polyp_morphology.yaml} +26 -8
  44. endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
  45. endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
  46. endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
  47. endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
  48. endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
  49. endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
  50. endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
  51. endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
  52. endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
  53. endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
  54. endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
  55. endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
  56. endoreg_db/data/finding_type/data.yaml +8 -12
  57. endoreg_db/data/requirement/01_patient_data.yaml +93 -0
  58. endoreg_db/data/requirement/old/colon_polyp_intervention.yaml +49 -0
  59. endoreg_db/data/requirement/old/coloreg_colon_polyp.yaml +49 -0
  60. endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
  61. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +29 -12
  62. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  63. endoreg_db/data/requirement_set/{endoscopy_bleeding_risk.yaml → 02_endoscopy_bleeding_risk.yaml} +0 -6
  64. endoreg_db/data/requirement_set/90_coloreg.yaml +190 -0
  65. endoreg_db/data/requirement_set/_old_ +109 -0
  66. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  67. endoreg_db/data/setup_config.yaml +4 -4
  68. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  69. endoreg_db/exceptions.py +4 -2
  70. endoreg_db/forms/examination_form.py +1 -1
  71. endoreg_db/helpers/data_loader.py +125 -53
  72. endoreg_db/helpers/default_objects.py +116 -81
  73. endoreg_db/import_files/__init__.py +27 -0
  74. endoreg_db/import_files/context/__init__.py +7 -0
  75. endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
  76. endoreg_db/import_files/context/ensure_center.py +17 -0
  77. endoreg_db/import_files/context/file_lock.py +66 -0
  78. endoreg_db/import_files/context/import_context.py +43 -0
  79. endoreg_db/import_files/context/validate_directories.py +56 -0
  80. endoreg_db/import_files/file_storage/__init__.py +15 -0
  81. endoreg_db/import_files/file_storage/create_report_file.py +76 -0
  82. endoreg_db/import_files/file_storage/create_video_file.py +75 -0
  83. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
  84. endoreg_db/import_files/file_storage/state_management.py +400 -0
  85. endoreg_db/import_files/file_storage/storage.py +36 -0
  86. endoreg_db/import_files/import_service.md +26 -0
  87. endoreg_db/import_files/processing/__init__.py +11 -0
  88. endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
  89. endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
  90. endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
  91. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
  92. endoreg_db/import_files/pseudonymization/fake.py +52 -0
  93. endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
  94. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
  95. endoreg_db/import_files/report_import_service.py +141 -0
  96. endoreg_db/import_files/video_import_service.py +150 -0
  97. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  98. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  99. endoreg_db/management/commands/import_report.py +130 -65
  100. endoreg_db/management/commands/import_video.py +9 -10
  101. endoreg_db/management/commands/import_video_with_classification.py +2 -2
  102. endoreg_db/management/commands/list_routes.py +18 -0
  103. endoreg_db/management/commands/load_ai_model_data.py +5 -5
  104. endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
  105. endoreg_db/management/commands/load_base_db_data.py +5 -134
  106. endoreg_db/management/commands/load_center_data.py +12 -12
  107. endoreg_db/management/commands/load_contraindication_data.py +14 -16
  108. endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
  109. endoreg_db/management/commands/load_disease_classification_data.py +15 -18
  110. endoreg_db/management/commands/load_disease_data.py +25 -28
  111. endoreg_db/management/commands/load_endoscope_data.py +20 -27
  112. endoreg_db/management/commands/load_event_data.py +14 -16
  113. endoreg_db/management/commands/load_examination_data.py +31 -44
  114. endoreg_db/management/commands/load_examination_indication_data.py +20 -21
  115. endoreg_db/management/commands/load_finding_data.py +52 -80
  116. endoreg_db/management/commands/load_information_source.py +21 -23
  117. endoreg_db/management/commands/load_lab_value_data.py +17 -26
  118. endoreg_db/management/commands/load_medication_data.py +13 -12
  119. endoreg_db/management/commands/load_organ_data.py +15 -19
  120. endoreg_db/management/commands/load_pdf_type_data.py +19 -18
  121. endoreg_db/management/commands/load_profession_data.py +14 -17
  122. endoreg_db/management/commands/load_qualification_data.py +20 -23
  123. endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
  124. endoreg_db/management/commands/load_requirement_data.py +62 -39
  125. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  126. endoreg_db/management/commands/load_risk_data.py +7 -6
  127. endoreg_db/management/commands/load_shift_data.py +20 -23
  128. endoreg_db/management/commands/load_tag_data.py +8 -11
  129. endoreg_db/management/commands/load_unit_data.py +17 -19
  130. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  131. endoreg_db/management/commands/start_filewatcher.py +46 -37
  132. endoreg_db/management/commands/storage_management.py +271 -203
  133. endoreg_db/management/commands/validate_video_files.py +1 -5
  134. endoreg_db/migrations/0001_initial.py +297 -250
  135. endoreg_db/models/__init__.py +78 -123
  136. endoreg_db/models/administration/__init__.py +21 -42
  137. endoreg_db/models/administration/ai/active_model.py +2 -2
  138. endoreg_db/models/administration/ai/ai_model.py +7 -6
  139. endoreg_db/models/administration/case/__init__.py +1 -15
  140. endoreg_db/models/administration/case/case.py +3 -3
  141. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  142. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  143. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  144. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  145. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  146. endoreg_db/models/administration/center/center.py +33 -19
  147. endoreg_db/models/administration/center/center_product.py +12 -9
  148. endoreg_db/models/administration/center/center_resource.py +25 -19
  149. endoreg_db/models/administration/center/center_shift.py +21 -17
  150. endoreg_db/models/administration/center/center_waste.py +16 -8
  151. endoreg_db/models/administration/person/__init__.py +2 -0
  152. endoreg_db/models/administration/person/employee/employee.py +10 -5
  153. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  154. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  155. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  156. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  157. endoreg_db/models/administration/person/patient/patient.py +129 -100
  158. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  159. endoreg_db/models/administration/person/person.py +4 -0
  160. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  161. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  162. endoreg_db/models/administration/product/product.py +20 -15
  163. endoreg_db/models/administration/product/product_material.py +17 -18
  164. endoreg_db/models/administration/product/product_weight.py +12 -8
  165. endoreg_db/models/administration/product/reference_product.py +23 -55
  166. endoreg_db/models/administration/qualification/qualification.py +7 -3
  167. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  168. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  169. endoreg_db/models/administration/shift/shift.py +16 -12
  170. endoreg_db/models/administration/shift/shift_type.py +23 -31
  171. endoreg_db/models/label/__init__.py +8 -9
  172. endoreg_db/models/label/annotation/image_classification.py +10 -9
  173. endoreg_db/models/label/annotation/video_segmentation_annotation.py +23 -28
  174. endoreg_db/models/label/label.py +15 -15
  175. endoreg_db/models/label/label_set.py +19 -6
  176. endoreg_db/models/label/label_type.py +1 -1
  177. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  178. endoreg_db/models/label/label_video_segment/label_video_segment.py +98 -102
  179. endoreg_db/models/label/video_segmentation_label.py +4 -0
  180. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  181. endoreg_db/models/media/frame/frame.py +22 -22
  182. endoreg_db/models/media/pdf/raw_pdf.py +194 -194
  183. endoreg_db/models/media/pdf/report_file.py +25 -29
  184. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +55 -47
  185. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  186. endoreg_db/models/media/processing_history/__init__.py +5 -0
  187. endoreg_db/models/media/processing_history/processing_history.py +96 -0
  188. endoreg_db/models/media/video/__init__.py +1 -0
  189. endoreg_db/models/media/video/create_from_file.py +139 -77
  190. endoreg_db/models/media/video/pipe_2.py +8 -9
  191. endoreg_db/models/media/video/video_file.py +174 -112
  192. endoreg_db/models/media/video/video_file_ai.py +288 -74
  193. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  194. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  195. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  196. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  197. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  198. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  199. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  200. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  201. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  202. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  203. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  204. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  205. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  206. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  207. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  208. endoreg_db/models/media/video/video_file_io.py +113 -61
  209. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  210. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  211. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  212. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  213. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  214. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  215. endoreg_db/models/media/video/video_file_segments.py +24 -17
  216. endoreg_db/models/media/video/video_metadata.py +19 -35
  217. endoreg_db/models/media/video/video_processing.py +96 -95
  218. endoreg_db/models/medical/contraindication/README.md +1 -0
  219. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  220. endoreg_db/models/medical/disease.py +22 -16
  221. endoreg_db/models/medical/event.py +31 -18
  222. endoreg_db/models/medical/examination/__init__.py +13 -6
  223. endoreg_db/models/medical/examination/examination.py +39 -20
  224. endoreg_db/models/medical/examination/examination_indication.py +30 -95
  225. endoreg_db/models/medical/examination/examination_time.py +23 -8
  226. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  227. endoreg_db/models/medical/examination/examination_type.py +3 -4
  228. endoreg_db/models/medical/finding/finding.py +32 -40
  229. endoreg_db/models/medical/finding/finding_classification.py +42 -72
  230. endoreg_db/models/medical/finding/finding_intervention.py +25 -22
  231. endoreg_db/models/medical/finding/finding_type.py +13 -12
  232. endoreg_db/models/medical/hardware/endoscope.py +26 -26
  233. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  234. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  235. endoreg_db/models/medical/medication/medication.py +22 -10
  236. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  237. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  238. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  239. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  240. endoreg_db/models/medical/organ/__init__.py +15 -12
  241. endoreg_db/models/medical/patient/medication_examples.py +6 -6
  242. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  243. endoreg_db/models/medical/patient/patient_event.py +19 -22
  244. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  245. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  246. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  247. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  248. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  249. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  250. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  251. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  252. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  253. endoreg_db/models/medical/risk/risk.py +7 -6
  254. endoreg_db/models/medical/risk/risk_type.py +8 -5
  255. endoreg_db/models/metadata/model_meta.py +60 -29
  256. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  257. endoreg_db/models/metadata/pdf_meta.py +31 -24
  258. endoreg_db/models/metadata/sensitive_meta.py +105 -85
  259. endoreg_db/models/metadata/sensitive_meta_logic.py +198 -103
  260. endoreg_db/models/metadata/video_meta.py +51 -31
  261. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  262. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  263. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  264. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  265. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  266. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  267. endoreg_db/models/other/emission/emission_factor.py +18 -8
  268. endoreg_db/models/other/gender.py +10 -5
  269. endoreg_db/models/other/information_source.py +50 -29
  270. endoreg_db/models/other/material.py +9 -5
  271. endoreg_db/models/other/resource.py +6 -4
  272. endoreg_db/models/other/tag.py +10 -5
  273. endoreg_db/models/other/transport_route.py +13 -8
  274. endoreg_db/models/other/unit.py +10 -6
  275. endoreg_db/models/other/waste.py +6 -5
  276. endoreg_db/models/report/report.py +6 -0
  277. endoreg_db/models/requirement/requirement.py +329 -361
  278. endoreg_db/models/requirement/requirement_error.py +85 -0
  279. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  280. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  281. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  282. endoreg_db/models/requirement/requirement_operator.py +103 -112
  283. endoreg_db/models/requirement/requirement_set.py +74 -57
  284. endoreg_db/models/state/__init__.py +4 -4
  285. endoreg_db/models/state/abstract.py +2 -2
  286. endoreg_db/models/state/anonymization.py +12 -0
  287. endoreg_db/models/state/audit_ledger.py +49 -51
  288. endoreg_db/models/state/label_video_segment.py +9 -0
  289. endoreg_db/models/state/raw_pdf.py +101 -68
  290. endoreg_db/models/state/sensitive_meta.py +6 -2
  291. endoreg_db/models/state/video.py +110 -90
  292. endoreg_db/models/upload_job.py +35 -34
  293. endoreg_db/models/utils.py +28 -25
  294. endoreg_db/queries/__init__.py +3 -1
  295. endoreg_db/root_urls.py +21 -2
  296. endoreg_db/schemas/examination_evaluation.py +1 -1
  297. endoreg_db/serializers/__init__.py +2 -10
  298. endoreg_db/serializers/anonymization.py +18 -10
  299. endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
  300. endoreg_db/serializers/meta/__init__.py +1 -6
  301. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  302. endoreg_db/serializers/misc/file_overview.py +11 -99
  303. endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
  304. endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
  305. endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
  306. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  307. endoreg_db/serializers/video/segmentation.py +2 -1
  308. endoreg_db/serializers/video/video_file_list.py +65 -34
  309. endoreg_db/serializers/video/video_processing_history.py +20 -5
  310. endoreg_db/services/__old/pdf_import.py +1487 -0
  311. endoreg_db/services/__old/video_import.py +1306 -0
  312. endoreg_db/services/anonymization.py +128 -89
  313. endoreg_db/services/lookup_service.py +65 -52
  314. endoreg_db/services/lookup_store.py +2 -2
  315. endoreg_db/services/pdf_import.py +0 -1382
  316. endoreg_db/services/report_import.py +10 -0
  317. endoreg_db/services/video_import.py +6 -1255
  318. endoreg_db/tasks/upload_tasks.py +79 -70
  319. endoreg_db/tasks/video_ingest.py +8 -4
  320. endoreg_db/urls/__init__.py +5 -32
  321. endoreg_db/urls/ai.py +32 -0
  322. endoreg_db/urls/media.py +121 -83
  323. endoreg_db/urls/root_urls.py +29 -0
  324. endoreg_db/utils/__init__.py +15 -5
  325. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  326. endoreg_db/utils/case_generator/__init__.py +3 -0
  327. endoreg_db/utils/dataloader.py +142 -40
  328. endoreg_db/utils/defaults/set_default_center.py +32 -0
  329. endoreg_db/utils/names.py +22 -16
  330. endoreg_db/utils/paths.py +110 -46
  331. endoreg_db/utils/permissions.py +2 -1
  332. endoreg_db/utils/pipelines/Readme.md +1 -1
  333. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  334. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +655 -0
  335. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
  336. endoreg_db/utils/setup_config.py +8 -5
  337. endoreg_db/utils/storage.py +115 -0
  338. endoreg_db/utils/validate_endo_roi.py +8 -2
  339. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  340. endoreg_db/views/__init__.py +85 -183
  341. endoreg_db/views/ai/__init__.py +8 -0
  342. endoreg_db/views/ai/label.py +155 -0
  343. endoreg_db/views/anonymization/media_management.py +202 -166
  344. endoreg_db/views/anonymization/overview.py +99 -67
  345. endoreg_db/views/anonymization/validate.py +182 -44
  346. endoreg_db/views/media/__init__.py +7 -20
  347. endoreg_db/views/media/pdf_media.py +197 -174
  348. endoreg_db/views/media/sensitive_metadata.py +193 -138
  349. endoreg_db/views/media/video_media.py +89 -82
  350. endoreg_db/views/meta/__init__.py +0 -8
  351. endoreg_db/views/misc/__init__.py +1 -7
  352. endoreg_db/views/misc/upload_views.py +94 -93
  353. endoreg_db/views/patient/patient.py +5 -4
  354. endoreg_db/views/report/__init__.py +5 -7
  355. endoreg_db/views/{pdf → report}/reimport.py +22 -22
  356. endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +46 -39
  357. endoreg_db/views/requirement/evaluate.py +188 -187
  358. endoreg_db/views/requirement/lookup.py +17 -3
  359. endoreg_db/views/requirement/lookup_store.py +22 -90
  360. endoreg_db/views/requirement/requirement_utils.py +89 -0
  361. endoreg_db/views/video/__init__.py +23 -24
  362. endoreg_db/views/video/correction.py +201 -172
  363. endoreg_db/views/video/reimport.py +1 -1
  364. endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +77 -40
  365. endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
  366. endoreg_db/views/video/video_stream.py +7 -8
  367. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +7 -3
  368. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +391 -413
  369. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
  370. endoreg_db/data/finding/anatomy_colon.yaml +0 -128
  371. endoreg_db/data/finding/colonoscopy.yaml +0 -40
  372. endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
  373. endoreg_db/data/finding/complication.yaml +0 -16
  374. endoreg_db/data/finding/data.yaml +0 -105
  375. endoreg_db/data/finding/examination_setting.yaml +0 -16
  376. endoreg_db/data/finding/medication_related.yaml +0 -18
  377. endoreg_db/data/finding/outcome.yaml +0 -12
  378. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +0 -95
  379. endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
  380. endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
  381. endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
  382. endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
  383. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -68
  384. endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
  385. endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -80
  386. endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
  387. endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
  388. endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
  389. endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
  390. endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
  391. endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
  392. endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
  393. endoreg_db/data/finding_classification/histology_colo.yaml +0 -51
  394. endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
  395. endoreg_db/data/finding_classification/medication_related.yaml +0 -23
  396. endoreg_db/data/finding_classification/visualized.yaml +0 -33
  397. endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
  398. endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
  399. endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
  400. endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
  401. endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
  402. endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
  403. endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
  404. endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
  405. endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
  406. endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
  407. endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
  408. endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
  409. endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
  410. endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
  411. endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
  412. endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
  413. endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
  414. endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
  415. endoreg_db/data/requirement/age.yaml +0 -26
  416. endoreg_db/data/requirement/gender.yaml +0 -25
  417. endoreg_db/management/commands/init_default_ai_model.py +0 -112
  418. endoreg_db/management/commands/reset_celery_schedule.py +0 -9
  419. endoreg_db/management/commands/validate_video.py +0 -204
  420. endoreg_db/migrations/0002_add_video_correction_models.py +0 -52
  421. endoreg_db/migrations/0003_add_center_display_name.py +0 -30
  422. endoreg_db/models/administration/permissions/__init__.py +0 -44
  423. endoreg_db/models/rule/__init__.py +0 -13
  424. endoreg_db/models/rule/rule.py +0 -27
  425. endoreg_db/models/rule/rule_applicator.py +0 -224
  426. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  427. endoreg_db/models/rule/rule_type.py +0 -20
  428. endoreg_db/models/rule/ruleset.py +0 -17
  429. endoreg_db/renames.yml +0 -8
  430. endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
  431. endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
  432. endoreg_db/serializers/_old/video.py +0 -71
  433. endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
  434. endoreg_db/serializers/meta/report_meta.py +0 -53
  435. endoreg_db/serializers/report/__init__.py +0 -9
  436. endoreg_db/serializers/report/mixins.py +0 -45
  437. endoreg_db/serializers/report/report.py +0 -105
  438. endoreg_db/serializers/report/report_list.py +0 -22
  439. endoreg_db/serializers/report/secure_file_url.py +0 -26
  440. endoreg_db/serializers/video/video_metadata.py +0 -105
  441. endoreg_db/services/requirements_object.py +0 -147
  442. endoreg_db/services/storage_aware_video_processor.py +0 -344
  443. endoreg_db/urls/files.py +0 -6
  444. endoreg_db/urls/label_video_segment_validate.py +0 -33
  445. endoreg_db/urls/label_video_segments.py +0 -46
  446. endoreg_db/urls/report.py +0 -48
  447. endoreg_db/urls/video.py +0 -61
  448. endoreg_db/utils/case_generator/case_generator.py +0 -159
  449. endoreg_db/utils/case_generator/utils.py +0 -30
  450. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +0 -368
  451. endoreg_db/views/label/__init__.py +0 -5
  452. endoreg_db/views/label/label.py +0 -15
  453. endoreg_db/views/label_video_segment/__init__.py +0 -16
  454. endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
  455. endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
  456. endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
  457. endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
  458. endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
  459. endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
  460. endoreg_db/views/label_video_segment/validate.py +0 -226
  461. endoreg_db/views/media/segments.py +0 -71
  462. endoreg_db/views/meta/available_files_list.py +0 -146
  463. endoreg_db/views/meta/report_meta.py +0 -53
  464. endoreg_db/views/meta/sensitive_meta_detail.py +0 -148
  465. endoreg_db/views/misc/secure_file_serving_view.py +0 -80
  466. endoreg_db/views/misc/secure_file_url_view.py +0 -84
  467. endoreg_db/views/misc/secure_url_validate.py +0 -79
  468. endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
  469. endoreg_db/views/patient_finding_location/__init__.py +0 -5
  470. endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
  471. endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
  472. endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
  473. endoreg_db/views/pdf/__init__.py +0 -8
  474. endoreg_db/views/report/report_list.py +0 -112
  475. endoreg_db/views/report/report_with_secure_url.py +0 -28
  476. endoreg_db/views/report/start_examination.py +0 -7
  477. endoreg_db/views/video/segmentation.py +0 -274
  478. endoreg_db/views/video/task_status.py +0 -49
  479. endoreg_db/views/video/timeline.py +0 -46
  480. endoreg_db/views/video/video_analyze.py +0 -52
  481. endoreg_db/views.py +0 -0
  482. /endoreg_db/data/requirement/{colonoscopy_baseline_austria.yaml → old/colonoscopy_baseline_austria.yaml} +0 -0
  483. /endoreg_db/data/requirement/{disease_cardiovascular.yaml → old/disease_cardiovascular.yaml} +0 -0
  484. /endoreg_db/data/requirement/{disease_classification_choice_cardiovascular.yaml → old/disease_classification_choice_cardiovascular.yaml} +0 -0
  485. /endoreg_db/data/requirement/{disease_hepatology.yaml → old/disease_hepatology.yaml} +0 -0
  486. /endoreg_db/data/requirement/{disease_misc.yaml → old/disease_misc.yaml} +0 -0
  487. /endoreg_db/data/requirement/{disease_renal.yaml → old/disease_renal.yaml} +0 -0
  488. /endoreg_db/data/requirement/{endoscopy_bleeding_risk.yaml → old/endoscopy_bleeding_risk.yaml} +0 -0
  489. /endoreg_db/data/requirement/{event_cardiology.yaml → old/event_cardiology.yaml} +0 -0
  490. /endoreg_db/data/requirement/{event_requirements.yaml → old/event_requirements.yaml} +0 -0
  491. /endoreg_db/data/requirement/{finding_colon_polyp.yaml → old/finding_colon_polyp.yaml} +0 -0
  492. /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
  493. /endoreg_db/data/requirement/{lab_value.yaml → old/lab_value.yaml} +0 -0
  494. /endoreg_db/data/requirement/{medication.yaml → old/medication.yaml} +0 -0
  495. /endoreg_db/data/requirement_operator/{age.yaml → _old/age.yaml} +0 -0
  496. /endoreg_db/data/requirement_operator/{lab_operators.yaml → _old/lab_operators.yaml} +0 -0
  497. /endoreg_db/data/requirement_operator/{model_operators.yaml → _old/model_operators.yaml} +0 -0
  498. /endoreg_db/{models/media/video/refactor_plan.md → import_files/pseudonymization/__init__.py} +0 -0
  499. /endoreg_db/{models/media/video/video_file_frames.py → import_files/pseudonymization/pseudonymize.py} +0 -0
  500. /endoreg_db/models/{metadata/frame_ocr_result.py → report/__init__.py} +0 -0
  501. /endoreg_db/{urls/sensitive_meta.py → models/report/images.py} +0 -0
  502. /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
  503. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
@@ -1,7 +1,7 @@
1
1
  """
2
- PDF Media Management View (Phase 1.2)
2
+ report Media Management View (Phase 1.2)
3
3
 
4
- Provides standardized REST API for PDF files including listing, detail retrieval,
4
+ Provides standardized REST API for report files including listing, detail retrieval,
5
5
  and streaming for the media management system.
6
6
 
7
7
  This is separate from the existing pdf.PDFMediaView which handles legacy workflows.
@@ -9,67 +9,71 @@ 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
 
24
27
 
25
28
  class PdfMediaView(APIView):
26
29
  """
27
- PDF Media Management API for CRUD operations on PDF files.
28
-
30
+ report Media Management API for CRUD operations on report files.
31
+
29
32
  Endpoints:
30
33
  - GET /api/media/pdfs/ - List all PDFs with filtering
31
- - GET /api/media/pdfs/{id}/ - Get PDF details
32
- - GET /api/media/pdfs/{id}/stream/ - Stream PDF file (same as detail for PDFs)
33
- - PATCH /api/media/pdfs/{id}/ - Update PDF metadata (future)
34
- - DELETE /api/media/pdfs/{id}/ - Delete PDF (future)
35
-
34
+ - GET /api/media/pdfs/{id}/ - Get report details
35
+ - GET /api/media/pdfs/{id}/stream/ - Stream report file (same as detail for PDFs)
36
+ - PATCH /api/media/pdfs/{id}/ - Update report metadata (future)
37
+ - DELETE /api/media/pdfs/{id}/ - Delete report (future)
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
- - PDF streaming functionality
52
+ - report streaming functionality
50
53
  - Filtering and search functionality
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
- Handle GET requests for PDF listing, detail retrieval, or streaming.
59
-
62
+ Handle GET requests for report listing, detail retrieval, or streaming.
63
+
60
64
  Args:
61
65
  request: HTTP request object
62
- pk: Optional PDF ID for detail view or streaming
63
-
66
+ pk: Optional report ID for detail view or streaming
67
+
64
68
  Returns:
65
- Response or FileResponse: JSON response with PDF data or PDF file stream
66
-
69
+ Response or FileResponse: JSON response with report data or report file stream
70
+
67
71
  Raises:
68
- Http404: If specific PDF not found
72
+ Http404: If specific report 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
@@ -80,174 +84,190 @@ class PdfMediaView(APIView):
80
84
 
81
85
  def _get_pdf_detail(self, pk):
82
86
  """
83
- Get detailed information for a specific PDF.
84
-
87
+ Get detailed information for a specific report.
88
+
85
89
  Args:
86
- pk: PDF primary key
87
-
90
+ pk: report primary key
91
+
88
92
  Returns:
89
- Response: JSON response with PDF details
90
-
93
+ Response: JSON response with report details
94
+
91
95
  Raises:
92
- Http404: If PDF not found
96
+ Http404: If report not found
93
97
  """
94
98
  try:
95
99
  # Validate pdf_id is numeric
96
100
  try:
97
101
  pdf_id_int = int(pk)
98
102
  except (ValueError, TypeError):
99
- raise Http404("Invalid PDF ID format")
100
-
101
- # Fetch PDF with related data
102
- pdf = RawPdfFile.objects.select_related('sensitive_meta').get(pk=pdf_id_int)
103
-
104
- # Build PDF details
103
+ raise Http404("Invalid report ID format")
104
+
105
+ # Fetch report with related data
106
+ pdf = RawPdfFile.objects.select_related("sensitive_meta").get(pk=pdf_id_int)
107
+
108
+ # Build report 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()
115
+ if getattr(pdf, "date_created", None)
116
+ else None,
111
117
  "anonymized_text": pdf.anonymized_text,
112
- "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/"),
118
+ "has_anonymized_text": bool(
119
+ pdf.anonymized_text and pdf.anonymized_text.strip()
120
+ ),
121
+ "is_validated": getattr(pdf.sensitive_meta, "is_verified", False)
122
+ if pdf.sensitive_meta
123
+ else False,
124
+ "stream_url": self.request.build_absolute_uri(
125
+ f"/api/media/pdfs/{pdf.pk}/stream/"
126
+ ),
115
127
  }
116
-
128
+
117
129
  # Add patient metadata if available
118
130
  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
-
131
+ pdf_data.update(
132
+ {
133
+ "patient_first_name": pdf.sensitive_meta.patient_first_name,
134
+ "patient_last_name": pdf.sensitive_meta.patient_last_name,
135
+ "patient_dob": pdf.sensitive_meta.patient_dob.strftime(
136
+ "%d.%m.%Y"
137
+ )
138
+ if pdf.sensitive_meta.patient_dob
139
+ else None,
140
+ "examination_date": pdf.sensitive_meta.examination_date.strftime(
141
+ "%d.%m.%Y"
142
+ )
143
+ if pdf.sensitive_meta.examination_date
144
+ else None,
145
+ }
146
+ )
147
+
126
148
  return Response(pdf_data)
127
-
149
+
128
150
  except RawPdfFile.DoesNotExist:
129
- raise Http404(f"PDF with ID {pk} not found")
130
-
151
+ raise Http404(f"report with ID {pk} not found")
152
+
131
153
  except Exception as e:
132
- logger.error(f"Unexpected error in PDF detail view for ID {pk}: {str(e)}")
154
+ logger.error(
155
+ f"Unexpected error in report detail view for ID {pk}: {str(e)}"
156
+ )
133
157
  return Response(
134
- {"error": "Failed to retrieve PDF details"},
135
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
158
+ {"error": "Failed to retrieve report details"},
159
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
136
160
  )
137
-
161
+
138
162
  @xframe_options_exempt
139
163
  def _stream_pdf(self, pk):
140
164
  """
141
- Stream PDF file content for viewing/download.
142
-
165
+ Stream report file content for viewing/download.
166
+
143
167
  Args:
144
- pk: PDF primary key
145
-
168
+ pk: report primary key
169
+
146
170
  Returns:
147
- FileResponse: PDF file stream
148
-
171
+ FileResponse: report file stream
172
+
149
173
  Raises:
150
- Http404: If PDF not found or file cannot be accessed
174
+ Http404: If report not found or file cannot be accessed
151
175
  """
152
176
  try:
153
177
  # Validate pdf_id is numeric
154
178
  try:
155
179
  pdf_id_int = int(pk)
156
180
  except (ValueError, TypeError):
157
- raise Http404("Invalid PDF ID format")
158
-
159
- # Fetch PDF
181
+ raise Http404("Invalid report ID format")
182
+
183
+ # Fetch report
160
184
  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
- raise Http404("PDF file not found")
165
-
166
- 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
185
+
186
+ file_field = pdf.file
187
+ file_path = file_field.path
188
+
189
+ if not file_field or not file_field.name:
190
+ raise Http404("report file not found")
191
+ if not file_exists(file_field):
192
+ raise Http404("report file does not exist in storage")
193
+
194
+ with open(file_path, "rb") as file_handle:
173
195
  response = FileResponse(
174
- open(file_path, 'rb'),
175
- content_type='application/pdf',
176
- as_attachment=False # View in browser, not download
196
+ file_handle,
197
+ content_type="application/pdf",
198
+ as_attachment=False,
177
199
  )
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)}")
192
- raise Http404("PDF file cannot be accessed")
193
-
200
+
201
+ filename = Path(file_field.name).name
202
+ response["Content-Disposition"] = f'inline; filename="{filename}"'
203
+
204
+ frontend_origin = os.environ.get("FRONTEND_ORIGIN", "http://localhost:8000")
205
+ response["Access-Control-Allow-Origin"] = frontend_origin
206
+ response["Access-Control-Allow-Credentials"] = "true"
207
+
208
+ return response
209
+
194
210
  except RawPdfFile.DoesNotExist:
195
- raise Http404(f"PDF with ID {pk} not found")
196
-
211
+ raise Http404(f"report with ID {pk} not found")
212
+
197
213
  except Exception as e:
198
- logger.error(f"Unexpected error in PDF streaming for ID {pk}: {str(e)}")
199
- raise Http404("PDF file cannot be streamed")
214
+ logger.error(f"Unexpected error in report streaming for ID {pk}: {str(e)}")
215
+ raise Http404("report file cannot be streamed")
200
216
 
201
217
  def _list_pdfs(self, request):
202
218
  """
203
219
  List PDFs with filtering, search, and pagination.
204
-
220
+
205
221
  Args:
206
222
  request: HTTP request with query parameters
207
-
223
+
208
224
  Returns:
209
- Response: JSON response with paginated PDF list
225
+ Response: JSON response with paginated report list
210
226
  """
211
227
  try:
212
228
  # Start with all PDFs
213
- queryset = RawPdfFile.objects.select_related('sensitive_meta').all()
214
-
229
+ queryset = RawPdfFile.objects.select_related("sensitive_meta").all()
230
+
215
231
  # Apply filters
216
232
  queryset = self._apply_filters(queryset, request.query_params)
217
-
233
+
218
234
  # Apply search
219
- search = request.query_params.get('search', '').strip()
235
+ search = request.query_params.get("search", "").strip()
220
236
  if search:
221
- queryset = queryset.filter(
222
- Q(file__icontains=search)
223
- )
224
-
237
+ queryset = queryset.filter(Q(file__icontains=search))
238
+
225
239
  # 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')
240
+ if hasattr(queryset.model, "date_created"):
241
+ queryset = queryset.order_by("-date_created")
228
242
  else:
229
- queryset = queryset.order_by('-id')
230
-
243
+ queryset = queryset.order_by("-pk")
244
+
231
245
  # Apply pagination
232
- limit = min(int(request.query_params.get('limit', 50)), 100)
233
- offset = int(request.query_params.get('offset', 0))
234
-
246
+ limit = min(int(request.query_params.get("limit", 50)), 100)
247
+ offset = int(request.query_params.get("offset", 0))
248
+
235
249
  total_count = queryset.count()
236
- pdfs = queryset[offset:offset + limit]
237
-
250
+ pdfs = queryset[offset : offset + limit]
251
+
238
252
  # Serialize PDFs manually (no dedicated serializer yet)
239
253
  results = []
240
254
  for pdf in pdfs:
241
255
  pdf_item = {
242
- "id": pdf.id,
243
- "filename": getattr(pdf.file, 'name', 'Unknown'),
256
+ "id": pdf.pk,
257
+ "filename": getattr(pdf.file, "name", "Unknown"),
244
258
  "file_size": self._safe_get_file_size(pdf.file),
245
259
  "pdf_hash": pdf.pdf_hash,
246
- "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/"),
260
+ "has_anonymized_text": bool(
261
+ pdf.anonymized_text and pdf.anonymized_text.strip()
262
+ ),
263
+ "is_validated": getattr(pdf.sensitive_meta, "is_verified", False)
264
+ if pdf.sensitive_meta
265
+ else False,
266
+ "stream_url": request.build_absolute_uri(
267
+ f"/api/media/pdfs/{pdf.pk}/stream/"
268
+ ),
249
269
  }
250
-
270
+
251
271
  # Determine status based on anonymization and validation
252
272
  if not pdf.anonymized_text or not pdf.anonymized_text.strip():
253
273
  pdf_item["status"] = "not_started"
@@ -255,42 +275,44 @@ class PdfMediaView(APIView):
255
275
  pdf_item["status"] = "validated"
256
276
  else:
257
277
  pdf_item["status"] = "done"
258
-
278
+
259
279
  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
-
280
+
281
+ return Response(
282
+ {
283
+ "count": total_count,
284
+ "next": self._get_next_url(request, offset, limit, total_count),
285
+ "previous": self._get_previous_url(request, offset, limit),
286
+ "results": results,
287
+ }
288
+ )
289
+
268
290
  except ValueError as e:
269
291
  return Response(
270
292
  {"error": f"Invalid query parameter: {str(e)}"},
271
- status=status.HTTP_400_BAD_REQUEST
293
+ status=status.HTTP_400_BAD_REQUEST,
272
294
  )
273
-
295
+
274
296
  except Exception as e:
275
- logger.error(f"Unexpected error in PDF list view: {str(e)}")
297
+ logger.error(f"Unexpected error in report list view: {str(e)}")
276
298
  return Response(
277
- {"error": "Failed to retrieve PDF list"},
278
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
299
+ {"error": "Failed to retrieve report list"},
300
+ status=status.HTTP_500_INTERNAL_SERVER_ERROR,
279
301
  )
280
302
 
281
303
  def _safe_get_file_size(self, file_field):
282
304
  """
283
305
  Safely get file size without causing errors if file doesn't exist.
284
-
306
+
285
307
  Args:
286
308
  file_field: Django FileField
287
-
309
+
288
310
  Returns:
289
311
  int: File size in bytes, or 0 if file doesn't exist
290
312
  """
291
313
  if not file_field or not file_field.name:
292
314
  return 0
293
-
315
+
294
316
  try:
295
317
  return file_field.size
296
318
  except (OSError, IOError, ValueError):
@@ -299,45 +321,46 @@ class PdfMediaView(APIView):
299
321
 
300
322
  def _apply_filters(self, queryset, query_params):
301
323
  """
302
- Apply status and other filters to PDF queryset.
303
-
324
+ Apply status and other filters to report queryset.
325
+
304
326
  Args:
305
327
  queryset: Base queryset to filter
306
328
  query_params: Request query parameters
307
-
329
+
308
330
  Returns:
309
331
  QuerySet: Filtered queryset
310
332
  """
311
- status_filter = query_params.get('status', '').strip().lower()
312
-
333
+ status_filter = query_params.get("status", "").strip().lower()
334
+
313
335
  if status_filter:
314
- if status_filter == 'not_started':
336
+ if status_filter == "not_started":
315
337
  # PDFs without anonymized text
316
338
  queryset = queryset.filter(
317
- Q(anonymized_text__isnull=True) | Q(anonymized_text__exact='')
339
+ Q(anonymized_text__isnull=True) | Q(anonymized_text__exact="")
318
340
  )
319
- elif status_filter == 'done':
341
+ elif status_filter == "done":
320
342
  # PDFs with anonymized text but not validated
321
343
  queryset = queryset.filter(
322
344
  ~Q(anonymized_text__isnull=True),
323
- ~Q(anonymized_text__exact=''),
324
- Q(sensitive_meta__is_verified=False) | Q(sensitive_meta__isnull=True)
345
+ ~Q(anonymized_text__exact=""),
346
+ Q(sensitive_meta__is_verified=False)
347
+ | Q(sensitive_meta__isnull=True),
325
348
  )
326
- elif status_filter == 'validated':
349
+ elif status_filter == "validated":
327
350
  # PDFs with anonymized text and validated
328
351
  queryset = queryset.filter(
329
352
  ~Q(anonymized_text__isnull=True),
330
- ~Q(anonymized_text__exact=''),
331
- sensitive_meta__is_verified=True
353
+ ~Q(anonymized_text__exact=""),
354
+ sensitive_meta__is_verified=True,
332
355
  )
333
-
356
+
334
357
  return queryset
335
358
 
336
359
  def _get_next_url(self, request, offset, limit, total_count):
337
360
  """Generate next page URL for pagination."""
338
361
  if offset + limit >= total_count:
339
362
  return None
340
-
363
+
341
364
  next_offset = offset + limit
342
365
  return self._build_paginated_url(request, next_offset, limit)
343
366
 
@@ -345,16 +368,16 @@ class PdfMediaView(APIView):
345
368
  """Generate previous page URL for pagination."""
346
369
  if offset <= 0:
347
370
  return None
348
-
371
+
349
372
  prev_offset = max(0, offset - limit)
350
373
  return self._build_paginated_url(request, prev_offset, limit)
351
374
 
352
375
  def _build_paginated_url(self, request, offset, limit):
353
376
  """Build URL with pagination parameters."""
354
377
  params = request.query_params.copy()
355
- params['offset'] = offset
356
- params['limit'] = limit
357
-
378
+ params["offset"] = offset
379
+ params["limit"] = limit
380
+
358
381
  base_url = request.build_absolute_uri(request.path)
359
382
  if params:
360
383
  return f"{base_url}?{params.urlencode()}"
@@ -363,26 +386,26 @@ class PdfMediaView(APIView):
363
386
  # Future implementation placeholders
364
387
  def patch(self, request, pk):
365
388
  """
366
- Update PDF metadata (Phase 1.2+ future enhancement).
367
-
389
+ Update report metadata (Phase 1.2+ future enhancement).
390
+
368
391
  Currently returns 501 Not Implemented.
369
392
  """
370
393
  return Response(
371
- {"error": "PDF metadata updates not yet implemented"},
372
- status=status.HTTP_501_NOT_IMPLEMENTED
394
+ {"error": "report metadata updates not yet implemented"},
395
+ status=status.HTTP_501_NOT_IMPLEMENTED,
373
396
  )
374
397
 
375
398
  def delete(self, request, pk):
376
399
  """
377
- Delete PDF file (Phase 1.2+ future enhancement).
378
-
400
+ Delete report file (Phase 1.2+ future enhancement).
401
+
379
402
  Currently returns 501 Not Implemented.
380
403
  Use /api/media-management/force-remove/{id}/ instead.
381
404
  """
382
405
  return Response(
383
406
  {
384
- "error": "PDF deletion not yet implemented",
385
- "alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead"
407
+ "error": "report deletion not yet implemented",
408
+ "alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead",
386
409
  },
387
- status=status.HTTP_501_NOT_IMPLEMENTED
410
+ status=status.HTTP_501_NOT_IMPLEMENTED,
388
411
  )