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

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

Potentially problematic release.


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

Files changed (360) hide show
  1. endoreg_db/authz/auth.py +74 -0
  2. endoreg_db/authz/backends.py +168 -0
  3. endoreg_db/authz/management/commands/list_routes.py +18 -0
  4. endoreg_db/authz/middleware.py +83 -0
  5. endoreg_db/authz/permissions.py +127 -0
  6. endoreg_db/authz/policy.py +218 -0
  7. endoreg_db/authz/views_auth.py +66 -0
  8. endoreg_db/config/env.py +13 -8
  9. endoreg_db/data/__init__.py +8 -31
  10. endoreg_db/data/_examples/disease.yaml +55 -0
  11. endoreg_db/data/_examples/disease_classification.yaml +13 -0
  12. endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
  13. endoreg_db/data/_examples/event.yaml +64 -0
  14. endoreg_db/data/_examples/examination.yaml +72 -0
  15. endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
  16. endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
  17. endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
  18. endoreg_db/data/_examples/finding/complication.yaml +16 -0
  19. endoreg_db/data/_examples/finding/data.yaml +105 -0
  20. endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
  21. endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
  22. endoreg_db/data/_examples/finding/outcome.yaml +12 -0
  23. endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
  24. endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
  25. endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
  26. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
  27. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
  28. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
  29. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
  30. endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
  31. endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
  32. endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
  33. endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
  34. endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
  35. endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
  36. endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
  37. endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
  38. endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
  39. endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
  40. endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
  41. endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
  42. endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
  43. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
  44. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
  45. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
  46. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
  47. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
  48. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
  49. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
  50. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
  51. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
  52. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
  53. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
  54. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
  55. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
  56. endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
  57. endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
  58. endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
  59. endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
  60. endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
  61. endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
  62. endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
  63. endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
  64. endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
  65. endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
  66. endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
  67. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  68. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  69. endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
  70. endoreg_db/data/_examples/finding_type/data.yaml +43 -0
  71. endoreg_db/data/_examples/requirement/age.yaml +26 -0
  72. endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
  73. endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
  74. endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
  75. endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
  76. endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
  77. endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
  78. endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
  79. endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
  80. endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
  81. endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
  82. endoreg_db/data/_examples/requirement/gender.yaml +25 -0
  83. endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
  84. endoreg_db/data/_examples/requirement/medication.yaml +93 -0
  85. endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
  86. endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
  87. endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
  88. endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
  89. endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
  90. endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
  91. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
  92. endoreg_db/data/event_classification/data.yaml +4 -0
  93. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  94. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
  95. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
  96. endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
  97. endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
  98. endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
  99. endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
  100. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
  101. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  102. endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
  103. endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
  104. endoreg_db/data/requirement_set/_old_ +109 -0
  105. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  106. endoreg_db/data/setup_config.yaml +4 -4
  107. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  108. endoreg_db/exceptions.py +5 -2
  109. endoreg_db/helpers/data_loader.py +1 -1
  110. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  111. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  112. endoreg_db/management/commands/import_video.py +9 -10
  113. endoreg_db/management/commands/import_video_with_classification.py +1 -1
  114. endoreg_db/management/commands/init_default_ai_model.py +1 -1
  115. endoreg_db/management/commands/list_routes.py +18 -0
  116. endoreg_db/management/commands/load_center_data.py +12 -12
  117. endoreg_db/management/commands/load_requirement_data.py +60 -31
  118. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  119. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  120. endoreg_db/management/commands/storage_management.py +271 -203
  121. endoreg_db/migrations/0001_initial.py +1799 -1300
  122. endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
  123. endoreg_db/migrations/_old/0001_initial.py +1857 -0
  124. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
  125. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
  126. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
  127. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
  128. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
  129. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
  130. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
  131. endoreg_db/models/__init__.py +78 -123
  132. endoreg_db/models/administration/__init__.py +21 -42
  133. endoreg_db/models/administration/ai/active_model.py +2 -2
  134. endoreg_db/models/administration/ai/ai_model.py +7 -6
  135. endoreg_db/models/administration/case/__init__.py +1 -15
  136. endoreg_db/models/administration/case/case.py +3 -3
  137. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  138. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  139. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  140. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  141. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  142. endoreg_db/models/administration/center/center.py +33 -19
  143. endoreg_db/models/administration/center/center_product.py +12 -9
  144. endoreg_db/models/administration/center/center_resource.py +25 -19
  145. endoreg_db/models/administration/center/center_shift.py +21 -17
  146. endoreg_db/models/administration/center/center_waste.py +16 -8
  147. endoreg_db/models/administration/person/__init__.py +2 -0
  148. endoreg_db/models/administration/person/employee/employee.py +10 -5
  149. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  150. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  151. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  152. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  153. endoreg_db/models/administration/person/patient/patient.py +103 -100
  154. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  155. endoreg_db/models/administration/person/person.py +4 -0
  156. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  157. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  158. endoreg_db/models/administration/product/product.py +20 -15
  159. endoreg_db/models/administration/product/product_material.py +17 -18
  160. endoreg_db/models/administration/product/product_weight.py +12 -8
  161. endoreg_db/models/administration/product/reference_product.py +23 -55
  162. endoreg_db/models/administration/qualification/qualification.py +7 -3
  163. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  164. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  165. endoreg_db/models/administration/shift/shift.py +16 -12
  166. endoreg_db/models/administration/shift/shift_type.py +23 -31
  167. endoreg_db/models/label/__init__.py +7 -8
  168. endoreg_db/models/label/annotation/image_classification.py +10 -9
  169. endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
  170. endoreg_db/models/label/label.py +15 -15
  171. endoreg_db/models/label/label_set.py +19 -6
  172. endoreg_db/models/label/label_type.py +1 -1
  173. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  174. endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
  175. endoreg_db/models/label/video_segmentation_label.py +4 -0
  176. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  177. endoreg_db/models/media/frame/frame.py +22 -22
  178. endoreg_db/models/media/pdf/raw_pdf.py +110 -182
  179. endoreg_db/models/media/pdf/report_file.py +25 -29
  180. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
  181. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  182. endoreg_db/models/media/video/__init__.py +1 -0
  183. endoreg_db/models/media/video/create_from_file.py +48 -56
  184. endoreg_db/models/media/video/pipe_2.py +8 -9
  185. endoreg_db/models/media/video/video_file.py +150 -108
  186. endoreg_db/models/media/video/video_file_ai.py +288 -74
  187. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  188. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  189. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  190. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  191. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  192. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  193. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  194. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  195. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  196. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  197. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  198. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  199. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  200. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  201. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  202. endoreg_db/models/media/video/video_file_io.py +109 -62
  203. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  204. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  205. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  206. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  207. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  208. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  209. endoreg_db/models/media/video/video_file_segments.py +24 -17
  210. endoreg_db/models/media/video/video_metadata.py +19 -35
  211. endoreg_db/models/media/video/video_processing.py +96 -95
  212. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  213. endoreg_db/models/medical/disease.py +22 -16
  214. endoreg_db/models/medical/event.py +31 -18
  215. endoreg_db/models/medical/examination/__init__.py +13 -6
  216. endoreg_db/models/medical/examination/examination.py +17 -18
  217. endoreg_db/models/medical/examination/examination_indication.py +26 -25
  218. endoreg_db/models/medical/examination/examination_time.py +16 -6
  219. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  220. endoreg_db/models/medical/examination/examination_type.py +3 -4
  221. endoreg_db/models/medical/finding/finding.py +38 -39
  222. endoreg_db/models/medical/finding/finding_classification.py +37 -48
  223. endoreg_db/models/medical/finding/finding_intervention.py +27 -22
  224. endoreg_db/models/medical/finding/finding_type.py +13 -12
  225. endoreg_db/models/medical/hardware/endoscope.py +20 -26
  226. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  227. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  228. endoreg_db/models/medical/medication/medication.py +22 -10
  229. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  230. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  231. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  232. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  233. endoreg_db/models/medical/organ/__init__.py +15 -12
  234. endoreg_db/models/medical/patient/medication_examples.py +1 -5
  235. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  236. endoreg_db/models/medical/patient/patient_event.py +19 -22
  237. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  238. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  239. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  240. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  241. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  242. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  243. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  244. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  245. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  246. endoreg_db/models/medical/risk/risk.py +7 -6
  247. endoreg_db/models/medical/risk/risk_type.py +8 -5
  248. endoreg_db/models/metadata/model_meta.py +60 -29
  249. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  250. endoreg_db/models/metadata/pdf_meta.py +19 -24
  251. endoreg_db/models/metadata/sensitive_meta.py +102 -85
  252. endoreg_db/models/metadata/sensitive_meta_logic.py +192 -173
  253. endoreg_db/models/metadata/video_meta.py +51 -31
  254. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  255. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  256. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  257. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  258. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  259. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  260. endoreg_db/models/other/emission/emission_factor.py +18 -8
  261. endoreg_db/models/other/gender.py +10 -5
  262. endoreg_db/models/other/information_source.py +25 -25
  263. endoreg_db/models/other/material.py +9 -5
  264. endoreg_db/models/other/resource.py +6 -4
  265. endoreg_db/models/other/tag.py +10 -5
  266. endoreg_db/models/other/transport_route.py +13 -8
  267. endoreg_db/models/other/unit.py +10 -6
  268. endoreg_db/models/other/waste.py +6 -5
  269. endoreg_db/models/requirement/requirement.py +580 -272
  270. endoreg_db/models/requirement/requirement_error.py +85 -0
  271. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  272. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  273. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  274. endoreg_db/models/requirement/requirement_operator.py +36 -33
  275. endoreg_db/models/requirement/requirement_set.py +74 -57
  276. endoreg_db/models/state/__init__.py +4 -4
  277. endoreg_db/models/state/abstract.py +2 -2
  278. endoreg_db/models/state/anonymization.py +12 -0
  279. endoreg_db/models/state/audit_ledger.py +46 -47
  280. endoreg_db/models/state/label_video_segment.py +9 -0
  281. endoreg_db/models/state/raw_pdf.py +40 -46
  282. endoreg_db/models/state/sensitive_meta.py +6 -2
  283. endoreg_db/models/state/video.py +58 -53
  284. endoreg_db/models/upload_job.py +32 -55
  285. endoreg_db/models/utils.py +1 -2
  286. endoreg_db/root_urls.py +21 -2
  287. endoreg_db/serializers/__init__.py +0 -2
  288. endoreg_db/serializers/anonymization.py +18 -10
  289. endoreg_db/serializers/meta/report_meta.py +1 -1
  290. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  291. endoreg_db/serializers/misc/file_overview.py +11 -99
  292. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  293. endoreg_db/serializers/video/segmentation.py +2 -1
  294. endoreg_db/serializers/video/video_processing_history.py +20 -5
  295. endoreg_db/services/anonymization.py +75 -73
  296. endoreg_db/services/lookup_service.py +37 -24
  297. endoreg_db/services/pdf_import.py +166 -68
  298. endoreg_db/services/storage_aware_video_processor.py +140 -114
  299. endoreg_db/services/video_import.py +193 -283
  300. endoreg_db/urls/__init__.py +7 -20
  301. endoreg_db/urls/media.py +108 -67
  302. endoreg_db/urls/root_urls.py +29 -0
  303. endoreg_db/utils/__init__.py +15 -5
  304. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  305. endoreg_db/utils/case_generator/__init__.py +3 -0
  306. endoreg_db/utils/dataloader.py +88 -16
  307. endoreg_db/utils/defaults/set_default_center.py +32 -0
  308. endoreg_db/utils/names.py +22 -16
  309. endoreg_db/utils/permissions.py +2 -1
  310. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  311. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
  312. endoreg_db/utils/setup_config.py +8 -5
  313. endoreg_db/utils/storage.py +115 -0
  314. endoreg_db/utils/validate_endo_roi.py +8 -2
  315. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  316. endoreg_db/views/__init__.py +0 -10
  317. endoreg_db/views/anonymization/media_management.py +198 -163
  318. endoreg_db/views/anonymization/overview.py +4 -1
  319. endoreg_db/views/anonymization/validate.py +174 -40
  320. endoreg_db/views/media/__init__.py +2 -0
  321. endoreg_db/views/media/pdf_media.py +131 -152
  322. endoreg_db/views/media/sensitive_metadata.py +46 -6
  323. endoreg_db/views/media/video_media.py +89 -82
  324. endoreg_db/views/media/video_segments.py +2 -3
  325. endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
  326. endoreg_db/views/patient/patient.py +5 -4
  327. endoreg_db/views/pdf/pdf_stream.py +20 -21
  328. endoreg_db/views/pdf/reimport.py +11 -32
  329. endoreg_db/views/requirement/evaluate.py +188 -187
  330. endoreg_db/views/requirement/lookup.py +17 -3
  331. endoreg_db/views/requirement/requirement_utils.py +89 -0
  332. endoreg_db/views/video/__init__.py +0 -2
  333. endoreg_db/views/video/correction.py +2 -2
  334. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
  335. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +341 -245
  336. endoreg_db/models/administration/permissions/__init__.py +0 -44
  337. endoreg_db/models/media/video/video_file_frames.py +0 -0
  338. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  339. endoreg_db/models/rule/__init__.py +0 -13
  340. endoreg_db/models/rule/rule.py +0 -27
  341. endoreg_db/models/rule/rule_applicator.py +0 -224
  342. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  343. endoreg_db/models/rule/rule_type.py +0 -20
  344. endoreg_db/models/rule/ruleset.py +0 -17
  345. endoreg_db/serializers/video/video_metadata.py +0 -105
  346. endoreg_db/urls/report.py +0 -48
  347. endoreg_db/urls/video.py +0 -61
  348. endoreg_db/utils/case_generator/case_generator.py +0 -159
  349. endoreg_db/utils/case_generator/utils.py +0 -30
  350. endoreg_db/views/report/__init__.py +0 -9
  351. endoreg_db/views/report/report_list.py +0 -112
  352. endoreg_db/views/report/report_with_secure_url.py +0 -28
  353. endoreg_db/views/report/start_examination.py +0 -7
  354. endoreg_db/views.py +0 -0
  355. /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
  356. /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
  357. /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
  358. /endoreg_db/{models/media/video/refactor_plan.md → views/pdf/pdf_stream_views.py} +0 -0
  359. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
  360. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -1,22 +1,26 @@
