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
@@ -3,7 +3,10 @@ import os
3
3
  import re
4
4
 
5
5
  from django.http import FileResponse, Http404, StreamingHttpResponse
6
- from django.views.decorators.clickjacking import xframe_options_sameorigin
6
+ from django.views.decorators.clickjacking import (
7
+ xframe_options_exempt,
8
+ xframe_options_sameorigin,
9
+ )
7
10
  from rest_framework.views import APIView
8
11
 
9
12
  from endoreg_db.models import RawPdfFile
@@ -36,34 +39,34 @@ class ClosingFileWrapper:
36
39
  self.file_handle.close()
37
40
 
38
41
 
39
- class PdfStreamView(APIView):
42
+ class ReportStreamView(APIView):
40
43
  """
41
- Streams a PDF file with correct HTTP range support and proper file handle management.
44
+ Streams a report file with correct HTTP range support and proper file handle management.
42
45
 
43
- Supports streaming both raw (original) and anonymized PDF files.
46
+ Supports streaming both raw (original) and processed report files.
44
47
 
45
48
  Query Parameters:
46
- type: 'raw' (default) or 'anonymized' - Selects which PDF file to stream
49
+ type: 'raw' (default) or 'processed' - Selects which report file to stream
47
50
 
48
51
  Examples:
49
- GET /api/media/pdf/1/?type=raw - Stream original raw PDF
50
- GET /api/media/pdf/1/?type=anonymized - Stream anonymized PDF
52
+ GET /api/media/pdf/1/?type=raw - Stream original raw report
53
+ GET /api/media/pdf/1/?type=processed - Stream processed report
51
54
  """
52
55
 
53
56
  permission_classes = [EnvironmentAwarePermission]
54
57
 
55
- @xframe_options_sameorigin
56
- def get(self, request, pdf_id: int, *args, **kwargs):
58
+ @xframe_options_exempt
59
+ def get(self, request, pk: int, *args, **kwargs):
57
60
  file_type = "raw" # Initialize for error logging
58
61
  try:
59
- pdf_obj = RawPdfFile.objects.filter(pk=pdf_id).first()
62
+ pdf_obj = RawPdfFile.objects.filter(pk=pk).first()
60
63
  if not pdf_obj:
61
- logger.warning(f"PDF not found: ID {pdf_id}")
62
- raise Http404("PDF not found")
64
+ logger.warning(f"report not found: ID {pk}")
65
+ raise Http404("report not found")
63
66
 
64
67
  # Parse query parameters to determine which file to stream
65
68
  file_type = request.query_params.get("type", "raw").lower()
66
- if file_type not in ["raw", "anonymized"]:
69
+ if file_type not in ["raw", "processed"]:
67
70
  logger.warning(f"Invalid file_type '{file_type}', defaulting to 'raw'")
68
71
  file_type = "raw"
69
72
 
@@ -71,43 +74,45 @@ class PdfStreamView(APIView):
71
74
  if file_type == "raw":
72
75
  file_field = pdf_obj.file
73
76
  if not file_field:
74
- logger.warning(f"No raw PDF file available for PDF ID {pdf_id}")
75
- raise Http404("Raw PDF file not available")
77
+ logger.warning(f"No raw report file available for report ID {pk}")
78
+ raise Http404("Raw report file not available")
76
79
  else: # anonymized
77
- file_field = pdf_obj.anonymized_file
80
+ file_field = pdf_obj.processed_file
78
81
  if not file_field:
79
82
  logger.warning(
80
- f"No anonymized PDF file available for PDF ID {pdf_id}"
83
+ f"No processed report file available for report ID {pk}"
81
84
  )
82
- raise Http404("Anonymized PDF file not available")
85
+ raise Http404("Processed report file not available")
83
86
 
84
87
  # Check if file exists on filesystem
85
88
  try:
86
89
  file_path = file_field.path
87
90
  if not os.path.exists(file_path):
88
- logger.error(f"PDF file does not exist on filesystem: {file_path}")
91
+ logger.error(
92
+ f"report file does not exist on filesystem: {file_path}"
93
+ )
89
94
  raise Http404(
90
- f"{file_type.capitalize()} PDF file not found on filesystem"
95
+ f"{file_type.capitalize()} report file not found on filesystem"
91
96
  )
92
97
 
93
98
  file_size = os.path.getsize(file_path)
