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

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

Potentially problematic release.


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

Files changed (372) hide show
  1. endoreg_db/authz/auth.py +74 -0
  2. endoreg_db/authz/backends.py +168 -0
  3. endoreg_db/authz/management/commands/list_routes.py +18 -0
  4. endoreg_db/authz/middleware.py +83 -0
  5. endoreg_db/authz/permissions.py +127 -0
  6. endoreg_db/authz/policy.py +218 -0
  7. endoreg_db/authz/views_auth.py +66 -0
  8. endoreg_db/config/env.py +13 -8
  9. endoreg_db/data/__init__.py +8 -31
  10. endoreg_db/data/_examples/disease.yaml +55 -0
  11. endoreg_db/data/_examples/disease_classification.yaml +13 -0
  12. endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
  13. endoreg_db/data/_examples/event.yaml +64 -0
  14. endoreg_db/data/_examples/examination.yaml +72 -0
  15. endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
  16. endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
  17. endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
  18. endoreg_db/data/_examples/finding/complication.yaml +16 -0
  19. endoreg_db/data/_examples/finding/data.yaml +105 -0
  20. endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
  21. endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
  22. endoreg_db/data/_examples/finding/outcome.yaml +12 -0
  23. endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
  24. endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
  25. endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
  26. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
  27. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
  28. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
  29. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
  30. endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
  31. endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
  32. endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
  33. endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
  34. endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
  35. endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
  36. endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
  37. endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
  38. endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
  39. endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
  40. endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
  41. endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
  42. endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
  43. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
  44. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
  45. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
  46. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
  47. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
  48. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
  49. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
  50. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
  51. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
  52. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
  53. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
  54. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
  55. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
  56. endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
  57. endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
  58. endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
  59. endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
  60. endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
  61. endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
  62. endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
  63. endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
  64. endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
  65. endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
  66. endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
  67. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  68. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  69. endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
  70. endoreg_db/data/_examples/finding_type/data.yaml +43 -0
  71. endoreg_db/data/_examples/requirement/age.yaml +26 -0
  72. endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
  73. endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
  74. endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
  75. endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
  76. endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
  77. endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
  78. endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
  79. endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
  80. endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
  81. endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
  82. endoreg_db/data/_examples/requirement/gender.yaml +25 -0
  83. endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
  84. endoreg_db/data/_examples/requirement/medication.yaml +93 -0
  85. endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
  86. endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
  87. endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
  88. endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
  89. endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
  90. endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
  91. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
  92. endoreg_db/data/event_classification/data.yaml +4 -0
  93. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  94. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
  95. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
  96. endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
  97. endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
  98. endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
  99. endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
  100. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
  101. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  102. endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
  103. endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
  104. endoreg_db/data/requirement_set/_old_ +109 -0
  105. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  106. endoreg_db/data/setup_config.yaml +4 -4
  107. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  108. endoreg_db/exceptions.py +5 -2
  109. endoreg_db/helpers/data_loader.py +1 -1
  110. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  111. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  112. endoreg_db/management/commands/import_video.py +9 -10
  113. endoreg_db/management/commands/import_video_with_classification.py +1 -1
  114. endoreg_db/management/commands/init_default_ai_model.py +1 -1
  115. endoreg_db/management/commands/list_routes.py +18 -0
  116. endoreg_db/management/commands/load_ai_model_data.py +2 -1
  117. endoreg_db/management/commands/load_center_data.py +12 -12
  118. endoreg_db/management/commands/load_requirement_data.py +60 -31
  119. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  120. endoreg_db/management/commands/setup_endoreg_db.py +14 -10
  121. endoreg_db/management/commands/storage_management.py +271 -203
  122. endoreg_db/migrations/0001_initial.py +1799 -1300
  123. endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
  124. endoreg_db/migrations/_old/0001_initial.py +1857 -0
  125. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
  126. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
  127. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
  128. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
  129. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
  130. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
  131. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
  132. endoreg_db/models/__init__.py +78 -123
  133. endoreg_db/models/administration/__init__.py +21 -42
  134. endoreg_db/models/administration/ai/active_model.py +2 -2
  135. endoreg_db/models/administration/ai/ai_model.py +7 -6
  136. endoreg_db/models/administration/case/__init__.py +1 -15
  137. endoreg_db/models/administration/case/case.py +3 -3
  138. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  139. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  140. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  141. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  142. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  143. endoreg_db/models/administration/center/center.py +33 -19
  144. endoreg_db/models/administration/center/center_product.py +12 -9
  145. endoreg_db/models/administration/center/center_resource.py +25 -19
  146. endoreg_db/models/administration/center/center_shift.py +21 -17
  147. endoreg_db/models/administration/center/center_waste.py +16 -8
  148. endoreg_db/models/administration/person/__init__.py +2 -0
  149. endoreg_db/models/administration/person/employee/employee.py +10 -5
  150. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  151. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  152. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  153. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  154. endoreg_db/models/administration/person/patient/patient.py +103 -100
  155. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  156. endoreg_db/models/administration/person/person.py +4 -0
  157. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  158. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  159. endoreg_db/models/administration/product/product.py +20 -15
  160. endoreg_db/models/administration/product/product_material.py +17 -18
  161. endoreg_db/models/administration/product/product_weight.py +12 -8
  162. endoreg_db/models/administration/product/reference_product.py +23 -55
  163. endoreg_db/models/administration/qualification/qualification.py +7 -3
  164. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  165. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  166. endoreg_db/models/administration/shift/shift.py +16 -12
  167. endoreg_db/models/administration/shift/shift_type.py +23 -31
  168. endoreg_db/models/label/__init__.py +7 -8
  169. endoreg_db/models/label/annotation/image_classification.py +10 -9
  170. endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
  171. endoreg_db/models/label/label.py +15 -15
  172. endoreg_db/models/label/label_set.py +19 -6
  173. endoreg_db/models/label/label_type.py +1 -1
  174. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  175. endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
  176. endoreg_db/models/label/video_segmentation_label.py +4 -0
  177. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  178. endoreg_db/models/media/frame/frame.py +22 -22
  179. endoreg_db/models/media/pdf/raw_pdf.py +249 -177
  180. endoreg_db/models/media/pdf/report_file.py +25 -29
  181. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
  182. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  183. endoreg_db/models/media/video/__init__.py +1 -0
  184. endoreg_db/models/media/video/create_from_file.py +48 -56
  185. endoreg_db/models/media/video/pipe_1.py +30 -33
  186. endoreg_db/models/media/video/pipe_2.py +8 -9
  187. endoreg_db/models/media/video/video_file.py +359 -204
  188. endoreg_db/models/media/video/video_file_ai.py +288 -74
  189. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  190. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  191. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  192. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  193. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  194. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  195. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  196. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  197. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  198. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  199. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  200. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  201. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  202. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  203. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  204. endoreg_db/models/media/video/video_file_io.py +109 -62
  205. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  206. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  207. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  208. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  209. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  210. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  211. endoreg_db/models/media/video/video_file_segments.py +24 -17
  212. endoreg_db/models/media/video/video_metadata.py +19 -35
  213. endoreg_db/models/media/video/video_processing.py +96 -95
  214. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  215. endoreg_db/models/medical/disease.py +22 -16
  216. endoreg_db/models/medical/event.py +31 -18
  217. endoreg_db/models/medical/examination/__init__.py +13 -6
  218. endoreg_db/models/medical/examination/examination.py +17 -18
  219. endoreg_db/models/medical/examination/examination_indication.py +26 -25
  220. endoreg_db/models/medical/examination/examination_time.py +16 -6
  221. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  222. endoreg_db/models/medical/examination/examination_type.py +3 -4
  223. endoreg_db/models/medical/finding/finding.py +38 -39
  224. endoreg_db/models/medical/finding/finding_classification.py +37 -48
  225. endoreg_db/models/medical/finding/finding_intervention.py +27 -22
  226. endoreg_db/models/medical/finding/finding_type.py +13 -12
  227. endoreg_db/models/medical/hardware/endoscope.py +20 -26
  228. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  229. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  230. endoreg_db/models/medical/medication/medication.py +22 -10
  231. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  232. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  233. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  234. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  235. endoreg_db/models/medical/organ/__init__.py +15 -12
  236. endoreg_db/models/medical/patient/medication_examples.py +1 -5
  237. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  238. endoreg_db/models/medical/patient/patient_event.py +19 -22
  239. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  240. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  241. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  242. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  243. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  244. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  245. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  246. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  247. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  248. endoreg_db/models/medical/risk/risk.py +7 -6
  249. endoreg_db/models/medical/risk/risk_type.py +8 -5
  250. endoreg_db/models/metadata/model_meta.py +60 -29
  251. endoreg_db/models/metadata/model_meta_logic.py +139 -18
  252. endoreg_db/models/metadata/pdf_meta.py +19 -24
  253. endoreg_db/models/metadata/sensitive_meta.py +102 -85
  254. endoreg_db/models/metadata/sensitive_meta_logic.py +383 -43
  255. endoreg_db/models/metadata/video_meta.py +51 -31
  256. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  257. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  258. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  259. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  260. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  261. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  262. endoreg_db/models/other/emission/emission_factor.py +18 -8
  263. endoreg_db/models/other/gender.py +10 -5
  264. endoreg_db/models/other/information_source.py +25 -25
  265. endoreg_db/models/other/material.py +9 -5
  266. endoreg_db/models/other/resource.py +6 -4
  267. endoreg_db/models/other/tag.py +10 -5
  268. endoreg_db/models/other/transport_route.py +13 -8
  269. endoreg_db/models/other/unit.py +10 -6
  270. endoreg_db/models/other/waste.py +6 -5
  271. endoreg_db/models/requirement/requirement.py +580 -272
  272. endoreg_db/models/requirement/requirement_error.py +85 -0
  273. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  274. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  275. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  276. endoreg_db/models/requirement/requirement_operator.py +36 -33
  277. endoreg_db/models/requirement/requirement_set.py +74 -57
  278. endoreg_db/models/state/__init__.py +4 -4
  279. endoreg_db/models/state/abstract.py +2 -2
  280. endoreg_db/models/state/anonymization.py +12 -0
  281. endoreg_db/models/state/audit_ledger.py +46 -47
  282. endoreg_db/models/state/label_video_segment.py +9 -0
  283. endoreg_db/models/state/raw_pdf.py +40 -46
  284. endoreg_db/models/state/sensitive_meta.py +6 -2
  285. endoreg_db/models/state/video.py +58 -53
  286. endoreg_db/models/upload_job.py +32 -55
  287. endoreg_db/models/utils.py +1 -2
  288. endoreg_db/root_urls.py +21 -2
  289. endoreg_db/serializers/__init__.py +26 -57
  290. endoreg_db/serializers/anonymization.py +18 -10
  291. endoreg_db/serializers/meta/report_meta.py +1 -1
  292. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  293. endoreg_db/serializers/misc/__init__.py +1 -1
  294. endoreg_db/serializers/misc/file_overview.py +33 -91
  295. endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
  296. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  297. endoreg_db/serializers/video/segmentation.py +2 -1
  298. endoreg_db/serializers/video/video_processing_history.py +20 -5
  299. endoreg_db/serializers/video_examination.py +198 -0
  300. endoreg_db/services/anonymization.py +75 -73
  301. endoreg_db/services/lookup_service.py +256 -73
  302. endoreg_db/services/lookup_store.py +174 -30
  303. endoreg_db/services/pdf_import.py +711 -310
  304. endoreg_db/services/storage_aware_video_processor.py +140 -114
  305. endoreg_db/services/video_import.py +266 -117
  306. endoreg_db/urls/__init__.py +27 -27
  307. endoreg_db/urls/label_video_segments.py +2 -0
  308. endoreg_db/urls/media.py +108 -66
  309. endoreg_db/urls/root_urls.py +29 -0
  310. endoreg_db/utils/__init__.py +15 -5
  311. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  312. endoreg_db/utils/case_generator/__init__.py +3 -0
  313. endoreg_db/utils/dataloader.py +88 -16
  314. endoreg_db/utils/defaults/set_default_center.py +32 -0
  315. endoreg_db/utils/names.py +22 -16
  316. endoreg_db/utils/permissions.py +2 -1
  317. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  318. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
  319. endoreg_db/utils/setup_config.py +8 -5
  320. endoreg_db/utils/storage.py +115 -0
  321. endoreg_db/utils/validate_endo_roi.py +8 -2
  322. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  323. endoreg_db/views/__init__.py +5 -12
  324. endoreg_db/views/anonymization/media_management.py +198 -163
  325. endoreg_db/views/anonymization/overview.py +4 -1
  326. endoreg_db/views/anonymization/validate.py +174 -40
  327. endoreg_db/views/media/__init__.py +2 -0
  328. endoreg_db/views/media/pdf_media.py +131 -150
  329. endoreg_db/views/media/sensitive_metadata.py +46 -6
  330. endoreg_db/views/media/video_media.py +89 -82
  331. endoreg_db/views/media/video_segments.py +187 -260
  332. endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
  333. endoreg_db/views/patient/patient.py +5 -4
  334. endoreg_db/views/pdf/__init__.py +5 -8
  335. endoreg_db/views/pdf/pdf_stream.py +186 -0
  336. endoreg_db/views/pdf/pdf_stream_views.py +0 -127
  337. endoreg_db/views/pdf/reimport.py +86 -91
  338. endoreg_db/views/requirement/evaluate.py +188 -187
  339. endoreg_db/views/requirement/lookup.py +186 -288
  340. endoreg_db/views/requirement/requirement_utils.py +89 -0
  341. endoreg_db/views/video/__init__.py +0 -4
  342. endoreg_db/views/video/correction.py +2 -2
  343. endoreg_db/views/video/video_examination_viewset.py +202 -289
  344. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
  345. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +350 -255
  346. endoreg_db/models/administration/permissions/__init__.py +0 -44
  347. endoreg_db/models/media/video/refactor_plan.md +0 -0
  348. endoreg_db/models/media/video/video_file_frames.py +0 -0
  349. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  350. endoreg_db/models/rule/__init__.py +0 -13
  351. endoreg_db/models/rule/rule.py +0 -27
  352. endoreg_db/models/rule/rule_applicator.py +0 -224
  353. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  354. endoreg_db/models/rule/rule_type.py +0 -20
  355. endoreg_db/models/rule/ruleset.py +0 -17
  356. endoreg_db/serializers/video/video_metadata.py +0 -105
  357. endoreg_db/urls/report.py +0 -48
  358. endoreg_db/urls/video.py +0 -61
  359. endoreg_db/utils/case_generator/case_generator.py +0 -159
  360. endoreg_db/utils/case_generator/utils.py +0 -30
  361. endoreg_db/views/pdf/pdf_media.py +0 -239
  362. endoreg_db/views/report/__init__.py +0 -9
  363. endoreg_db/views/report/report_list.py +0 -112
  364. endoreg_db/views/report/report_with_secure_url.py +0 -28
  365. endoreg_db/views/report/start_examination.py +0 -7
  366. endoreg_db/views/video/video_media.py +0 -158
  367. endoreg_db/views.py +0 -0
  368. /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
  369. /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
  370. /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
  371. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
  372. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,159 +0,0 @@