1
- from django.db import models
2
1
  from typing import TYPE_CHECKING
3
2
 
3
+ from django.db import models
4
+
4
5
  if TYPE_CHECKING:
5
- from .patient_examination import PatientExamination
6
6
  from ..examination import (
7
7
  ExaminationIndication,
8
8
  ExaminationIndicationClassificationChoice,
9
9
  )
10
+ from .patient_examination import PatientExamination
11
+
12
+
10
13
  class PatientExaminationIndication(models.Model):
11
- '''A model to store the indication for a patient examination.'''
12
- patient_examination = models.ForeignKey('PatientExamination', on_delete=models.CASCADE, related_name='indications')
13
- examination_indication = models.ForeignKey('ExaminationIndication', on_delete=models.CASCADE)
14
- indication_choice = models.ForeignKey('ExaminationIndicationClassificationChoice', on_delete=models.CASCADE, blank=True, null=True)
14
+ """A model to store the indication for a patient examination."""
15
+
16
+ patient_examination = models.ForeignKey("PatientExamination", on_delete=models.CASCADE, related_name="indications")
17
+ examination_indication = models.ForeignKey("ExaminationIndication", on_delete=models.CASCADE)
18
+ indication_choice = models.ForeignKey("ExaminationIndicationClassificationChoice", on_delete=models.CASCADE, blank=True, null=True)
15
19
 