94
99
  except (OSError, IOError, AttributeError) as e:
95
- logger.error(f"Error accessing {file_type} PDF file {pdf_id}: {e}")
96
- raise Http404(f"{file_type.capitalize()} PDF file not accessible")
100
+ logger.error(f"Error accessing {file_type} report file {pk}: {e}")
101
+ raise Http404(f"{file_type.capitalize()} report file not accessible")
97
102
 
98
103
  # Generate safe filename
99
104
  base_filename = (
100
105
  os.path.basename(file_field.name)
101
106
  if file_field.name
102
- else f"document_{pdf_id}.pdf"
107
+ else f"document_{pk}.pdf"
103
108
  )
104
109
  if not base_filename.endswith(".pdf"):
105
110
  base_filename += ".pdf"
106
111
 
107
112
  # Add type indicator to filename for clarity
108
- if file_type == "anonymized":
113
+ if file_type == "processed":
109
114
  name_parts = base_filename.rsplit(".", 1)
110
- safe_filename = f"{name_parts[0]}_anonymized.{name_parts[1]}"
115
+ safe_filename = f"{name_parts[0]}_processed.{name_parts[1]}"
111
116
  else:
112
117
  safe_filename = base_filename
113
118
 
@@ -115,7 +120,7 @@ class PdfStreamView(APIView):
115
120
  range_header = request.headers.get("Range")
116
121
  if range_header:
117
122
  logger.debug(
118
- f"Range request for {file_type} PDF {pdf_id}: {range_header}"
123
+ f"Range request for {file_type} report {pk}: {range_header}"
119
124
  )
120
125
  match = _RANGE_RE.match(range_header)
121
126
  if match:
@@ -139,7 +144,7 @@ class PdfStreamView(APIView):
139
144
  file_handle.seek(start)
140
145
 
141
146
  logger.debug(
142
- f"Serving {file_type} PDF {pdf_id} range {start}-{end}/{file_size}"
147
+ f"Serving {file_type} report {pk} range {start}-{end}/{file_size}"
143
148
  )
144
149
 