1
- from endoreg_db.models import CaseTemplate, CaseTemplateRule, CaseTemplateRuleType
2
- from endoreg_db.case_generator.lab_sample_factory import LabSampleFactory
3
-
4
- DEFAULT_CASE_TEMPLATE_NAME = "pre_default_screening_colonoscopy"
5
-
6
- class CaseGenerator:
7
- """
8
- Provides methods to generate cases based on a template.
9
- """
10
-
11
- def __init__(self, template: CaseTemplate = None):
12
- """
13
- Initializes the CaseGenerator with a template.
14
-
15
- Args:
16
- template (CaseTemplate, optional): The template to use for case generation. Defaults to the predefined template.
17
- """
18
- self.template = template or CaseTemplate.objects.get(name=DEFAULT_CASE_TEMPLATE_NAME)
19
- self.lab_sample_factory = LabSampleFactory()
20
-
21
- # Define available rule types
22
- rule_type_names = [
23
- "create-object",
24
- "set-field-default",
25
- "set-field-by-distribution",
26
- "set-field-by-value",
27
- "set-field-single-choice",
28
- "set-field-multiple-choice",
29
- ]
30
- self.available_rule_types = CaseTemplateRuleType.objects.filter(name__in=rule_type_names)
31
-
32
- def _validate_rule_type(self, rule_type: CaseTemplateRuleType):
33
- """
34
- Validates if the rule type is supported.
35
-
36
- Args:
37
- rule_type (CaseTemplateRuleType): The rule type to validate.
38
-
39
- Raises:
40
- ValueError: If the rule type is not supported.
41
- """
42
- if rule_type not in self.available_rule_types:
43
- raise ValueError(f"Rule type {rule_type} is not supported.")
44
-
45
- def _apply_create_object(self, rule: CaseTemplateRule, parent=None):
46
- """
47
- Applies a create-object rule to generate a model instance.
48
-
49
- Args:
50
- rule (CaseTemplateRule): The rule to apply.
51
- parent (Optional[Model]): The parent object for the rule.
52
-
53
- Returns:
54
- Model: The created model instance.
55
- """
56
- target_model = rule.get_target_model()
57
- extra_params = rule.extra_parameters or {}
58
- create_method_info = extra_params.get("create_method", {})
59
-
60
- assert create_method_info, "Create method must be set for a create-object rule."
61
-
62
- create_method = getattr(target_model, create_method_info["name"])
63
- kwargs = create_method_info.get("kwargs", {})
64
-
65
- if parent:
66
- kwargs[rule.parent_field] = parent
67
-
68
- target_instance = create_method(**kwargs)
69
- target_instance.save()
70
-
71
- for action in extra_params.get("actions", []):
72
- action_method = getattr(target_instance, action["name"])
73
- action_kwargs = action.get("kwargs", {})
74
- action_method(**action_kwargs)
75
-
76
- for chained_rule in rule.chained_rules.all():
77
- self.apply_rule(chained_rule, parent=target_instance)
78
-
79
- return target_instance
80
-
81
- def _apply_set_field_by_distribution(self, rule: CaseTemplateRule, parent):
82
- """
83
- Applies a set-field-by-distribution rule.
84
-
85
- Args:
86
- rule (CaseTemplateRule): The rule to apply.
87
- parent (Model): The parent object for the rule.
88
-
89
- Returns:
90
- Model: The updated parent object.
91
- """
92
- assert parent, "Parent must be provided for set-field-by-distribution rules."
93
- assert rule.target_field, "Target field must be specified for the rule."
94
-
95
- distribution = rule.get_distribution()
96
- value = distribution.generate_value()
97
-
98
- setattr(parent, rule.target_field, value)
99
- parent.save()
100
- return parent
101
-
102
- def apply_rule(self, rule: CaseTemplateRule, parent=None):
103
- """
104
- Applies a rule based on its type to generate a case.
105
-
106
- Args:
107
- rule (CaseTemplateRule): The rule to apply.
108
- parent (Optional[Model]): The parent object for the rule.
109
-
110
- Returns:
111
- Model: The case by applying the rule.
112
- """
113
- self._validate_rule_type(rule.rule_type)
114
-
115
- if rule.rule_type.name == "create-object":
116
- return self._apply_create_object(rule, parent)
117
-
118
- if rule.rule_type.name == "set-field-by-distribution":
119
- return self._apply_set_field_by_distribution(rule, parent)
120
-
121
- raise ValueError(f"Unsupported rule type: {rule.rule_type.name}")
122
-
123
- def generate_case(self, case_template: CaseTemplate = None):
124
- """
125
- Generates a case based on the provided or default template.
126
-
127
- Args:
128
- case_template (CaseTemplate, optional): The template to use for case generation. Defaults to None.
129
-
130
- Returns:
131
- Tuple[Model, Model]: The generated patient and medication schedule.
132
- """
133
- case_template = case_template or CaseTemplate.objects.get(name=DEFAULT_CASE_TEMPLATE_NAME)
134
-
135
- create_patient_rule = case_template.get_create_patient_rule()
136
- patient = self.apply_rule(create_patient_rule)
137
-
138
- medication_schedule_rule = case_template.get_create_patient_medication_schedule_rule()
139
- medication_schedule = self.apply_rule(medication_schedule_rule, parent=patient)
140
-
141
- return patient, medication_schedule
142
-
143
- # if not create_new_patient:
144
- # raise NotImplementedError("Only new patients are supported at the moment.")
145
- # else:
146
- # # TODO Implement patient rules
147
- # patient_rules = None # all rules of type "create_patient"
148
- # patient = self.generate_patient(patient_rules)
149
-
150
- # # Generate case based on template
151
- # rules = self.template.get_rules()
152
- # chained_rules = set()
153
-
154
- # for rule in rules:
155
- # rule_chain = rule.get_all_downward_chained_rules()
156
- # chained_rules.add(rule)
157
- # chained_rules.update(rule_chain)
158
-
159
- # return chained_rules
@@ -1,30 +0,0 @@
1
- from endoreg_db.models import CaseTemplate
2
- from endoreg_db.case_generator.case_generator import CaseGenerator
3
-
4
- # TEMPLATE_NAME = "pre_endo-anticoagulation-af-low_risk"
5
- TEMPLATE_NAME = "pre_default_screening_colonoscopy"
6
-
7
- def fetch_template(template_name: str = DEFAULT_TEMPLATE_NAME) -> CaseTemplate:
8
- """
9
- Fetches a CaseTemplate by name.
10
-
11
- Args:
12
- template_name (str): The name of the template to fetch. Defaults to DEFAULT_TEMPLATE_NAME.
13
-
14
- Returns:
15
- CaseTemplate: The fetched CaseTemplate instance.
16
- """
17
- return CaseTemplate.objects.get(name=template_name)
18
-
19
- def initialize_case_generator(template_name: str = DEFAULT_TEMPLATE_NAME) -> CaseGenerator:
20
- """
21
- Initializes a CaseGenerator with the specified template.
22
-
23
- Args:
24
- template_name (str): The name of the template to use. Defaults to DEFAULT_TEMPLATE_NAME.
25
-
26
- Returns:
27
- CaseGenerator: An instance of CaseGenerator initialized with the template.
28
- """
29
- template = fetch_template(template_name)
30
- return CaseGenerator(template)
@@ -1,239 +0,0 @@
1
- from django.http import FileResponse, Http404, StreamingHttpResponse
2
- import mimetypes
3
- import os
4
- import logging
5
- import re
6
- from ...models import RawPdfFile
7
- from ...serializers._old.raw_pdf_meta_validation import PDFFileForMetaSerializer, SensitiveMetaUpdateSerializer
8
- from rest_framework.views import APIView
9
- from rest_framework.response import Response
10
- from rest_framework import status
11
- from ...models import SensitiveMeta
12
- from django.views.decorators.clickjacking import xframe_options_sameorigin
13
- from django.utils.decorators import method_decorator
14
- from django.db import transaction
15
- from django.urls import reverse
16
- from django.utils.encoding import iri_to_uri
17
- from endoreg_db.utils.paths import PDF_DIR, STORAGE_DIR
18
- from .pdf_stream_views import ClosingFileWrapper
19
-
20
- logger = logging.getLogger(__name__)
21
- _RANGE_RE = re.compile(r"bytes=(\d+)-(\d*)")
22
-
23
- class PDFMediaView(APIView):
24
- """
25
- Unified API for PDFs to support frontend flows:
26
- - Without `id`: returns next PDF metadata (including anonymized_text) and stream URLs
27
- - With `id`: streams the PDF (original by default; `?variant=anonymized` for anonymized)
28
- - Integrates with Media Management expectations (clean deletion after validation is handled elsewhere)
29
- """
30
-
31
- def get(self, request):
32
- """
33
- Handles both:
34
- - Fetching PDF metadata (if `id` is NOT provided)
35
- - Streaming the actual PDF file (if `id` is provided)
36
- """
37
- pdf_id = request.GET.get("id")
38
- last_id = request.GET.get("last_id")
39
-
40
- if pdf_id:
41
- return self.serve_pdf_file(pdf_id)
42
- else:
43
- return self.fetch_pdf_metadata(last_id)
44
-
45
- def fetch_pdf_metadata(self, last_id):
46
- """
47
- Fetches the first or next available PDF metadata and provides stream URLs.
48
- """
49
- pdf_entry = PDFFileForMetaSerializer.get_next_pdf(last_id)
50
- if pdf_entry is None:
51
- return Response({"error": "No more PDFs available."}, status=status.HTTP_404_NOT_FOUND)
52
-
53
- serialized_pdf = PDFFileForMetaSerializer(pdf_entry, context={'request': self.request})
54
-
55
- # Build stream URLs pointing to this unified endpoint
56
- try:
57
- media_url = reverse('pdf_media')
58
- except Exception:
59
- media_url = "/api/pdf/media/"
60
- stream_url = f"{media_url}?id={pdf_entry.id}"
61
- anon_stream_url = f"{media_url}?id={pdf_entry.id}&variant=anonymized"
62
-
63
- data = dict(serialized_pdf.data)
64
- data.update({
65
- 'stream_url': iri_to_uri(self.request.build_absolute_uri(stream_url)),
66
- 'anonymized_stream_url': iri_to_uri(self.request.build_absolute_uri(anon_stream_url)),
67
- 'pdf_id': pdf_entry.id,
68
- 'has_anonymized': bool(getattr(pdf_entry, 'anonymized_file', None) and getattr(pdf_entry.anonymized_file, 'name', None)),
69
- })
70
- return Response(data, status=status.HTTP_200_OK)
71
-
72
- @method_decorator(xframe_options_sameorigin)
73
- def serve_pdf_file(self, pdf_id):
74
- """
75
- Streams the actual PDF file (original or anonymized) with Range support.
76
- Query param `variant=anonymized` selects anonymized file; default is original.
77
- """
78
- variant = (self.request.GET.get('variant') or 'original').lower()
79
- range_header = self.request.headers.get("Range")
80
-
81
- try:
82
- pdf_entry = RawPdfFile.objects.get(id=pdf_id)
83
- except RawPdfFile.DoesNotExist:
84
- return Response({"error": "Invalid PDF ID."}, status=status.HTTP_400_BAD_REQUEST)
85
-
86
- # Choose file according to variant
87
- file_field = None
88
- if variant == 'anonymized' and getattr(pdf_entry, 'anonymized_file', None):
89
- file_field = pdf_entry.anonymized_file
90
- else:
91
- file_field = pdf_entry.file
92
-
93
- if not file_field:
94
- return Response({"error": "PDF file not found."}, status=status.HTTP_404_NOT_FOUND)
95
-
96
- # Resolve path and attempt self-heal for originals
97
- try:
98
- file_path = file_field.path
99
- except Exception:
100
- file_path = None
101
-
102
- if not file_path or not os.path.exists(file_path):
103
- if variant != 'anonymized':
104
- # Try to self-heal original reference to sensitive storage
105
- sensitive_path = os.path.join(str(PDF_DIR), "sensitive", f"{pdf_entry.pdf_hash}.pdf")
106
- if os.path.exists(sensitive_path):
107
- try:
108
- relative_name = os.path.relpath(sensitive_path, str(STORAGE_DIR))
109
- if getattr(pdf_entry.file, 'name', None) != relative_name:
110
- pdf_entry.file.name = relative_name
111
- pdf_entry.save(update_fields=['file'])
112
- logger.info("Self-healed PDF file reference for ID %s -> %s", pdf_entry.id, pdf_entry.file.path)
113
- file_path = sensitive_path
114
- except Exception as e:
115
- logger.error("Failed to self-heal file path for PDF %s: %s", pdf_entry.id, e)
116
- file_path = sensitive_path
117
- # If still missing (or anonymized missing), fail
118
- if not file_path or not os.path.exists(file_path):
119
- raise Http404("PDF file not found on server.")
120
-
121
- # Prepare headers
122
- safe_filename = os.path.basename(getattr(file_field, 'name', None) or f"document_{pdf_id}.pdf")
123
- if not safe_filename.endswith('.pdf'):
124
- safe_filename += '.pdf'
125
-
126
- file_size = os.path.getsize(file_path)
127
-
128
- # Range support
129
- if range_header:
130
- match = _RANGE_RE.match(range_header)
131
- if match:
132
- start = int(match.group(1))
133
- end = int(match.group(2) or file_size - 1)
134
-
135
- if start < 0 or start >= file_size:
136
- raise Http404("Invalid range")
137
- if end >= file_size:
138
- end = file_size - 1
139
-
140
- chunk_size = end - start + 1
141
- try:
142
- fh = open(file_path, 'rb')
143
- fh.seek(start)
144
- resp = StreamingHttpResponse(
145
- ClosingFileWrapper(fh, blksize=8192),
146
- status=206,
147
- content_type="application/pdf",
148
- )
149
- resp["Content-Length"] = str(chunk_size)
150
- resp["Content-Range"] = f"bytes {start}-{end}/{file_size}"
151
- resp["Accept-Ranges"] = "bytes"
152
- resp["Content-Disposition"] = f'inline; filename="{safe_filename}"'
153
- return resp
154
- except (OSError, IOError) as e:
155
- logger.error(f"Error opening PDF file for range request: {e}")
156
- raise Http404("Error accessing PDF file")
157
-
158
- # Fallback: serve full file
159
- mime_type, _ = mimetypes.guess_type(file_path)
160
- try:
161
- fh = open(file_path, 'rb')
162
- response = FileResponse(fh, content_type=mime_type or "application/pdf")
163
- response["Content-Length"] = str(file_size)
164
- response["Accept-Ranges"] = "bytes"
165
- response["Content-Disposition"] = f'inline; filename="{safe_filename}"'
166
- return response
167
- except (OSError, IOError) as e:
168
- logger.error(f"Error opening PDF file: {e}")
169
- raise Http404("Error accessing PDF file")
170
-
171
- class UpdateSensitiveMetaView(APIView):
172
- """
173
- API endpoint to update patient details in the SensitiveMeta table.
174
- Handles partial updates (only edited fields) and raw file deletion after validation acceptance.
175
- """
176
-
177
- @transaction.atomic
178
- def patch(self, request, *args, **kwargs):
179
- """
180
- Updates the provided fields for a specific patient record.
181
- Only updates fields that are sent in the request.
182
- Automatically deletes raw PDF files when validation is accepted.
183
- """
184
- sensitive_meta_id = request.data.get("sensitive_meta_id")
185
-
186
- if not sensitive_meta_id:
187
- return Response({"error": "sensitive_meta_id is required."}, status=status.HTTP_400_BAD_REQUEST)
188
-
189
- try:
190
- sensitive_meta = SensitiveMeta.objects.get(id=sensitive_meta_id)
191
- except SensitiveMeta.DoesNotExist:
192
- return Response({"error": "Patient record not found."}, status=status.HTTP_404_NOT_FOUND)
193
-
194
- is_accepting_validation = request.data.get("is_verified", False)
195
- delete_raw_files = request.data.get("delete_raw_files", False)
196
- if is_accepting_validation:
197
- delete_raw_files = True
198
- logger.info(f"Validation accepted for PDF SensitiveMeta {sensitive_meta_id}, marking raw files for deletion")
199
-
200
- serializer = SensitiveMetaUpdateSerializer(sensitive_meta, data=request.data, partial=True)
201
-
202
- if serializer.is_valid():
203
- updated_sm = serializer.save()
204
- if delete_raw_files and updated_sm.is_verified:
205
- try:
206
- pdf_file = RawPdfFile.objects.filter(sensitive_meta=updated_sm).first()
207
- if pdf_file:
208
- self._schedule_raw_file_deletion(pdf_file)
209
- logger.info(f"Scheduled raw file deletion for PDF {pdf_file.id}")
210
- else:
211
- logger.warning(f"No PDF file found for SensitiveMeta {sensitive_meta_id}")
212
- except Exception as e:
213
- logger.error(f"Error scheduling raw file deletion for PDF SensitiveMeta {sensitive_meta_id}: {e}")
214
- return Response({"message": "Patient information updated successfully.", "updated_data": serializer.data}, status=status.HTTP_200_OK)
215
-
216
- return Response({"error": "Invalid data.", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
217
-
218
- def _schedule_raw_file_deletion(self, pdf_file):
219
- """
220
- Schedule deletion of raw PDF file after validation acceptance.
221
- Deletes the original (sensitive) file but keeps anonymized_file for frontend.
222
- """
223
- try:
224
- def cleanup_raw_files():
225
- try:
226
- if pdf_file.file and getattr(pdf_file.file, 'path', None) and os.path.exists(pdf_file.file.path):
227
- logger.info(f"Deleting original (sensitive) PDF file: {pdf_file.file.path}")
228
- os.remove(pdf_file.file.path)
229
- pdf_file.file = None
230
- pdf_file.save(update_fields=['file'])
231
- logger.info(f"Successfully deleted original file for PDF {pdf_file.id}")
232
- else:
233
- logger.info(f"Original file already deleted or not found for PDF {pdf_file.id}")
234
- except Exception as e:
235
- logger.error(f"Error during raw file cleanup for PDF {pdf_file.id}: {e}")
236
- transaction.on_commit(cleanup_raw_files)
237
- except Exception as e:
238
- logger.error(f"Error scheduling raw file deletion for PDF {pdf_file.id}: {e}")
239
- raise
@@ -1,9 +0,0 @@
1
- from .report_list import ReportListView
2
- from .report_with_secure_url import ReportWithSecureUrlView
3
- from .start_examination import start_examination
4
-
5
- __all__ = [
6
- "ReportListView",
7
- "ReportWithSecureUrlView",
8
- "start_examination",
9
- ]
@@ -1,112 +0,0 @@
1
- from endoreg_db.models import RawPdfFile
2
- from endoreg_db.serializers.report.report_list import ReportListSerializer
3
-
4
- from django.core.paginator import Paginator
5
- from django.db.models import Q
6
- from rest_framework import status
7
- from rest_framework.response import Response
8
- from rest_framework.views import APIView
9
-
10
- import logging
11
- logger = logging.getLogger(__name__)
12
-
13
- class ReportListView(APIView):
14
- """
15
- API-Endpunkt für paginierte Report-Listen mit optionaler Filterung
16
- GET /api/reports/
17
- """
18
-
19
- def get(self, request):
20
- try:
21
- # Query-Parameter abrufen
22
- page = int(request.GET.get('page', 1))
23
- page_size = min(int(request.GET.get('page_size', 20)), 100) # Max 100 pro Seite
24
-
25
- # Filter-Parameter
26
- file_type_filter = request.GET.get('file_type')
27
- patient_name_filter = request.GET.get('patient_name')
28
- casenumber_filter = request.GET.get('casenumber')
29
- date_from = request.GET.get('date_from')
30
- date_to = request.GET.get('date_to')
31
-
32
- # Base QuerySet mit related data
33
- queryset = RawPdfFile.objects.select_related('sensitive_meta').all()
34
-
35
- # Filter anwenden
36
- if patient_name_filter:
37
- queryset = queryset.filter(
38
- Q(sensitive_meta__patient_first_name__icontains=patient_name_filter) |
39
- Q(sensitive_meta__patient_last_name__icontains=patient_name_filter)
40
- )
41
-
42
- if casenumber_filter:
43
- queryset = queryset.filter(
44
- sensitive_meta__case_number__icontains=casenumber_filter
45
- )
46
-
47
- if file_type_filter:
48
- # Filter basierend auf Dateiendung
49
- queryset = queryset.filter(file__endswith=f'.{file_type_filter}')
50
-
51
- if date_from:
52
- queryset = queryset.filter(created_at__gte=date_from)
53
-
54
- if date_to:
55
- queryset = queryset.filter(created_at__lte=date_to)
56
-
57
- # Sortierung (neueste zuerst)
58
- queryset = queryset.order_by('-created_at')
59
-
60
- # Paginierung
61
- paginator = Paginator(queryset, page_size)
62
-
63
- if page > paginator.num_pages:
64
- return Response({
65
- 'count': paginator.count,
66
- 'next': None,
67
- 'previous': None,
68
- 'results': []
69
- })
70
-
71
- page_obj = paginator.get_page(page)
72
-
73
- # Serialisierung
74
- serializer = ReportListSerializer(
75
- page_obj.object_list,
76
- many=True,
77
- context={'request': request}
78
- )
79
-
80
- # URLs für Paginierung
81
- next_url = None
82
- previous_url = None
83
-
84
- if page_obj.has_next():
85
- next_url = request.build_absolute_uri(
86
- f"?page={page_obj.next_page_number()}&page_size={page_size}"
87
- )
88
-
89
- if page_obj.has_previous():
90
- previous_url = request.build_absolute_uri(
91
- f"?page={page_obj.previous_page_number()}&page_size={page_size}"
92
- )
93
-
94
- return Response({
95
- 'count': paginator.count,
96
- 'next': next_url,
97
- 'previous': previous_url,
98
- 'results': serializer.data
99
- })
100
-
101
- except ValueError as e:
102
- logger.error("Ungültige Parameter in ReportListView: %s", str(e))
103
- return Response(
104
- {"error": "Ungültige Parameter"},
105
- status=status.HTTP_400_BAD_REQUEST
106
- )
107
- except (AttributeError, TypeError, OSError) as e:
108
- logger.error("Fehler in ReportListView: %s", str(e))
109
- return Response(
110
- {"error": "Fehler beim Laden der Reports"},
111
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
112
- )
@@ -1,28 +0,0 @@
1
- from endoreg_db.models import RawPdfFile
2
- from endoreg_db.serializers.report.report import ReportDataSerializer
3
-
4
- from django.shortcuts import get_object_or_404
5
- from rest_framework import status
6
- from rest_framework.response import Response
7
- from rest_framework.views import APIView
8
-
9
- import logging
10
- logger = logging.getLogger(__name__)
11
-
12
- class ReportWithSecureUrlView(APIView):
13
- """
14
- API-Endpunkt für Reports mit sicherer URL-Generierung
15
- GET /api/reports/{report_id}/with-secure-url/
16
- """
17
-
18
- def get(self, request, report_id):
19
- try:
20
- report = get_object_or_404(RawPdfFile, id=report_id)
21
- serializer = ReportDataSerializer(report, context={'request': request})
22
- return Response(serializer.data)
23
- except (ValueError, TypeError) as e:
24
- logger.error("Fehler beim Laden des Reports %s: %s", report_id, str(e))
25
- return Response(
26
- {"error": "Report konnte nicht geladen werden"},
27
- status=status.HTTP_500_INTERNAL_SERVER_ERROR
28
- )
@@ -1,7 +0,0 @@
1
- from django.shortcuts import render
2
- from django.contrib.admin.views.decorators import staff_member_required
3
-
4
- @staff_member_required
5
- def start_examination(request):
6
- return render(request, 'admin/start_examination.html')
7
-