16
20
  if TYPE_CHECKING:
17
- patient_examination: "PatientExamination"
18
- examination_indication: "ExaminationIndication"
19
- indication_choice: "ExaminationIndicationClassificationChoice"
21
+ patient_examination: models.ForeignKey["PatientExamination"]
22
+ examination_indication: models.ForeignKey["ExaminationIndication"]
23
+ indication_choice: models.ForeignKey["ExaminationIndicationClassificationChoice|None"]
20
24
 
21
25
  def __str__(self):
22
26
  return f"{self.patient_examination} - {self.examination_indication}"
@@ -26,19 +30,17 @@ class PatientExaminationIndication(models.Model):
26
30
  e = pe.examination
27
31
 
28
32
  return e
29
-
33
+
30
34
  def get_patient_examination(self):
31
35
  pe = self.patient_examination
32
36
  return pe
33
-
37
+
34
38
  def get_patient(self):
35
39
  pe = self.get_patient_examination()
36
40
  patient = pe.patient
37
41
  return patient
38
-
39
- def get_choices(self):
40
42
 
43
+ def get_choices(self):
41
44
  examination_indication = self.examination_indication
42
45
  choices = [_ for _ in examination_indication.get_choices()]
43
46
  return choices
44
-
@@ -1,71 +1,73 @@
1
- from django.db import models
1
+ from typing import TYPE_CHECKING
2
+
2
3
  from django.core.exceptions import ValidationError