145
150
  response = StreamingHttpResponse(
@@ -157,31 +162,33 @@ class PdfStreamView(APIView):
157
162
  return response
158
163
  except (OSError, IOError) as e:
159
164
  logger.error(
160
- f"Error opening {file_type} PDF file for range request: {e}"
165
+ f"Error opening {file_type} report file for range request: {e}"
161
166
  )
162
- raise Http404(f"Error accessing {file_type} PDF file")
167
+ raise Http404(f"Error accessing {file_type} report file")
163
168
  else:
164
169
  logger.warning(f"Invalid Range header format: {range_header}")
165
170
 
166
171
  # Serve entire file using FileResponse (automatically handles file closing)
167
- logger.debug(f"Serving full {file_type} PDF {pdf_id} ({file_size} bytes)")
172
+ logger.debug(f"Serving full {file_type} report {pk} ({file_size} bytes)")
168
173
 
169
174
  try:
170
- file_handle = open(file_path, "rb")
171
- response = FileResponse(file_handle, content_type="application/pdf")
172
- response["Content-Length"] = str(file_size)
173
- response["Accept-Ranges"] = "bytes"
174
- response["Content-Disposition"] = f'inline; filename="{safe_filename}"'
175
+ with open(file_path, "rb") as file_handle:
176
+ response = FileResponse(file_handle, content_type="application/pdf")
177
+ response["Content-Length"] = str(file_size)
178
+ response["Accept-Ranges"] = "bytes"
179
+ response["Content-Disposition"] = (
180
+ f'inline; filename="{safe_filename}"'
181
+ )
175
182
 
176
183
  # FileResponse will take ownership of file_handle and close it after response
177
184
  return response
178
185
  except (OSError, IOError) as e:
179
- logger.error(f"Error opening {file_type} PDF file: {e}")
180
- raise Http404(f"Error accessing {file_type} PDF file")
186
+ logger.error(f"Error opening {file_type} report file: {e}")
187
+ raise Http404(f"Error accessing {file_type} report file")
181
188
 
182
189
  except Exception as e:
183
190
  logger.error(
184
- f"Unexpected error streaming {file_type if 'file_type' in locals() else 'PDF'} {pdf_id}: {e}",
191
+ f"Unexpected error streaming {file_type if 'file_type' in locals() else 'report'} {pk}: {e}",
185
192
  exc_info=True,
186
193
  )
187
- raise Http404("Error streaming PDF")
194
+ raise Http404("Error streaming report")
@@ -1,52 +1,31 @@
1
1
  from endoreg_db.models.requirement.requirement import Requirement
2
+ from endoreg_db.views.requirement.requirement_utils import safe_evaluate_requirement
2
3
  from endoreg_db.models.requirement.requirement_set import RequirementSet
4
+ from endoreg_db.models.requirement.requirement_evaluation.evaluate_with_dependencies import (
5
+ evaluate_requirement_sets_with_dependencies,
6
+ RequirementStatus, # if you export it there, otherwise re-declare in view
7
+ )
3
8
  from endoreg_db.models.medical.patient.patient_examination import PatientExamination
4
-
5
-
6
9
  from rest_framework import status
7
10
  from rest_framework.decorators import api_view
8
11
  from rest_framework.response import Response
12
+ import json
9
13
  import logging
10
14
 
11
15
  logger = logging.getLogger(__name__)
12
- @api_view(['POST'])
16
+
17
+
18
+ @api_view(["POST"])
13
19
  def evaluate_requirements(request):
14
20
  """
15
- Evaluate requirements and always return 200 with structured results.
16
- Payload:
17
- {
18
- "requirement_set_ids": [<int>, ...], // optional; evaluates all if omitted
19
- "patient_examination_id": <int> // required
20
- }
21
-
22
- Response (HTTP 200 always):
23
- {
24
- "ok": <bool>, // false if any errors occurred
25
- "errors": [<str>, ...], // high-level problems (e.g. invalid input, missing PE)
26
- "meta": {
27
- "patientExaminationId": <int|null>,
28
- "setsEvaluated": <int>,
29
- "requirementsEvaluated": <int>,
30
- "status": "ok" | "partial" | "failed"
31
- },
32
- "results": [
33
- {
34
- "requirement_set_id": <int>,
35
- "requirement_set_name": <str>,
36
- "requirement_name": <str>,
37
- "met": <bool>,
38
- "details": <str>, // always a string for UI display
39
- "error": <str|null> // per-item error if evaluation failed
40
- }
41
- ]
42
- }
21
+ Evaluate requirements (all selected sets) and always return 200 with structured results.
43
22
  """
44
23
  payload = request.data or {}
45
24
  req_set_ids = payload.get("requirement_set_ids")
46
25
  pe_id = payload.get("patient_examination_id")
47
26
 
48
- results = []
49
- errors = []
27
+ results: list[dict] = []
28
+ errors: list[str] = []
50
29
  sets_evaluated = 0
51
30
  requirements_evaluated = 0
52
31
 
@@ -55,53 +34,63 @@ def evaluate_requirements(request):
55
34
  msg = "patient_examination_id is required"
56
35
  errors.append(msg)
57
36
  logger.warning("evaluate_requirements: %s; payload=%s", msg, payload)
58
- return Response({
59
- "ok": False,
60
- "errors": errors,
61
- "meta": {
62
- "patientExaminationId": None,
63
- "setsEvaluated": 0,
64
- "requirementsEvaluated": 0,
65
- "status": "failed",
37
+ return Response(
38
+ {
39
+ "ok": False,
40
+ "errors": errors,
41
+ "meta": {
42
+ "patientExaminationId": None,
43
+ "setsEvaluated": 0,
44
+ "requirementsEvaluated": 0,
45
+ "status": "failed",
46
+ },
47
+ "results": [],
66
48
  },
67
- "results": []
68
- }, status=status.HTTP_200_OK)
49
+ status=status.HTTP_200_OK,
50
+ )
69
51
 
70
52
  # ---- fetch PatientExamination
71
53
  try:
72
- pe = (PatientExamination.objects
73
- .select_related("patient")
74
- .get(id=pe_id))
54
+ pe = (
55
+ PatientExamination.objects.select_related("patient")
56
+ .get(id=pe_id)
57
+ )
75
58
  except PatientExamination.DoesNotExist:
76
59
  msg = f"PatientExamination with id {pe_id} does not exist"
77
60
  errors.append(msg)
78
61
  logger.warning("evaluate_requirements: %s", msg)
79
- return Response({
80
- "ok": False,
81
- "errors": errors,
82
- "meta": {
83
- "patientExaminationId": pe_id,
84
- "setsEvaluated": 0,
85
- "requirementsEvaluated": 0,
86
- "status": "failed",
62
+ return Response(
63
+ {
64
+ "ok": False,
65
+ "errors": errors,
66
+ "meta": {
67
+ "patientExaminationId": pe_id,
68
+ "setsEvaluated": 0,
69
+ "requirementsEvaluated": 0,
70
+ "status": "failed",
71
+ },
72
+ "results": [],
87
73
  },
88
- "results": []
89
- }, status=status.HTTP_200_OK)
74
+ status=status.HTTP_200_OK,
75
+ )
90
76
  except Exception as e:
91
77
  msg = f"Unexpected error retrieving PatientExamination {pe_id}: {e}"
92
78
  errors.append(msg)
93
79
  logger.exception("evaluate_requirements: %s", msg)
94
- return Response({
95
- "ok": False,
96
- "errors": errors,
97
- "meta": {
98
- "patientExaminationId": pe_id,
99
- "setsEvaluated": 0,
100
- "requirementsEvaluated": 0,
101
- "status": "failed",
80
+ return Response(
81
+ {
82
+ "ok": False,
83
+ "errors": errors,
84
+ "meta": {
85
+ "patientExaminationId": pe_id,
86
+ "setsEvaluated": 0,
87
+ "requirementsEvaluated": 0,
88
+ "status": "failed",
89
+ },
90
+ "results": [],
102
91
  },
103
- "results": []
104
- }, status=status.HTTP_200_OK)
92
+ status=status.HTTP_200_OK,
93
+ )
105
94
 
106
95
  # ---- determine requirement sets
107
96
  try:
@@ -120,75 +109,152 @@ def evaluate_requirements(request):
120
109
  msg = f"Error loading RequirementSets: {e}"
121
110
  errors.append(msg)
122
111
  logger.exception("evaluate_requirements: %s", msg)
123
- return Response({
124
- "ok": False,
112
+ return Response(
113
+ {
114
+ "ok": False,
115
+ "errors": errors,
116
+ "meta": {
117
+ "patientExaminationId": pe_id,
118
+ "setsEvaluated": 0,
119
+ "requirementsEvaluated": 0,
120
+ "status": "failed",
121
+ },
122
+ "results": [],
123
+ },
124
+ status=status.HTTP_200_OK,
125
+ )
126
+
127
+ # nothing to evaluate → still return 200
128
+ if not requirement_sets:
129
+ response_payload = {
130
+ "ok": len(errors) == 0,
125
131
  "errors": errors,
126
132
  "meta": {
127
133
  "patientExaminationId": pe_id,
128
134
  "setsEvaluated": 0,
129
135
  "requirementsEvaluated": 0,
130
- "status": "failed",
136
+ "status": "failed" if errors else "ok",
131
137
  },
132
- "results": []
133
- }, status=status.HTTP_200_OK)
138
+ "results": [],
139
+ }
140
+ return Response(response_payload, status=status.HTTP_200_OK)
141
+
142
+ # mapping from IDs to objects for later lookup
143
+ sets_by_id: dict[int, RequirementSet] = {s.id: s for s in requirement_sets}
144
+
145
+ # ---- main evaluation with set dependencies
146
+ try:
147
+ # returns: { set_id: { req_id: (status, details) } }
148
+ set_results = evaluate_requirement_sets_with_dependencies(
149
+ requirement_sets,
150
+ pe.patient,
151
+ mode="strict",
152
+ )
153
+
154
+ for set_id, req_dict in set_results.items():
155
+ req_set = sets_by_id.get(set_id)
156
+ set_name = getattr(
157
+ req_set, "name", str(set_id)
158
+ ) if req_set is not None else str(set_id)
134
159
 
135
- # ---- evaluate
136
- for req_set in requirement_sets:
137
- for req in req_set.requirements.all():
138
- # (optionally) reload Requirement to ensure fresh instance as in your original code
139
- try:
140
- requirement_obj = Requirement.objects.get(name=req.name)
141
- except Exception:
142
- # fall back to the prefetched instance
143
- requirement_obj = req
160
+ for req_id, (status_value, details) in req_dict.items():
161
+ try:
162
+ requirement_obj = Requirement.objects.get(id=req_id)
163
+ req_name = getattr(requirement_obj, "name", f"#{req_id}")
164
+ except Requirement.DoesNotExist:
165
+ requirement_obj = None
166
+ req_name = f"#{req_id}"
144
167
 
145
- try:
146
- met, details = requirement_obj.evaluate_with_details(pe.patient, mode="strict")
147
- # normalize details to a string for the frontend
168
+ # map RequirementStatus → met + error
169
+ if status_value == "PASSED":
170
+ met = True
171
+ error_str = None
172
+ elif status_value in ("FAILED", "BLOCKED"):
173
+ met = False
174
+ error_str = None
175
+ else: # "ERROR"
176
+ met = False
177
+ error_str = "Technischer Fehler bei der Auswertung"
178
+
179
+ # normalize details to string
148
180
  if isinstance(details, str):
149
181
  details_str = details
150
182
  else:
151
183
  try:
152
- details_str = json.dumps(details, ensure_ascii=False, default=str)
184
+ details_str = json.dumps(
185
+ details, ensure_ascii=False, default=str
186
+ )
153
187
  except Exception:
154
188
  details_str = str(details)
155
189
 
156
- results.append({
157
- "requirement_set_id": getattr(req_set, "id", None),
158
- "requirement_set_name": getattr(req_set, "name", str(getattr(req_set, "id", ""))),
159
- "requirement_name": getattr(requirement_obj, "name", "unknown"),
160
- "met": bool(met),
161
- "details": details_str if details_str else ("Voraussetzung erfüllt" if met else "Voraussetzung nicht erfüllt"),
162
- "error": None
163
- })
164
- except (TypeError, ValueError) as e:
165
- msg = f"Fehler bei der Bewertung der Voraussetzung: {e}"
166
- logger.warning("evaluate_requirements: requirement '%s' error: %s",
167
- getattr(requirement_obj, "name", "unknown"), e)
168
- results.append({
169
- "requirement_set_id": getattr(req_set, "id", None),
170
- "requirement_set_name": getattr(req_set, "name", str(getattr(req_set, "id", ""))),
171
- "requirement_name": getattr(requirement_obj, "name", "unknown"),
172
- "met": False,
173
- "details": msg,
174
- "error": f"{e.__class__.__name__}: {e}"
175
- })
176
- errors.append(msg)
177
- except Exception as e:
178
- msg = f"Unerwarteter Fehler bei der Bewertung: {e}"
179
- logger.exception("evaluate_requirements: requirement '%s' unexpected error",
180
- getattr(requirement_obj, "name", "unknown"))
181
- results.append({
182
- "requirement_set_id": getattr(req_set, "id", None),
183
- "requirement_set_name": getattr(req_set, "name", str(getattr(req_set, "id", ""))),
184
- "requirement_name": getattr(requirement_obj, "name", "unknown"),
185
- "met": False,
186
- "details": msg,
187
- "error": f"{e.__class__.__name__}: {e}"
188
- })
189
- errors.append(msg)
190
+ # default fallback text if details are empty
191
+ if not details_str:
192
+ details_str = (
193
+ "Voraussetzung erfüllt"
194
+ if met
195
+ else "Voraussetzung nicht erfüllt"
196
+ )
197
+
198
+ if status_value == "ERROR":
199
+ # add a high-level error for meta if there was an internal error
200
+ msg = (
201
+ f"Technischer Fehler bei der Auswertung von "
202
+ f"Voraussetzung '{req_name}' in Set '{set_name}'."
203
+ )
204
+ errors.append(msg)
205
+
206
+ results.append(
207
+ {
208
+ "requirement_set_id": set_id,
209
+ "requirement_set_name": set_name,
210
+ "requirement_name": req_name,
211
+ "met": bool(met),
212
+ "details": details_str,
213
+ "error": error_str,
214
+ "status": status_value,
215
+ }
216
+ )
217
+ requirements_evaluated += 1
218
+
219
+ except Exception as e:
220
+ # hard failure of the orchestrator → log and fall back to per-requirement evaluation
221
+ msg = f"Unerwarteter Fehler bei der gruppenbasierten Bewertung: {e}"
222
+ errors.append(msg)
223
+ logger.exception("evaluate_requirements: %s", msg)
224
+
225
+ for req_set in requirement_sets:
226
+ for req in req_set.requirements.all():
227
+ met, details, error = safe_evaluate_requirement(
228
+ req, pe.patient, mode="strict"
229
+ )
230
+ # normalize details to string
231
+ if not isinstance(details, str):
232
+ try:
233
+ details = json.dumps(details, ensure_ascii=False, default=str)
234
+ except Exception:
235
+ details = str(details)
236
+
237
+ if not details:
238
+ details = (
239
+ "Voraussetzung erfüllt"
240
+ if met
241
+ else "Voraussetzung nicht erfüllt"
242
+ )
190
243
 
191
- requirements_evaluated += 1
244
+ results.append(
245
+ {
246
+ "requirement_set_id": req_set.id,
247
+ "requirement_set_name": getattr(
248
+ req_set, "name", str(req_set.id)
249
+ ),
250
+ "requirement_name": getattr(req, "name", "unknown"),
251
+ "met": bool(met),
252
+ "details": details,
253
+ "error": error,
254
+ "status": "PASSED" if met else "FAILED",
255
+ }
256
+ )
257
+ requirements_evaluated += 1
192
258
 
193
259
  # ---- response meta & status summary
194
260
  any_errors = len(errors) > 0
@@ -201,79 +267,14 @@ def evaluate_requirements(request):
201
267
 
202
268
  response_payload = {
203
269
  "ok": not any_errors,
204
- "errors": errors, # frontend can render these in a toast / banner
270
+ "errors": errors,
205
271
  "meta": {
206
272
  "patientExaminationId": pe_id,
207
273
  "setsEvaluated": sets_evaluated,
208
274
  "requirementsEvaluated": requirements_evaluated,
209
- "status": status_label
275
+ "status": status_label,
210
276
  },
211
- "results": results
277
+ "results": results,
212
278
  }
213
279
 
214
280
  return Response(response_payload, status=status.HTTP_200_OK)
215
-
216
-
217
- def evaluate_requirement_set(request) -> Response:
218
- """
219
- Evaluate a specific RequirementSet based on provided RequirementLinks data.
220
-
221
- Expects a JSON payload with the following structure:
222
- {
223
- "requirement_set_ids": [<requirement_set_ids>],
224
- "patient_examination_id": <patient_examination_id>
225
- }
226
-
227
- Returns a JSON response with the evaluation results for the specified RequirementSet:
228
- {
229
- "results": [
230
- {
231
- "requirement_name": <requirement_name>,
232
- "met": <true/false>,
233
- "details": <additional_details>
234
- },
235
- ...
236
- ]
237
- }
238
- """
239
- try:
240
- data = request.data
241
- requirement_set_ids = data.get("requirement_set_ids", None)
242
- patient_examination_id = data.get("patient_examination_id")
243
-
244
- if not requirement_set_ids:
245
- return Response({"error": "requirement_set_ids is required"}, status=status.HTTP_400_BAD_REQUEST)
246
-
247
- if not patient_examination_id:
248
- return Response({"error": "patient_examination_id is required"}, status=status.HTTP_400_BAD_REQUEST)
249
-
250
- # Get the patient examination
251
- try:
252
- patient_examination = PatientExamination.objects.get(id=patient_examination_id)
253
- except PatientExamination.DoesNotExist:
254
- return Response({"error": f"PatientExamination with id {patient_examination_id} does not exist"}, status=status.HTTP_404_NOT_FOUND)
255
-
256
- results = []
257
-
258
- # Construct RequirementLinks from the provided data
259
- for requirement_set_id in requirement_set_ids:
260
- # Fetch the specified RequirementSet
261
- try:
262
- req_set = RequirementSet.objects.get(id=requirement_set_id)
263
- except RequirementSet.DoesNotExist:
264
- return Response({"error": f"RequirementSet with id {requirement_set_id} does not exist"}, status=status.HTTP_404_NOT_FOUND)
265
-
266
- for requirement in req_set.requirements.all():
267
- met, details = requirement.evaluate(patient_examination.patient, mode="strict")
268
- results.append({
269
- "requirement_name": requirement.name,
270
- "met": met,
271
- "details": details
272
- })
273
-
274
- return Response({
275
- "results": results
276
- }, status=status.HTTP_200_OK)
277
-
278
- except Exception as e:
279
- return Response({"results": str(e), details: "Fehler bei der Bewertung der Voraussetzung"}, status=status.HTTP_400_BAD_REQUEST)