endoreg-db 0.8.9.2__py3-none-any.whl → 0.8.9.10__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 (450) hide show
  1. endoreg_db/admin.py +10 -5
  2. endoreg_db/apps.py +4 -7
  3. endoreg_db/authz/auth.py +1 -0
  4. endoreg_db/authz/backends.py +1 -1
  5. endoreg_db/authz/management/commands/list_routes.py +2 -0
  6. endoreg_db/authz/middleware.py +8 -7
  7. endoreg_db/authz/permissions.py +21 -10
  8. endoreg_db/authz/policy.py +14 -19
  9. endoreg_db/authz/views_auth.py +14 -10
  10. endoreg_db/codemods/rename_datetime_fields.py +8 -1
  11. endoreg_db/exceptions.py +5 -2
  12. endoreg_db/forms/__init__.py +0 -1
  13. endoreg_db/forms/examination_form.py +4 -3
  14. endoreg_db/forms/patient_finding_intervention_form.py +30 -8
  15. endoreg_db/forms/patient_form.py +9 -13
  16. endoreg_db/forms/questionnaires/__init__.py +1 -1
  17. endoreg_db/forms/settings/__init__.py +4 -1
  18. endoreg_db/forms/unit.py +2 -1
  19. endoreg_db/helpers/count_db.py +17 -14
  20. endoreg_db/helpers/default_objects.py +2 -1
  21. endoreg_db/helpers/download_segmentation_model.py +4 -3
  22. endoreg_db/helpers/interact.py +0 -5
  23. endoreg_db/helpers/test_video_helper.py +33 -25
  24. endoreg_db/import_files/__init__.py +1 -1
  25. endoreg_db/import_files/context/__init__.py +1 -1
  26. endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
  27. endoreg_db/import_files/context/ensure_center.py +4 -4
  28. endoreg_db/import_files/context/file_lock.py +3 -3
  29. endoreg_db/import_files/context/import_context.py +11 -12
  30. endoreg_db/import_files/context/validate_directories.py +1 -0
  31. endoreg_db/import_files/file_storage/create_report_file.py +57 -34
  32. endoreg_db/import_files/file_storage/create_video_file.py +64 -35
  33. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
  34. endoreg_db/import_files/file_storage/state_management.py +89 -122
  35. endoreg_db/import_files/file_storage/storage.py +5 -1
  36. endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
  37. endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
  38. endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
  39. endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
  40. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
  41. endoreg_db/import_files/report_import_service.py +36 -30
  42. endoreg_db/import_files/video_import_service.py +27 -23
  43. endoreg_db/logger_conf.py +56 -40
  44. endoreg_db/management/__init__.py +1 -1
  45. endoreg_db/management/commands/__init__.py +1 -1
  46. endoreg_db/management/commands/check_auth.py +45 -38
  47. endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
  48. endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
  49. endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
  50. endoreg_db/management/commands/fix_video_paths.py +75 -54
  51. endoreg_db/management/commands/import_report.py +1 -3
  52. endoreg_db/management/commands/list_routes.py +2 -0
  53. endoreg_db/management/commands/load_ai_model_data.py +8 -2
  54. endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
  55. endoreg_db/management/commands/load_center_data.py +3 -3
  56. endoreg_db/management/commands/load_distribution_data.py +35 -38
  57. endoreg_db/management/commands/load_endoscope_data.py +0 -3
  58. endoreg_db/management/commands/load_examination_data.py +20 -4
  59. endoreg_db/management/commands/load_finding_data.py +18 -3
  60. endoreg_db/management/commands/load_gender_data.py +17 -24
  61. endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
  62. endoreg_db/management/commands/load_information_source.py +0 -3
  63. endoreg_db/management/commands/load_lab_value_data.py +14 -3
  64. endoreg_db/management/commands/load_legacy_data.py +303 -0
  65. endoreg_db/management/commands/load_name_data.py +1 -2
  66. endoreg_db/management/commands/load_pdf_type_data.py +4 -8
  67. endoreg_db/management/commands/load_profession_data.py +0 -1
  68. endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
  69. endoreg_db/management/commands/load_requirement_data.py +6 -2
  70. endoreg_db/management/commands/load_unit_data.py +0 -4
  71. endoreg_db/management/commands/load_user_groups.py +5 -7
  72. endoreg_db/management/commands/model_input.py +169 -0
  73. endoreg_db/management/commands/register_ai_model.py +22 -16
  74. endoreg_db/management/commands/setup_endoreg_db.py +110 -32
  75. endoreg_db/management/commands/storage_management.py +14 -8
  76. endoreg_db/management/commands/summarize_db_content.py +154 -63
  77. endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
  78. endoreg_db/management/commands/validate_video_files.py +82 -50
  79. endoreg_db/management/commands/video_validation.py +4 -6
  80. endoreg_db/migrations/0001_initial.py +112 -63
  81. endoreg_db/models/__init__.py +8 -0
  82. endoreg_db/models/administration/ai/active_model.py +5 -5
  83. endoreg_db/models/administration/ai/ai_model.py +41 -18
  84. endoreg_db/models/administration/ai/model_type.py +1 -0
  85. endoreg_db/models/administration/case/case.py +22 -22
  86. endoreg_db/models/administration/center/__init__.py +5 -5
  87. endoreg_db/models/administration/center/center.py +6 -2
  88. endoreg_db/models/administration/center/center_resource.py +18 -4
  89. endoreg_db/models/administration/center/center_shift.py +3 -1
  90. endoreg_db/models/administration/center/center_waste.py +6 -2
  91. endoreg_db/models/administration/person/__init__.py +1 -1
  92. endoreg_db/models/administration/person/employee/__init__.py +1 -1
  93. endoreg_db/models/administration/person/employee/employee_type.py +3 -1
  94. endoreg_db/models/administration/person/examiner/__init__.py +1 -1
  95. endoreg_db/models/administration/person/examiner/examiner.py +10 -2
  96. endoreg_db/models/administration/person/names/first_name.py +6 -4
  97. endoreg_db/models/administration/person/names/last_name.py +4 -3
  98. endoreg_db/models/administration/person/patient/__init__.py +1 -1
  99. endoreg_db/models/administration/person/patient/patient.py +0 -1
  100. endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
  101. endoreg_db/models/administration/person/person.py +1 -1
  102. endoreg_db/models/administration/product/__init__.py +7 -6
  103. endoreg_db/models/administration/product/product.py +6 -2
  104. endoreg_db/models/administration/product/product_group.py +9 -7
  105. endoreg_db/models/administration/product/product_material.py +9 -2
  106. endoreg_db/models/administration/product/reference_product.py +64 -15
  107. endoreg_db/models/administration/qualification/qualification.py +3 -1
  108. endoreg_db/models/administration/shift/shift.py +3 -1
  109. endoreg_db/models/administration/shift/shift_type.py +12 -4
  110. endoreg_db/models/aidataset/__init__.py +5 -0
  111. endoreg_db/models/aidataset/aidataset.py +193 -0
  112. endoreg_db/models/label/__init__.py +1 -1
  113. endoreg_db/models/label/label.py +10 -2
  114. endoreg_db/models/label/label_set.py +3 -1
  115. endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
  116. endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
  117. endoreg_db/models/media/__init__.py +12 -5
  118. endoreg_db/models/media/frame/__init__.py +1 -1
  119. endoreg_db/models/media/frame/frame.py +34 -8
  120. endoreg_db/models/media/pdf/__init__.py +2 -1
  121. endoreg_db/models/media/pdf/raw_pdf.py +11 -4
  122. endoreg_db/models/media/pdf/report_file.py +6 -2
  123. endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
  124. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
  125. endoreg_db/models/media/video/create_from_file.py +20 -41
  126. endoreg_db/models/media/video/pipe_1.py +75 -30
  127. endoreg_db/models/media/video/pipe_2.py +37 -12
  128. endoreg_db/models/media/video/video_file.py +36 -24
  129. endoreg_db/models/media/video/video_file_ai.py +235 -70
  130. endoreg_db/models/media/video/video_file_anonymize.py +240 -65
  131. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
  132. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
  133. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
  134. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
  135. endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
  136. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
  137. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
  138. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
  139. endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
  140. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
  141. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
  142. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
  143. endoreg_db/models/media/video/video_file_io.py +85 -33
  144. endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
  145. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
  146. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
  147. endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
  148. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
  149. endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
  150. endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
  151. endoreg_db/models/media/video/video_file_segments.py +118 -27
  152. endoreg_db/models/media/video/video_metadata.py +25 -6
  153. endoreg_db/models/media/video/video_processing.py +54 -15
  154. endoreg_db/models/medical/__init__.py +3 -13
  155. endoreg_db/models/medical/contraindication/__init__.py +3 -1
  156. endoreg_db/models/medical/disease.py +18 -6
  157. endoreg_db/models/medical/event.py +6 -2
  158. endoreg_db/models/medical/examination/__init__.py +5 -1
  159. endoreg_db/models/medical/examination/examination.py +22 -6
  160. endoreg_db/models/medical/examination/examination_indication.py +23 -7
  161. endoreg_db/models/medical/examination/examination_time.py +6 -2
  162. endoreg_db/models/medical/finding/__init__.py +3 -1
  163. endoreg_db/models/medical/finding/finding.py +37 -12
  164. endoreg_db/models/medical/finding/finding_classification.py +27 -8
  165. endoreg_db/models/medical/finding/finding_intervention.py +19 -6
  166. endoreg_db/models/medical/finding/finding_type.py +3 -1
  167. endoreg_db/models/medical/hardware/__init__.py +1 -1
  168. endoreg_db/models/medical/hardware/endoscope.py +14 -2
  169. endoreg_db/models/medical/laboratory/__init__.py +1 -1
  170. endoreg_db/models/medical/laboratory/lab_value.py +139 -39
  171. endoreg_db/models/medical/medication/__init__.py +7 -3
  172. endoreg_db/models/medical/medication/medication.py +3 -1
  173. endoreg_db/models/medical/medication/medication_indication.py +3 -1
  174. endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
  175. endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
  176. endoreg_db/models/medical/medication/medication_schedule.py +3 -1
  177. endoreg_db/models/medical/patient/__init__.py +2 -10
  178. endoreg_db/models/medical/patient/medication_examples.py +3 -14
  179. endoreg_db/models/medical/patient/patient_disease.py +17 -5
  180. endoreg_db/models/medical/patient/patient_event.py +12 -4
  181. endoreg_db/models/medical/patient/patient_examination.py +52 -15
  182. endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
  183. endoreg_db/models/medical/patient/patient_finding.py +105 -29
  184. endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
  185. endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
  186. endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
  187. endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
  188. endoreg_db/models/medical/patient/patient_medication.py +25 -7
  189. endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
  190. endoreg_db/models/metadata/model_meta.py +40 -12
  191. endoreg_db/models/metadata/model_meta_logic.py +51 -16
  192. endoreg_db/models/metadata/sensitive_meta.py +65 -28
  193. endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
  194. endoreg_db/models/metadata/video_meta.py +146 -39
  195. endoreg_db/models/metadata/video_prediction_logic.py +70 -21
  196. endoreg_db/models/metadata/video_prediction_meta.py +80 -27
  197. endoreg_db/models/operation_log.py +63 -0
  198. endoreg_db/models/other/__init__.py +10 -10
  199. endoreg_db/models/other/distribution/__init__.py +9 -7
  200. endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
  201. endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
  202. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
  203. endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
  204. endoreg_db/models/other/emission/__init__.py +1 -1
  205. endoreg_db/models/other/emission/emission_factor.py +9 -3
  206. endoreg_db/models/other/information_source.py +15 -5
  207. endoreg_db/models/other/material.py +3 -1
  208. endoreg_db/models/other/transport_route.py +3 -1
  209. endoreg_db/models/other/unit.py +6 -2
  210. endoreg_db/models/report/report.py +0 -1
  211. endoreg_db/models/requirement/requirement.py +84 -27
  212. endoreg_db/models/requirement/requirement_error.py +5 -6
  213. endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
  214. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
  215. endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
  216. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
  217. endoreg_db/models/requirement/requirement_operator.py +28 -8
  218. endoreg_db/models/requirement/requirement_set.py +34 -11
  219. endoreg_db/models/state/__init__.py +1 -0
  220. endoreg_db/models/state/audit_ledger.py +9 -2
  221. endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
  222. endoreg_db/models/state/processing_history/processing_history.py +136 -0
  223. endoreg_db/models/state/raw_pdf.py +0 -1
  224. endoreg_db/models/state/video.py +2 -4
  225. endoreg_db/models/utils.py +4 -2
  226. endoreg_db/queries/__init__.py +2 -6
  227. endoreg_db/queries/annotations/__init__.py +1 -3
  228. endoreg_db/queries/annotations/legacy.py +37 -26
  229. endoreg_db/root_urls.py +3 -4
  230. endoreg_db/schemas/examination_evaluation.py +3 -0
  231. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
  232. endoreg_db/serializers/__init__.py +2 -8
  233. endoreg_db/serializers/administration/__init__.py +1 -2
  234. endoreg_db/serializers/administration/ai/__init__.py +0 -1
  235. endoreg_db/serializers/administration/ai/active_model.py +3 -1
  236. endoreg_db/serializers/administration/ai/ai_model.py +5 -3
  237. endoreg_db/serializers/administration/ai/model_type.py +3 -1
  238. endoreg_db/serializers/administration/center.py +7 -2
  239. endoreg_db/serializers/administration/gender.py +4 -2
  240. endoreg_db/serializers/anonymization.py +13 -13
  241. endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
  242. endoreg_db/serializers/examination/__init__.py +1 -1
  243. endoreg_db/serializers/examination/base.py +12 -13
  244. endoreg_db/serializers/examination/dropdown.py +6 -7
  245. endoreg_db/serializers/examination_serializer.py +3 -6
  246. endoreg_db/serializers/finding/__init__.py +1 -1
  247. endoreg_db/serializers/finding/finding.py +14 -7
  248. endoreg_db/serializers/finding_classification/__init__.py +3 -3
  249. endoreg_db/serializers/finding_classification/choice.py +3 -3
  250. endoreg_db/serializers/finding_classification/classification.py +2 -4
  251. endoreg_db/serializers/label_video_segment/__init__.py +5 -3
  252. endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
  253. endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
  254. endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
  255. endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
  256. endoreg_db/serializers/meta/__init__.py +1 -2
  257. endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
  258. endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
  259. endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
  260. endoreg_db/serializers/misc/__init__.py +2 -2
  261. endoreg_db/serializers/misc/file_overview.py +11 -7
  262. endoreg_db/serializers/misc/stats.py +10 -8
  263. endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
  264. endoreg_db/serializers/misc/upload_job.py +32 -29
  265. endoreg_db/serializers/patient/__init__.py +2 -1
  266. endoreg_db/serializers/patient/patient.py +32 -15
  267. endoreg_db/serializers/patient/patient_dropdown.py +11 -3
  268. endoreg_db/serializers/patient_examination/__init__.py +1 -1
  269. endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
  270. endoreg_db/serializers/patient_finding/__init__.py +1 -1
  271. endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
  272. endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
  273. endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
  274. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
  275. endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
  276. endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
  277. endoreg_db/serializers/pdf/__init__.py +1 -3
  278. endoreg_db/serializers/requirements/requirement_schema.py +1 -6
  279. endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
  280. endoreg_db/serializers/video/__init__.py +2 -2
  281. endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
  282. endoreg_db/serializers/video/video_file_brief.py +6 -2
  283. endoreg_db/serializers/video/video_file_detail.py +36 -23
  284. endoreg_db/serializers/video/video_file_list.py +4 -2
  285. endoreg_db/serializers/video/video_processing_history.py +54 -50
  286. endoreg_db/services/__init__.py +1 -1
  287. endoreg_db/services/anonymization.py +2 -2
  288. endoreg_db/services/examination_evaluation.py +40 -17
  289. endoreg_db/services/model_meta_from_hf.py +76 -0
  290. endoreg_db/services/polling_coordinator.py +101 -70
  291. endoreg_db/services/pseudonym_service.py +27 -22
  292. endoreg_db/services/report_import.py +6 -3
  293. endoreg_db/services/segment_sync.py +75 -59
  294. endoreg_db/services/video_import.py +6 -7
  295. endoreg_db/urls/__init__.py +2 -2
  296. endoreg_db/urls/ai.py +7 -25
  297. endoreg_db/urls/anonymization.py +61 -15
  298. endoreg_db/urls/auth.py +4 -4
  299. endoreg_db/urls/classification.py +4 -9
  300. endoreg_db/urls/examination.py +27 -18
  301. endoreg_db/urls/media.py +27 -34
  302. endoreg_db/urls/patient.py +11 -7
  303. endoreg_db/urls/requirements.py +3 -1
  304. endoreg_db/urls/root_urls.py +2 -3
  305. endoreg_db/urls/stats.py +24 -16
  306. endoreg_db/urls/upload.py +3 -11
  307. endoreg_db/utils/__init__.py +14 -15
  308. endoreg_db/utils/ai/__init__.py +1 -1
  309. endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
  310. endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
  311. endoreg_db/utils/ai/get.py +2 -1
  312. endoreg_db/utils/ai/inference_dataset.py +14 -15
  313. endoreg_db/utils/ai/model_training/config.py +117 -0
  314. endoreg_db/utils/ai/model_training/dataset.py +74 -0
  315. endoreg_db/utils/ai/model_training/losses.py +68 -0
  316. endoreg_db/utils/ai/model_training/metrics.py +78 -0
  317. endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
  318. endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
  319. endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
  320. endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
  321. endoreg_db/utils/ai/predict.py +4 -4
  322. endoreg_db/utils/ai/preprocess.py +19 -11
  323. endoreg_db/utils/calc_duration_seconds.py +4 -4
  324. endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
  325. endoreg_db/utils/check_video_files.py +74 -47
  326. endoreg_db/utils/cropping.py +10 -9
  327. endoreg_db/utils/dataloader.py +11 -3
  328. endoreg_db/utils/dates.py +3 -4
  329. endoreg_db/utils/defaults/set_default_center.py +7 -6
  330. endoreg_db/utils/env.py +6 -2
  331. endoreg_db/utils/extract_specific_frames.py +24 -9
  332. endoreg_db/utils/file_operations.py +30 -18
  333. endoreg_db/utils/fix_video_path_direct.py +57 -41
  334. endoreg_db/utils/frame_anonymization_utils.py +157 -157
  335. endoreg_db/utils/hashs.py +3 -18
  336. endoreg_db/utils/links/requirement_link.py +96 -52
  337. endoreg_db/utils/ocr.py +30 -25
  338. endoreg_db/utils/operation_log.py +61 -0
  339. endoreg_db/utils/parse_and_generate_yaml.py +12 -13
  340. endoreg_db/utils/paths.py +6 -6
  341. endoreg_db/utils/permissions.py +40 -24
  342. endoreg_db/utils/pipelines/process_video_dir.py +50 -26
  343. endoreg_db/utils/product/sum_emissions.py +5 -3
  344. endoreg_db/utils/product/sum_weights.py +4 -2
  345. endoreg_db/utils/pydantic_models/__init__.py +3 -4
  346. endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
  347. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
  348. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
  349. endoreg_db/utils/setup_config.py +21 -5
  350. endoreg_db/utils/storage.py +3 -1
  351. endoreg_db/utils/translation.py +19 -15
  352. endoreg_db/utils/uuid.py +1 -0
  353. endoreg_db/utils/validate_endo_roi.py +12 -4
  354. endoreg_db/utils/validate_subcategory_dict.py +26 -24
  355. endoreg_db/utils/validate_video_detailed.py +207 -149
  356. endoreg_db/utils/video/__init__.py +7 -3
  357. endoreg_db/utils/video/extract_frames.py +30 -18
  358. endoreg_db/utils/video/names.py +11 -6
  359. endoreg_db/utils/video/streaming_processor.py +175 -101
  360. endoreg_db/utils/video/video_splitter.py +30 -19
  361. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
  362. endoreg_db/views/__init__.py +0 -20
  363. endoreg_db/views/anonymization/__init__.py +6 -2
  364. endoreg_db/views/anonymization/media_management.py +2 -6
  365. endoreg_db/views/anonymization/overview.py +34 -1
  366. endoreg_db/views/anonymization/validate.py +79 -18
  367. endoreg_db/views/auth/__init__.py +1 -1
  368. endoreg_db/views/auth/keycloak.py +16 -14
  369. endoreg_db/views/examination/__init__.py +12 -15
  370. endoreg_db/views/examination/examination.py +5 -5
  371. endoreg_db/views/examination/examination_manifest_cache.py +5 -5
  372. endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
  373. endoreg_db/views/examination/get_finding_classifications.py +9 -7
  374. endoreg_db/views/examination/get_findings.py +8 -10
  375. endoreg_db/views/examination/get_instruments.py +3 -2
  376. endoreg_db/views/examination/get_interventions.py +1 -1
  377. endoreg_db/views/finding/__init__.py +2 -2
  378. endoreg_db/views/finding/finding.py +58 -54
  379. endoreg_db/views/finding/get_classifications.py +1 -1
  380. endoreg_db/views/finding/get_interventions.py +1 -1
  381. endoreg_db/views/finding_classification/__init__.py +5 -5
  382. endoreg_db/views/finding_classification/finding_classification.py +5 -6
  383. endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
  384. endoreg_db/views/media/__init__.py +13 -13
  385. endoreg_db/views/media/pdf_media.py +9 -9
  386. endoreg_db/views/media/sensitive_metadata.py +10 -7
  387. endoreg_db/views/media/video_media.py +4 -4
  388. endoreg_db/views/meta/__init__.py +1 -1
  389. endoreg_db/views/meta/sensitive_meta_list.py +20 -22
  390. endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
  391. endoreg_db/views/misc/__init__.py +6 -34
  392. endoreg_db/views/misc/center.py +2 -1
  393. endoreg_db/views/misc/csrf.py +2 -1
  394. endoreg_db/views/misc/gender.py +2 -1
  395. endoreg_db/views/misc/stats.py +141 -106
  396. endoreg_db/views/patient/__init__.py +1 -3
  397. endoreg_db/views/patient/patient.py +141 -99
  398. endoreg_db/views/patient_examination/__init__.py +5 -5
  399. endoreg_db/views/patient_examination/patient_examination.py +43 -42
  400. endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
  401. endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
  402. endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
  403. endoreg_db/views/patient_examination/video.py +114 -80
  404. endoreg_db/views/patient_finding/__init__.py +1 -1
  405. endoreg_db/views/patient_finding/patient_finding.py +17 -10
  406. endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
  407. endoreg_db/views/patient_finding_classification/__init__.py +1 -1
  408. endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
  409. endoreg_db/views/report/reimport.py +1 -1
  410. endoreg_db/views/report/report_stream.py +5 -8
  411. endoreg_db/views/requirement/__init__.py +2 -1
  412. endoreg_db/views/requirement/evaluate.py +7 -9
  413. endoreg_db/views/requirement/lookup.py +2 -3
  414. endoreg_db/views/requirement/lookup_store.py +0 -1
  415. endoreg_db/views/requirement/requirement_utils.py +2 -4
  416. endoreg_db/views/stats/__init__.py +4 -4
  417. endoreg_db/views/stats/stats_views.py +152 -115
  418. endoreg_db/views/video/__init__.py +18 -27
  419. endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
  420. endoreg_db/views/{ai → video/ai}/label.py +20 -16
  421. endoreg_db/views/video/correction.py +5 -6
  422. endoreg_db/views/video/reimport.py +134 -99
  423. endoreg_db/views/video/segments_crud.py +134 -44
  424. endoreg_db/views/video/video_apply_mask.py +13 -12
  425. endoreg_db/views/video/video_correction.py +2 -1
  426. endoreg_db/views/video/video_download_processed.py +15 -15
  427. endoreg_db/views/video/video_meta_stats.py +7 -6
  428. endoreg_db/views/video/video_processing_history.py +3 -2
  429. endoreg_db/views/video/video_remove_frames.py +13 -12
  430. endoreg_db/views/video/video_stream.py +110 -82
  431. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
  432. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +434 -431
  433. endoreg_db/management/commands/import_fallback_video.py +0 -203
  434. endoreg_db/management/commands/import_video.py +0 -422
  435. endoreg_db/management/commands/import_video_with_classification.py +0 -367
  436. endoreg_db/models/media/processing_history/processing_history.py +0 -96
  437. endoreg_db/serializers/label/__init__.py +0 -7
  438. endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
  439. endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
  440. endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
  441. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
  442. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
  443. endoreg_db/services/__old/pdf_import.py +0 -1487
  444. endoreg_db/services/__old/video_import.py +0 -1306
  445. endoreg_db/tasks/upload_tasks.py +0 -216
  446. endoreg_db/tasks/video_ingest.py +0 -161
  447. endoreg_db/tasks/video_processing_tasks.py +0 -327
  448. endoreg_db/views/misc/translation.py +0 -182
  449. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
  450. {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
@@ -55,7 +55,9 @@ def _add_months(base_date: datetime.date, months: int) -> datetime.date:
55
55
  return base_date.replace(year=year, month=month, day=day)
56
56
 
57
57
 
58
- def _shift_date_by_unit(base_date: datetime.date, value: int, unit: str) -> datetime.date:
58
+ def _shift_date_by_unit(
59
+ base_date: datetime.date, value: int, unit: str
60
+ ) -> datetime.date:
59
61
  """Returns ``base_date`` shifted by ``value`` units using the supported unit set.
60
62
 
61
63
  Hour-level offsets are not supported because ``base_date`` is a ``date``
@@ -65,7 +67,9 @@ def _shift_date_by_unit(base_date: datetime.date, value: int, unit: str) -> date
65
67
  if unit == "days":
66
68
  return base_date + timedelta(days=value)
67
69
  if unit == "hours":
68
- raise NotImplementedError("Hour-level timeframe comparisons require datetime-aware inputs; _shift_date_by_unit only operates on date objects.")
70
+ raise NotImplementedError(
71
+ "Hour-level timeframe comparisons require datetime-aware inputs; _shift_date_by_unit only operates on date objects."
72
+ )
69
73
  if unit == "weeks":
70
74
  return base_date + timedelta(weeks=value)
71
75
  if unit == "months":
@@ -75,7 +79,9 @@ def _shift_date_by_unit(base_date: datetime.date, value: int, unit: str) -> date
75
79
  raise NotImplementedError(f"Timeframe unit '{unit}' is not supported.")
76
80
 
77
81
 
78
- def _is_date_in_timeframe(date_to_check: datetime.date | None, requirement: "Requirement") -> bool:
82
+ def _is_date_in_timeframe(
83
+ date_to_check: datetime.date | None, requirement: "Requirement"
84
+ ) -> bool:
79
85
  """
80
86
  Checks if a given date falls within the timeframe specified by a Requirement.
81
87
 
@@ -107,7 +113,9 @@ def _is_date_in_timeframe(date_to_check: datetime.date | None, requirement: "Req
107
113
 
108
114
  unit = _normalize_timeframe_unit(requirement)
109
115
  if not unit:
110
- raise NotImplementedError("Timeframe unit could not be resolved from requirement's Unit name/abbreviation.")
116
+ raise NotImplementedError(
117
+ "Timeframe unit could not be resolved from requirement's Unit name/abbreviation."
118
+ )
111
119
 
112
120
  today = datetime.date.today()
113
121
  timeframe_start_delta = int(requirement.numeric_value_min)
@@ -122,7 +130,9 @@ def _is_date_in_timeframe(date_to_check: datetime.date | None, requirement: "Req
122
130
  return start_date_bound <= date_to_check <= end_date_bound
123
131
 
124
132
 
125
- def _evaluate_models_match_any(requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs) -> bool:
133
+ def _evaluate_models_match_any(
134
+ requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs
135
+ ) -> bool:
126
136
  """
127
137
  Checks if the requirement_links matches any of the input_links.
128
138
 
@@ -161,7 +171,9 @@ def _evaluate_models_match_any_in_timeframe(
161
171
  # --- Handle PatientEvents ---
162
172
  # Check if the requirement is concerned with events
163
173
  if requirement_links.events: # This list contains Event model instances
164
- required_event_models = set(requirement_links.events) # Target Event models from the Requirement
174
+ required_event_models = set(
175
+ requirement_links.events
176
+ ) # Target Event models from the Requirement
165
177
 
166
178
  # input_links.patient_events contains PatientEvent instances provided as input
167
179
  for patient_event_instance in input_links.patient_events:
@@ -191,7 +203,10 @@ def _evaluate_models_match_any_in_timeframe(
191
203
 
192
204
 
193
205
  def _evaluate_models_match_all_in_timeframe(
194
- requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs
206
+ requirement_links: "RequirementLinks",
207
+ input_links: "RequirementLinks",
208
+ requirement: "Requirement",
209
+ **kwargs,
195
210
  ) -> bool:
196
211
  if not _evaluate_models_match_all(requirement_links, input_links, **kwargs):
197
212
  return False
@@ -211,7 +226,9 @@ def _evaluate_models_match_all_in_timeframe(
211
226
  found_in_timeframe = False
212
227
  for patient_event in patient_events:
213
228
  if getattr(patient_event, "event", None) == event:
214
- if _is_date_in_timeframe(getattr(patient_event, "date", None), requirement):
229
+ if _is_date_in_timeframe(
230
+ getattr(patient_event, "date", None), requirement
231
+ ):
215
232
  found_in_timeframe = True
216
233
  break
217
234
  if not found_in_timeframe:
@@ -223,7 +240,10 @@ def _evaluate_models_match_all_in_timeframe(
223
240
 
224
241
 
225
242
  def _evaluate_models_match_none_in_timeframe(
226
- requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs
243
+ requirement_links: "RequirementLinks",
244
+ input_links: "RequirementLinks",
245
+ requirement: "Requirement",
246
+ **kwargs,
227
247
  ) -> bool:
228
248
  if not requirement_links.events:
229
249
  return True
@@ -239,7 +259,9 @@ def _evaluate_models_match_none_in_timeframe(
239
259
  return True
240
260
 
241
261
 
242
- def _evaluate_models_match_all(requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs) -> bool:
262
+ def _evaluate_models_match_all(
263
+ requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs
264
+ ) -> bool:
243
265
  """
244
266
  Evaluates if all active links in requirement_links are present in input_links.
245
267
 
@@ -256,7 +278,9 @@ def _evaluate_models_match_all(requirement_links: "RequirementLinks", input_link
256
278
  True if all specified items in requirement_links are found in input_links,
257
279
  False otherwise.
258
280
  """
259
- active_req_links = requirement_links.active() # Get dict of non-empty lists from requirement
281
+ active_req_links = (
282
+ requirement_links.active()
283
+ ) # Get dict of non-empty lists from requirement
260
284
 
261
285
  if not active_req_links: # If the requirement specifies no actual items to link
262
286
  return True # Vacuously true, as there are no conditions to fail
@@ -279,7 +303,9 @@ def _evaluate_models_match_all(requirement_links: "RequirementLinks", input_link
279
303
  return True
280
304
 
281
305
 
282
- def _evaluate_models_match_none(requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs) -> bool:
306
+ def _evaluate_models_match_none(
307
+ requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs
308
+ ) -> bool:
283
309
  """Returns True when no required models are present in the input links."""
284
310
  active_req_links = requirement_links.active()
285
311
  if not active_req_links:
@@ -366,28 +392,48 @@ def _count_matching_events_in_timeframe(
366
392
  return matched_events
367
393
 
368
394
 
369
- def _evaluate_models_match_n(requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs) -> bool:
395
+ def _evaluate_models_match_n(
396
+ requirement_links: "RequirementLinks",
397
+ input_links: "RequirementLinks",
398
+ requirement: "Requirement",
399
+ **kwargs,
400
+ ) -> bool:
370
401
  expected = _resolve_expected_count(requirement)
371
402
  if expected is None:
372
403
  return False
373
404
  return _count_matching_models(requirement_links, input_links) == expected
374
405
 
375
406
 
376
- def _evaluate_models_match_n_or_more(requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs) -> bool:
407
+ def _evaluate_models_match_n_or_more(
408
+ requirement_links: "RequirementLinks",
409
+ input_links: "RequirementLinks",
410
+ requirement: "Requirement",
411
+ **kwargs,
412
+ ) -> bool:
377
413
  threshold = _resolve_expected_count(requirement)
378
414
  if threshold is None:
379
415
  return False
380
416
  return _count_matching_models(requirement_links, input_links) >= max(threshold, 0)
381
417
 
382
418
 
383
- def _evaluate_models_match_n_or_less(requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs) -> bool:
419
+ def _evaluate_models_match_n_or_less(
420
+ requirement_links: "RequirementLinks",
421
+ input_links: "RequirementLinks",
422
+ requirement: "Requirement",
423
+ **kwargs,
424
+ ) -> bool:
384
425
  limit = _resolve_expected_count(requirement)
385
426
  if limit is None:
386
427
  return False
387
428
  return _count_matching_models(requirement_links, input_links) <= limit
388
429
 
389
430
 
390
- def _evaluate_models_match_count_in_range(requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs) -> bool:
431
+ def _evaluate_models_match_count_in_range(
432
+ requirement_links: "RequirementLinks",
433
+ input_links: "RequirementLinks",
434
+ requirement: "Requirement",
435
+ **kwargs,
436
+ ) -> bool:
391
437
  if requirement.numeric_value_min is None or requirement.numeric_value_max is None:
392
438
  return False
393
439
 
@@ -400,7 +446,12 @@ def _evaluate_models_match_count_in_range(requirement_links: "RequirementLinks",
400
446
  return lower <= match_count <= upper
401
447
 
402
448
 
403
- def _evaluate_models_match_n_in_timeframe(requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs) -> bool:
449
+ def _evaluate_models_match_n_in_timeframe(
450
+ requirement_links: "RequirementLinks",
451
+ input_links: "RequirementLinks",
452
+ requirement: "Requirement",
453
+ **kwargs,
454
+ ) -> bool:
404
455
  if not _has_timeframe_configuration(requirement):
405
456
  return False
406
457
 
@@ -408,11 +459,17 @@ def _evaluate_models_match_n_in_timeframe(requirement_links: "RequirementLinks",
408
459
  if expected is None:
409
460
  return False
410
461
 
411
- return _count_matching_events_in_timeframe(requirement_links, input_links, requirement) == expected
462
+ return (
463
+ _count_matching_events_in_timeframe(requirement_links, input_links, requirement)
464
+ == expected
465
+ )
412
466
 
413
467
 
414
468
  def _evaluate_models_match_n_or_more_in_timeframe(
415
- requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs
469
+ requirement_links: "RequirementLinks",
470
+ input_links: "RequirementLinks",
471
+ requirement: "Requirement",
472
+ **kwargs,
416
473
  ) -> bool:
417
474
  if not _has_timeframe_configuration(requirement):
418
475
  return False
@@ -421,11 +478,16 @@ def _evaluate_models_match_n_or_more_in_timeframe(
421
478
  if threshold is None:
422
479
  return False
423
480
 
424
- return _count_matching_events_in_timeframe(requirement_links, input_links, requirement) >= max(threshold, 0)
481
+ return _count_matching_events_in_timeframe(
482
+ requirement_links, input_links, requirement
483
+ ) >= max(threshold, 0)
425
484
 
426
485
 
427
486
  def _evaluate_models_match_n_or_less_in_timeframe(
428
- requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs
487
+ requirement_links: "RequirementLinks",
488
+ input_links: "RequirementLinks",
489
+ requirement: "Requirement",
490
+ **kwargs,
429
491
  ) -> bool:
430
492
  if not _has_timeframe_configuration(requirement):
431
493
  return False
@@ -434,10 +496,18 @@ def _evaluate_models_match_n_or_less_in_timeframe(
434
496
  if limit is None:
435
497
  return False
436
498
 
437
- return _count_matching_events_in_timeframe(requirement_links, input_links, requirement) <= limit
499
+ return (
500
+ _count_matching_events_in_timeframe(requirement_links, input_links, requirement)
501
+ <= limit
502
+ )
438
503
 
439
504
 
440
- def _evaluate_age_gte(requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs) -> bool:
505
+ def _evaluate_age_gte(
506
+ requirement_links: "RequirementLinks",
507
+ input_links: "RequirementLinks",
508
+ requirement: "Requirement",
509
+ **kwargs,
510
+ ) -> bool:
441
511
  """
442
512
  Checks if any patient in the input has an age greater than or equal to the requirement's numeric_value.
443
513
 
@@ -465,35 +535,54 @@ def _evaluate_age_gte(requirement_links: "RequirementLinks", input_links: "Requi
465
535
 
466
536
  # Check if we have Patient instances in the original_input_args
467
537
  original_args = kwargs.get("original_input_args", [])
468
- logger.debug(f"age_gte: Found {len(original_args)} original input arguments: {[type(arg).__name__ for arg in original_args]}")
538
+ logger.debug(
539
+ f"age_gte: Found {len(original_args)} original input arguments: {[type(arg).__name__ for arg in original_args]}"
540
+ )
469
541
 
470
542
  for i, arg in enumerate(original_args):
471
543
  logger.debug(f"age_gte: Checking argument {i}: {type(arg).__name__}")
472
544
  if isinstance(arg, Patient):
473
545
  patient_age = arg.age()
474
- logger.debug(f"age_gte: Patient {arg} has age {patient_age}, comparing with min_age {min_age}")
546
+ logger.debug(
547
+ f"age_gte: Patient {arg} has age {patient_age}, comparing with min_age {min_age}"
548
+ )
475
549
  if patient_age is not None and patient_age >= min_age:
476
- logger.debug(f"age_gte: Patient age {patient_age} >= {min_age}, returning True")
550
+ logger.debug(
551
+ f"age_gte: Patient age {patient_age} >= {min_age}, returning True"
552
+ )
477
553
  return True
478
554
  else:
479
- logger.debug(f"age_gte: Patient age {patient_age} < {min_age} or is None")
555
+ logger.debug(
556
+ f"age_gte: Patient age {patient_age} < {min_age} or is None"
557
+ )
480
558
  # Handle QuerySets of patients
481
559
  elif hasattr(arg, "model") and issubclass(arg.model, Patient):
482
560
  logger.debug(f"age_gte: Found Patient QuerySet with {arg.count()} patients")
483
561
  for patient in arg:
484
562
  patient_age = patient.age()
485
- logger.debug(f"age_gte: Patient {patient} has age {patient_age}, comparing with min_age {min_age}")
563
+ logger.debug(
564
+ f"age_gte: Patient {patient} has age {patient_age}, comparing with min_age {min_age}"
565
+ )
486
566
  if patient_age is not None and patient_age >= min_age:
487
- logger.debug(f"age_gte: Patient age {patient_age} >= {min_age}, returning True")
567
+ logger.debug(
568
+ f"age_gte: Patient age {patient_age} >= {min_age}, returning True"
569
+ )
488
570
  return True
489
571
  else:
490
- logger.debug(f"age_gte: Argument {i} is not a Patient or Patient QuerySet: {type(arg)}")
572
+ logger.debug(
573
+ f"age_gte: Argument {i} is not a Patient or Patient QuerySet: {type(arg)}"
574
+ )
491
575
 
492
576
  logger.debug(f"age_gte: No patient found with age >= {min_age}, returning False")
493
577
  return False
494
578
 
495
579
 
496
- def _evaluate_age_lte(requirement_links: "RequirementLinks", input_links: "RequirementLinks", requirement: "Requirement", **kwargs) -> bool:
580
+ def _evaluate_age_lte(
581
+ requirement_links: "RequirementLinks",
582
+ input_links: "RequirementLinks",
583
+ requirement: "Requirement",
584
+ **kwargs,
585
+ ) -> bool:
497
586
  """
498
587
  Checks if any patient in the input has an age less than or equal to the requirement's numeric_value.
499
588
 
@@ -530,7 +619,12 @@ def _evaluate_age_lte(requirement_links: "RequirementLinks", input_links: "Requi
530
619
  return False
531
620
 
532
621
 
533
- def dispatch_operator_evaluation(operator_name: str, requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs) -> bool:
622
+ def dispatch_operator_evaluation(
623
+ operator_name: str,
624
+ requirement_links: "RequirementLinks",
625
+ input_links: "RequirementLinks",
626
+ **kwargs,
627
+ ) -> bool:
534
628
  """
535
629
  Dispatches the evaluation to the appropriate function based on the operator name.
536
630
 
@@ -547,29 +641,43 @@ def dispatch_operator_evaluation(operator_name: str, requirement_links: "Require
547
641
  Raises:
548
642
  NotImplementedError: If the evaluation logic for the operator's name is not implemented.
549
643
  """
550
- from endoreg_db.models.requirement.requirement import Requirement # Runtime import for isinstance
644
+ from endoreg_db.models.requirement.requirement import (
645
+ Requirement,
646
+ ) # Runtime import for isinstance
551
647
 
552
648
  from .lab_value_operators import LAB_VALUE_OPERATOR_FUNCTIONS
553
649
 
554
650
  eval_func = None
555
- requirement = kwargs.get("requirement") # Get requirement for operators that need it
651
+ requirement = kwargs.get(
652
+ "requirement"
653
+ ) # Get requirement for operators that need it
556
654
 
557
655
  def _kwargs_without_requirement() -> dict:
558
656
  return {k: v for k, v in kwargs.items() if k != "requirement"}
559
657
 
560
658
  if operator_name == "models_match_any":
561
659
  eval_func = _evaluate_models_match_any
562
- return eval_func(requirement_links=requirement_links, input_links=input_links, **kwargs)
660
+ return eval_func(
661
+ requirement_links=requirement_links, input_links=input_links, **kwargs
662
+ )
563
663
  elif operator_name == "models_match_all":
564
664
  eval_func = _evaluate_models_match_all
565
- return eval_func(requirement_links=requirement_links, input_links=input_links, **kwargs)
665
+ return eval_func(
666
+ requirement_links=requirement_links, input_links=input_links, **kwargs
667
+ )
566
668
  elif operator_name == "models_match_none":
567
669
  eval_func = _evaluate_models_match_none
568
- return eval_func(requirement_links=requirement_links, input_links=input_links, **kwargs)
670
+ return eval_func(
671
+ requirement_links=requirement_links, input_links=input_links, **kwargs
672
+ )
569
673
  elif operator_name == "models_match_any_in_timeframe":
570
674
  # 'requirement' is already extracted from kwargs via requirement = kwargs.get("requirement")
571
- if not isinstance(requirement, Requirement): # Ensure requirement is present and correct type
572
- raise ValueError("models_match_any_in_timeframe operator requires a valid 'requirement' instance in kwargs.")
675
+ if not isinstance(
676
+ requirement, Requirement
677
+ ): # Ensure requirement is present and correct type
678
+ raise ValueError(
679
+ "models_match_any_in_timeframe operator requires a valid 'requirement' instance in kwargs."
680
+ )
573
681
  kwargs_for_eval = _kwargs_without_requirement()
574
682
  eval_func = _evaluate_models_match_any_in_timeframe
575
683
  return eval_func(
@@ -580,76 +688,155 @@ def dispatch_operator_evaluation(operator_name: str, requirement_links: "Require
580
688
  )
581
689
  elif operator_name == "models_match_all_in_timeframe":
582
690
  if not isinstance(requirement, Requirement):
583
- raise ValueError("models_match_all_in_timeframe operator requires a valid 'requirement' instance in kwargs.")
691
+ raise ValueError(
692
+ "models_match_all_in_timeframe operator requires a valid 'requirement' instance in kwargs."
693
+ )
584
694
  kwargs_for_eval = _kwargs_without_requirement()
585
- return _evaluate_models_match_all_in_timeframe(requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval)
695
+ return _evaluate_models_match_all_in_timeframe(
696
+ requirement_links=requirement_links,
697
+ input_links=input_links,
698
+ requirement=requirement,
699
+ **kwargs_for_eval,
700
+ )
586
701
  elif operator_name == "models_match_none_in_timeframe":
587
702
  if not isinstance(requirement, Requirement):
588
- raise ValueError("models_match_none_in_timeframe operator requires a valid 'requirement' instance in kwargs.")
703
+ raise ValueError(
704
+ "models_match_none_in_timeframe operator requires a valid 'requirement' instance in kwargs."
705
+ )
589
706
  kwargs_for_eval = _kwargs_without_requirement()
590
707
  return _evaluate_models_match_none_in_timeframe(
591
- requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval
708
+ requirement_links=requirement_links,
709
+ input_links=input_links,
710
+ requirement=requirement,
711
+ **kwargs_for_eval,
592
712
  )
593
713
  elif operator_name == "models_match_n":
594
714
  if not isinstance(requirement, Requirement):
595
- raise ValueError("models_match_n operator requires a valid 'requirement' instance in kwargs.")
715
+ raise ValueError(
716
+ "models_match_n operator requires a valid 'requirement' instance in kwargs."
717
+ )
596
718
  kwargs_for_eval = _kwargs_without_requirement()
597
- return _evaluate_models_match_n(requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval)
719
+ return _evaluate_models_match_n(
720
+ requirement_links=requirement_links,
721
+ input_links=input_links,
722
+ requirement=requirement,
723
+ **kwargs_for_eval,
724
+ )
598
725
  elif operator_name == "models_match_n_or_more":
599
726
  if not isinstance(requirement, Requirement):
600
- raise ValueError("models_match_n_or_more operator requires a valid 'requirement' instance in kwargs.")
727
+ raise ValueError(
728
+ "models_match_n_or_more operator requires a valid 'requirement' instance in kwargs."
729
+ )
601
730
  kwargs_for_eval = _kwargs_without_requirement()
602
- return _evaluate_models_match_n_or_more(requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval)
731
+ return _evaluate_models_match_n_or_more(
732
+ requirement_links=requirement_links,
733
+ input_links=input_links,
734
+ requirement=requirement,
735
+ **kwargs_for_eval,
736
+ )
603
737
  elif operator_name == "models_match_n_or_less":
604
738
  if not isinstance(requirement, Requirement):
605
- raise ValueError("models_match_n_or_less operator requires a valid 'requirement' instance in kwargs.")
739
+ raise ValueError(
740
+ "models_match_n_or_less operator requires a valid 'requirement' instance in kwargs."
741
+ )
606
742
  kwargs_for_eval = _kwargs_without_requirement()
607
- return _evaluate_models_match_n_or_less(requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval)
743
+ return _evaluate_models_match_n_or_less(
744
+ requirement_links=requirement_links,
745
+ input_links=input_links,
746
+ requirement=requirement,
747
+ **kwargs_for_eval,
748
+ )
608
749
  elif operator_name == "models_match_count_in_range":
609
750
  if not isinstance(requirement, Requirement):
610
- raise ValueError("models_match_count_in_range operator requires a valid 'requirement' instance in kwargs.")
751
+ raise ValueError(
752
+ "models_match_count_in_range operator requires a valid 'requirement' instance in kwargs."
753
+ )
611
754
  kwargs_for_eval = _kwargs_without_requirement()
612
- return _evaluate_models_match_count_in_range(requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval)
755
+ return _evaluate_models_match_count_in_range(
756
+ requirement_links=requirement_links,
757
+ input_links=input_links,
758
+ requirement=requirement,
759
+ **kwargs_for_eval,
760
+ )
613
761
  elif operator_name == "models_match_n_in_timeframe":
614
762
  if not isinstance(requirement, Requirement):
615
- raise ValueError("models_match_n_in_timeframe operator requires a valid 'requirement' instance in kwargs.")
763
+ raise ValueError(
764
+ "models_match_n_in_timeframe operator requires a valid 'requirement' instance in kwargs."
765
+ )
616
766
  kwargs_for_eval = _kwargs_without_requirement()
617
- return _evaluate_models_match_n_in_timeframe(requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval)
767
+ return _evaluate_models_match_n_in_timeframe(
768
+ requirement_links=requirement_links,
769
+ input_links=input_links,
770
+ requirement=requirement,
771
+ **kwargs_for_eval,
772
+ )
618
773
  elif operator_name == "models_match_n_or_more_in_timeframe":
619
774
  if not isinstance(requirement, Requirement):
620
- raise ValueError("models_match_n_or_more_in_timeframe operator requires a valid 'requirement' instance in kwargs.")
775
+ raise ValueError(
776
+ "models_match_n_or_more_in_timeframe operator requires a valid 'requirement' instance in kwargs."
777
+ )
621
778
  kwargs_for_eval = _kwargs_without_requirement()
622
779
  return _evaluate_models_match_n_or_more_in_timeframe(
623
- requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval
780
+ requirement_links=requirement_links,
781
+ input_links=input_links,
782
+ requirement=requirement,
783
+ **kwargs_for_eval,
624
784
  )
625
785
  elif operator_name == "models_match_n_or_less_in_timeframe":
626
786
  if not isinstance(requirement, Requirement):
627
- raise ValueError("models_match_n_or_less_in_timeframe operator requires a valid 'requirement' instance in kwargs.")
787
+ raise ValueError(
788
+ "models_match_n_or_less_in_timeframe operator requires a valid 'requirement' instance in kwargs."
789
+ )
628
790
  kwargs_for_eval = _kwargs_without_requirement()
629
791
  return _evaluate_models_match_n_or_less_in_timeframe(
630
- requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval
792
+ requirement_links=requirement_links,
793
+ input_links=input_links,
794
+ requirement=requirement,
795
+ **kwargs_for_eval,
631
796
  )
632
797
  elif operator_name in LAB_VALUE_OPERATOR_FUNCTIONS:
633
- if not isinstance(requirement, Requirement): # Ensure requirement is present and correct type
634
- raise ValueError(f"Lab value operator '{operator_name}' requires a valid 'requirement' instance in kwargs.")
798
+ if not isinstance(
799
+ requirement, Requirement
800
+ ): # Ensure requirement is present and correct type
801
+ raise ValueError(
802
+ f"Lab value operator '{operator_name}' requires a valid 'requirement' instance in kwargs."
803
+ )
635
804
 
636
805
  eval_func = LAB_VALUE_OPERATOR_FUNCTIONS[operator_name]
637
- return eval_func(input_links=input_links, requirement=requirement, operator_kwargs=kwargs)
806
+ return eval_func(
807
+ input_links=input_links, requirement=requirement, operator_kwargs=kwargs
808
+ )
638
809
  elif operator_name == "age_gte":
639
810
  if not isinstance(requirement, Requirement):
640
- raise ValueError("age_gte operator requires a valid 'requirement' instance in kwargs.")
811
+ raise ValueError(
812
+ "age_gte operator requires a valid 'requirement' instance in kwargs."
813
+ )
641
814
 
642
815
  # Create a new kwargs dict for the call, excluding 'requirement' to avoid passing it twice
643
816
  kwargs_for_eval = {k: v for k, v in kwargs.items() if k != "requirement"}
644
817
 
645
- return _evaluate_age_gte(requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval)
818
+ return _evaluate_age_gte(
819
+ requirement_links=requirement_links,
820
+ input_links=input_links,
821
+ requirement=requirement,
822
+ **kwargs_for_eval,
823
+ )
646
824
  elif operator_name == "age_lte":
647
825
  if not isinstance(requirement, Requirement):
648
- raise ValueError("age_lte operator requires a valid 'requirement' instance in kwargs.")
826
+ raise ValueError(
827
+ "age_lte operator requires a valid 'requirement' instance in kwargs."
828
+ )
649
829
 
650
830
  # Create a new kwargs dict for the call, excluding 'requirement' to avoid passing it twice
651
831
  kwargs_for_eval = {k: v for k, v in kwargs.items() if k != "requirement"}
652
832
 
653
- return _evaluate_age_lte(requirement_links=requirement_links, input_links=input_links, requirement=requirement, **kwargs_for_eval)
833
+ return _evaluate_age_lte(
834
+ requirement_links=requirement_links,
835
+ input_links=input_links,
836
+ requirement=requirement,
837
+ **kwargs_for_eval,
838
+ )
654
839
  else:
655
- raise NotImplementedError(f"Evaluation logic for operator '{operator_name}' is not implemented.")
840
+ raise NotImplementedError(
841
+ f"Evaluation logic for operator '{operator_name}' is not implemented."
842
+ )
@@ -1,11 +1,14 @@
1
- from typing import TYPE_CHECKING, Any, Dict, Tuple, Union
1
+ from typing import TYPE_CHECKING, Any, Tuple, Union
2
2
 
3
3
  if TYPE_CHECKING:
4
4
  from endoreg_db.models import Patient, PatientExamination, Requirement
5
- from endoreg_db.models.requirement.requirement_operator import OperatorInstructions, RequirementOperator
5
+ from endoreg_db.models.requirement.requirement_operator import OperatorInstructions
6
6
 
7
7
 
8
- def fetch_input_target(input_object: Union["Patient", "PatientExamination"], operator_instructions: "OperatorInstructions") -> Tuple[str, Any]:
8
+ def fetch_input_target(
9
+ input_object: Union["Patient", "PatientExamination"],
10
+ operator_instructions: "OperatorInstructions",
11
+ ) -> Tuple[str, Any]:
9
12
  input_target_names = operator_instructions.input_targets
10
13
 
11
14
  # Iterate over targets and stop with first successful fetch
@@ -20,7 +23,9 @@ def fetch_input_target(input_object: Union["Patient", "PatientExamination"], ope
20
23
  except AttributeError:
21
24
  continue # Try next input target
22
25
 
23
- raise AttributeError(f"None of the input targets {input_target_names} could be resolved on the input object.")
26
+ raise AttributeError(
27
+ f"None of the input targets {input_target_names} could be resolved on the input object."
28
+ )
24
29
 
25
30
 
26
31
  def fetch_requirement_targets(
@@ -35,7 +40,9 @@ def fetch_requirement_targets(
35
40
  target_value = getattr(requirement, target_name)
36
41
  target_values[target_name] = target_value
37
42
  except AttributeError:
38
- raise AttributeError(f"Requirement does not have attribute '{target_name}'.")
43
+ raise AttributeError(
44
+ f"Requirement does not have attribute '{target_name}'."
45
+ )
39
46
  target_values[target_name] = target_value
40
47
 
41
48
  return target_values
@@ -46,7 +53,9 @@ def model_attribute_set_any(
46
53
  operator_instructions: "OperatorInstructions",
47
54
  requirement: "Requirement",
48
55
  ) -> bool:
49
- input_target_name, input_value = fetch_input_target(input_object, operator_instructions)
56
+ input_target_name, input_value = fetch_input_target(
57
+ input_object, operator_instructions
58
+ )
50
59
 
51
60
  if not input_value:
52
61
  return False
@@ -59,18 +68,24 @@ def model_attribute_numeric_in_range(
59
68
  operator_instructions: "OperatorInstructions",
60
69
  requirement: "Requirement",
61
70
  ) -> bool:
62
- input_target_name, input_value = fetch_input_target(input_object, operator_instructions)
71
+ input_target_name, input_value = fetch_input_target(
72
+ input_object, operator_instructions
73
+ )
63
74
 
64
75
  # make sure, input_value is numeric
65
76
  try:
66
77
  numeric_value = float(input_value)
67
78
  except (TypeError, ValueError):
68
- raise ValueError(f"Input value for target '{input_target_name}' is not numeric: {input_value}")
79
+ raise ValueError(
80
+ f"Input value for target '{input_target_name}' is not numeric: {input_value}"
81
+ )
69
82
 
70
83
  _min = requirement.numeric_value_min
71
84
  _max = requirement.numeric_value_max
72
85
 
73
- assert _min is not None and _max is not None, "Numeric range requires both min and max values to be set."
86
+ assert _min is not None and _max is not None, (
87
+ "Numeric range requires both min and max values to be set."
88
+ )
74
89
 
75
90
  return_value = _min <= numeric_value <= _max
76
91
  return return_value
@@ -81,7 +96,9 @@ def model_attribute_is_among_values(
81
96
  operator_instructions: "OperatorInstructions",
82
97
  requirement: "Requirement",
83
98
  ) -> bool:
84
- input_target_name, input_value = fetch_input_target(input_object, operator_instructions)
99
+ input_target_name, input_value = fetch_input_target(
100
+ input_object, operator_instructions
101
+ )
85
102
  target_values = fetch_requirement_targets(requirement, operator_instructions)
86
103
 
87
104
  for target_name, target_values in target_values.items():