4
+ from django.db import models
3
5
  from django.utils import timezone
4
- from typing import TYPE_CHECKING
5
6
 
6
7
  if TYPE_CHECKING:
7
8
  from endoreg_db.models import (
8
- PatientExamination,
9
9
  Finding,
10
- PatientFindingIntervention,
11
- PatientFindingClassification,
12
10
  LabelVideoSegment,
11
+ PatientExamination,
12
+ PatientFindingClassification,
13
+ PatientFindingIntervention,
13
14
  )
14
15
  from endoreg_db.utils.links.requirement_link import RequirementLinks
15
-
16
+
17
+
16
18
  class PatientFinding(models.Model):
17
- patient_examination = models.ForeignKey('PatientExamination', on_delete=models.CASCADE, related_name='patient_findings') # type: ignore[assignment]
18
- finding = models.ForeignKey('Finding', on_delete=models.CASCADE, related_name='finding_patient_findings') # type: ignore[assignment]
19
-
19
+ patient_examination = models.ForeignKey("PatientExamination", on_delete=models.CASCADE, related_name="patient_findings")
20
+ finding = models.ForeignKey("Finding", on_delete=models.CASCADE, related_name="finding_patient_findings")
21
+
20
22
  # Audit-Felder für medizinische Nachverfolgung
21
23
  created_at = models.DateTimeField(auto_now_add=True)
22
24
  updated_at = models.DateTimeField(auto_now=True)
23
- created_by = models.ForeignKey('auth.User', on_delete=models.PROTECT, related_name='created_findings', null=True, blank=True)
24
- updated_by = models.ForeignKey('auth.User', on_delete=models.PROTECT, related_name='updated_findings', null=True, blank=True)
25
-
25
+ created_by = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="created_findings", null=True, blank=True)
26
+ updated_by = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="updated_findings", null=True, blank=True)
27
+
26
28
  # Soft Delete für historische Daten
27
29
  is_active = models.BooleanField(default=True, help_text="Deaktiviert statt gelöscht für Audit-Trail")
28
30
  deactivated_at = models.DateTimeField(null=True, blank=True)
29
- deactivated_by = models.ForeignKey('auth.User', on_delete=models.PROTECT, related_name='deactivated_findings', null=True, blank=True)
30
-
31
+ deactivated_by = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="deactivated_findings", null=True, blank=True)
32
+
31
33
  if TYPE_CHECKING:
32
- patient_examination: "PatientExamination"
33
- finding: "Finding"
34
- classifications: models.QuerySet["PatientFindingClassification"]
35
- interventions: models.QuerySet["PatientFindingIntervention"]
36
- video_segments: models.QuerySet["LabelVideoSegment"]
34
+ patient_examination: models.ForeignKey["PatientExamination"]
35
+ finding: models.ForeignKey["Finding"]
36
+
37
+ @property
38
+ def video_segments(self) -> models.manager.RelatedManager["LabelVideoSegment"]: ...
39
+
40
+ @property
41
+ def interventions(self) -> models.manager.RelatedManager["PatientFindingIntervention"]: ...
42
+
43
+ @property
44
+ def classifications(self) -> models.manager.RelatedManager["PatientFindingClassification"]: ...
37
45
 
38
46
  class Meta:
39
- verbose_name = 'Patient Finding'
40
- verbose_name_plural = 'Patient Findings'
41
- ordering = ['patient_examination', 'finding']
42
-
47
+ verbose_name = "Patient Finding"
48
+ verbose_name_plural = "Patient Findings"
49
+ ordering = ["patient_examination", "finding"]
50
+
43
51
  # Wichtige Constraints für Datenintegrität
44
52
  constraints = [
45
53
  models.UniqueConstraint(
46
- fields=['patient_examination', 'finding'],
47
- condition=models.Q(is_active=True),
48
- name='unique_active_finding_per_examination'
54
+ fields=["patient_examination", "finding"], condition=models.Q(is_active=True), name="unique_active_finding_per_examination"
49
55
  ),
50
56
  models.CheckConstraint(
51
- check=models.Q(
52
- deactivated_at__isnull=True,
53
- deactivated_by__isnull=True
54
- ) | models.Q(
55
- deactivated_at__isnull=False,
56
- deactivated_by__isnull=False,
57
- is_active=False
58
- ),
59
- name='deactivation_fields_consistency'
60
- )
57
+ check=models.Q( # called .check in future?
58
+ deactivated_at__isnull=True, deactivated_by__isnull=True
59
+ )
60
+ | models.Q(deactivated_at__isnull=False, deactivated_by__isnull=False, is_active=False),
61
+ name="deactivation_fields_consistency",
62
+ ),
61
63
  ]
62
-
64
+
63
65
  # Performance-optimierte Indizes
64
66
  indexes = [
65
- models.Index(fields=['patient_examination', 'finding']),
66
- models.Index(fields=['patient_examination', 'is_active']),
67
- models.Index(fields=['created_at']),
68
- models.Index(fields=['finding', 'is_active']),
67
+ models.Index(fields=["patient_examination", "finding"]),
68
+ models.Index(fields=["patient_examination", "is_active"]),
69
+ models.Index(fields=["created_at"]),
70
+ models.Index(fields=["finding", "is_active"]),
69
71
  ]
70
72
 
71
73
  @property
@@ -80,61 +82,56 @@ class PatientFinding(models.Model):
80
82
  def clean(self):
81
83
  """
82
84
  Validates the patient finding against business rules before saving.
83
-
85
+
84
86
  Ensures that the selected finding is allowed for the associated examination and that all required findings are present. Raises a ValidationError if the finding is not permitted or required findings are missing.
85
87
  """
86
88
  super().clean()
87
-
89
+
88
90
  # Prüfe ob Finding für diese Examination erlaubt ist
89
91
  if self.finding and self.patient_examination:
90
- available_findings = self.patient_examination.examination.get_available_findings()
92
+ available_findings = self.patient_examination.examination_safe.get_available_findings()
91
93
  if self.finding not in available_findings:
92
- raise ValidationError({
93
- 'finding': f'Finding "{self.finding.name}" ist nicht für Examination "{self.patient_examination.examination.name}" erlaubt.'
94
- })
95
-
94
+ raise ValidationError(
95
+ {"finding": f'Finding "{self.finding.name}" ist nicht für Examination "{self.patient_examination.examination_safe.name}" erlaubt.'}
96
+ )
97
+
96
98
  # Prüfe Required Findings Logic
97
99
  if self.finding and self.patient_examination:
98
100
  self._validate_required_findings()
99
-
100
- # This avoids validation errors on partial updates
101
+
102
+ # This avoids validation errors on partial updates
101
103
  def save(self, *args, **kwargs):
102
104
  """
103
105
  Validates the model instance before saving, unless performing a partial update.
104
-
106
+
105
107
  Performs full model validation with `full_clean()` before saving, except when `update_fields` is specified for a partial update.
106
108
  """
107
- if not kwargs.get('update_fields'):
109
+ if not kwargs.get("update_fields"):
108
110
  self.full_clean()
109
111
  super().save(*args, **kwargs)
110
112
 
111
-
112
113
  def _validate_required_findings(self):
113
114
  """Validiert Required vs Optional Finding Constraints"""
114
115
  examination = self.patient_examination.examination
115
-
116
+
116
117
  # Hole Required Findings für diese Examination
117
- required_findings = getattr(examination, 'required_findings', None)
118
+ required_findings = getattr(examination, "required_findings", None)
118
119
  if required_findings and required_findings.exists():
119
120
  # Prüfe ob alle Required Findings vorhanden sind
120
- existing_findings = self.patient_examination.patient_findings.filter(
121
- is_active=True
122
- ).values_list('finding', flat=True)
123
-
121
+ existing_findings = self.patient_examination.patient_findings.filter(is_active=True).values_list("finding", flat=True)
122
+
124
123
  missing_required = required_findings.exclude(id__in=existing_findings)
125
124
  if missing_required.exists() and self.finding not in required_findings.all():
126
- missing_names = ', '.join([f.name for f in missing_required])
127
- raise ValidationError(
128
- f'Erforderliche Findings fehlen: {missing_names}'
129
- )
125
+ missing_names = ", ".join([f.name for f in missing_required])
126
+ raise ValidationError(f"Erforderliche Findings fehlen: {missing_names}")
130
127
 
131
128
  def deactivate(self, user=None, reason=None):
132
129
  """Soft Delete mit Audit-Trail"""
133
130
  self.is_active = False
134
131
  self.deactivated_at = timezone.now()
135
132
  self.deactivated_by = user
136
- self.save(update_fields=['is_active', 'deactivated_at', 'deactivated_by'])
137
-
133
+ self.save(update_fields=["is_active", "deactivated_at", "deactivated_by"])
134
+
138
135
  # Deaktiviere auch abhängige Objekte
139
136
  self.locations.update(is_active=False, deactivated_at=timezone.now())
140
137
  self.morphologies.update(is_active=False, deactivated_at=timezone.now())
@@ -143,7 +140,7 @@ class PatientFinding(models.Model):
143
140
  def reactivate(self, user=None):
144
141
  """
145
142
  Reactivates a previously deactivated patient finding after validating its state.
146
-
143
+
147
144
  If validation passes, sets the finding as active, clears deactivation fields, updates the user, and saves changes. Raises a ValidationError if reactivation is not allowed.
148
145
  """
149
146
  if not self.is_active:
@@ -154,57 +151,50 @@ class PatientFinding(models.Model):
154
151
  self.deactivated_at = None
155
152
  self.deactivated_by = None
156
153
  self.updated_by = user
157
- self.save(update_fields=['is_active', 'deactivated_at', 'deactivated_by', 'updated_by'])
154
+ self.save(update_fields=["is_active", "deactivated_at", "deactivated_by", "updated_by"])
158
155
  except ValidationError as e:
159
- raise ValidationError(f'Reaktivierung nicht möglich: {e}')
160
-
156
+ raise ValidationError(f"Reaktivierung nicht möglich: {e}")
161
157
 
162
158
  def get_interventions(self):
163
159
  """
164
160
  Retrieve all active interventions associated with this patient finding.
165
-
161
+
166
162
  Returns:
167
163
  QuerySet: Active related interventions with intervention details prefetched.
168
164
  """
169
- return self.interventions.filter(is_active=True).select_related('intervention')
165
+ return self.interventions.filter(is_active=True).select_related("intervention")
170
166
 
171
167
  def add_classification(
172
- self,
173
- classification_id,
174
- classification_choice_id,
175
- user=None,
176
- ) -> "PatientFindingClassification":
168
+ self,
169
+ classification_id,
170
+ classification_choice_id,
171
+ user=None,
172
+ ) -> "PatientFindingClassification":
173
+ """
174
+ Add a classification choice to this patient finding after validating its association.
175
+
176
+ Parameters:
177
+ classification_id: The ID of the classification to add.
178
+ classification_choice_id: The ID of the classification choice to associate.
179
+ user: Optional user performing the action.
180
+
181
+ Returns:
182
+ PatientFindingClassification: The created or existing active classification association.
183
+
184
+ Raises:
185
+ ValidationError: If the classification does not exist or the choice is not valid for the classification.
177
186
  """
178
- Add a classification choice to this patient finding after validating its association.
179
-
180
- Parameters:
181
- classification_id: The ID of the classification to add.
182
- classification_choice_id: The ID of the classification choice to associate.
183
- user: Optional user performing the action.
184
-
185
- Returns:
186
- PatientFindingClassification: The created or existing active classification association.
187
-
188
- Raises:
189
- ValidationError: If the classification does not exist or the choice is not valid for the classification.
190
- """
191
- from .patient_finding_classification import PatientFindingClassification
192
187
  from ..finding import FindingClassification, FindingClassificationChoice
188
+ from .patient_finding_classification import PatientFindingClassification
193
189
 
194
190
  try:
195
191
  classification = FindingClassification.objects.get(id=classification_id)
196
192
  classification_choice = FindingClassificationChoice.objects.filter(id=classification_choice_id).first()
197
193
 
198
194
  if not classification.choices.filter(id=classification_choice_id).exists():
199
- raise ValidationError(
200
- f'Classification Choice {classification_choice_id} gehört nicht zu Classification {classification_id}'
201
- )
195
+ raise ValidationError(f"Classification Choice {classification_choice_id} gehört nicht zu Classification {classification_id}")
202
196
 
203
- existing = self.classifications.filter(
204
- classification=classification,
205
- classification_choice=classification_choice,
206
- is_active=True
207
- ).first()
197
+ existing = self.classifications.filter(classification=classification, classification_choice=classification_choice, is_active=True).first()
208
198
 
209
199
  if existing:
210
200
  return existing
@@ -218,54 +208,50 @@ class PatientFinding(models.Model):
218
208
  return patient_finding_classification
219
209
 
220
210
  except FindingClassification.DoesNotExist:
221
- raise ValidationError(f'Classification {classification_id} nicht gefunden')
211
+ raise ValidationError(f"Classification {classification_id} nicht gefunden")
222
212
 
223
213
  def add_intervention(self, intervention_id, state="pending", date=None, user=None):
224
214
  """
225
215
  Add an intervention to the patient finding after validating its existence.
226
-
216
+
227
217
  Parameters:
228
218
  intervention_id (int): The ID of the intervention to add.
229
219
  state (str, optional): The state of the intervention. Defaults to "pending".
230
220
  date (datetime, optional): The date of the intervention. Defaults to the current time if not provided.
231
221
  user (User, optional): The user creating the intervention.
232
-
222
+
233
223
  Returns:
234
224
  PatientFindingIntervention: The created intervention instance.
235
-
225
+
236
226
  Raises:
237
227
  ValidationError: If the specified intervention does not exist.
238
228
  """
239
- from .patient_finding_intervention import PatientFindingIntervention
240
229
  from ..finding import FindingIntervention
241
-
230
+ from .patient_finding_intervention import PatientFindingIntervention
231
+
242
232
  try:
243
233
  intervention = FindingIntervention.objects.get(id=intervention_id)
244
-
234
+
245
235
  patient_finding_intervention = PatientFindingIntervention.objects.create(
246
- patient_finding=self,
247
- intervention=intervention,
248
- state=state,
249
- date=date or timezone.now(),
250
- created_by=user
236
+ patient_finding=self, intervention=intervention, state=state, date=date or timezone.now(), created_by=user
251
237
  )
252
-
238
+
253
239
  return patient_finding_intervention
254
-
240
+
255
241
  except FindingIntervention.DoesNotExist:
256
- raise ValidationError(f'Intervention {intervention_id} nicht gefunden')
242
+ raise ValidationError(f"Intervention {intervention_id} nicht gefunden")
257
243
 
258
244
  def add_video_segment(self, video_segment):
259
245
  """
260
246
  Associates a video segment with this patient finding.
261
-
247
+
262
248
  Parameters:
263
- video_segment: The video segment instance to add.
264
-
249
+ video_segment: The video segment instance to add.
250
+
265
251
  Returns:
266
- The added video segment instance.
252
+ The added video segment instance.
267
253
  """
268
- self.video_segments.add(video_segment) #TODO
254
+ self.video_segments.add(video_segment) # TODO
269
255
  return video_segment
270
256
 
271
257
  # Manager für active/inactive Objekte
@@ -273,36 +259,32 @@ class PatientFinding(models.Model):
273
259
  def active_classifications(self):
274
260
  """
275
261
  Return all active classifications associated with this patient finding.
276
-
262
+
277
263
  Returns:
278
- QuerySet: Active related classifications where `is_active` is True.
264
+ QuerySet: Active related classifications where `is_active` is True.
279
265
  """
280
266
  return self.classifications.filter(is_active=True)
281
-
267
+
282
268
  @property
283
269
  def locations(self):
284
270
  """
285
271
  Return all classifications of this patient finding that are of type "location".
286
-
272
+
287
273
  Returns:
288
- QuerySet: Classifications related to this finding filtered by the "location" classification type.
274
+ QuerySet: Classifications related to this finding filtered by the "location" classification type.
289
275
  """
290
- classifications = self.classifications.filter(
291
- classification__classification_types__name__iexact="location"
292
- )
276
+ classifications = self.classifications.filter(classification__classification_types__name__iexact="location")
293
277
  return classifications
294
278
 
295
279
  @property
296
280
  def morphologies(self):
297
281
  """
298
282
  Return all classifications of this patient finding that are of type "morphology".
299
-
283
+
300
284
  Returns:
301
- QuerySet: Classifications related to this finding filtered by the "morphology" classification type.
285
+ QuerySet: Classifications related to this finding filtered by the "morphology" classification type.
302
286
  """
303
- classifications = self.classifications.filter(
304
- classification__classification_types__name__iexact="morphology"
305
- )
287
+ classifications = self.classifications.filter(classification__classification_types__name__iexact="morphology")
306
288
  return classifications
307
289
 
308
290
  @property
@@ -314,39 +296,40 @@ class PatientFinding(models.Model):
314
296
  """
315
297
  Aggregates and returns all related model instances relevant for requirement evaluation
316
298
  as a RequirementLinks object.
317
-
299
+
318
300
  This property provides access to:
319
301
  - The finding associated with this patient finding
320
302
  - All active finding classifications and their choices
321
303
  - All active finding interventions
322
304
  - The patient examination and patient
323
305
  """
306
+ from typing import List, cast
307
+
324
308
  from endoreg_db.utils.links.requirement_link import RequirementLinks
325
- from typing import cast, List
326
-
309
+
327
310
  # Get the base finding
328
311
  findings_list = [self.finding] if self.finding else []
329
-
312
+
330
313
  # Get all active finding classifications and their choices
331
314
  finding_classifications_list = []
332
315
  finding_classification_choices_list = []
333
-
316
+
334
317
  for pf_classification in self.active_classifications:
335
318
  if pf_classification.classification:
336
319
  finding_classifications_list.append(pf_classification.classification)
337
320
  if pf_classification.classification_choice:
338
321
  finding_classification_choices_list.append(pf_classification.classification_choice)
339
-
322
+
340
323
  # Get all active finding interventions
341
324
  finding_interventions_list = []
342
325
  for pf_intervention in self.active_interventions:
343
326
  if pf_intervention.intervention:
344
327
  finding_interventions_list.append(pf_intervention.intervention)
345
-
328
+
346
329
  # Include patient examination and patient for context
347
330
  patient_examinations_list = [self.patient_examination] if self.patient_examination else []
348
331
  patient_findings_list = cast("List[PatientFinding]", [self]) # Include self for direct patient finding evaluations
349
-
332
+
350
333
  return RequirementLinks(
351
334
  findings=findings_list,
352
335
  finding_classifications=finding_classifications_list,