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
@@ -1,24 +1,30 @@
1
1
  from typing import TYPE_CHECKING, List, Optional, Callable, Dict, Any
2
- import re # Added import
2
+ import re # Added import
3
3
  from endoreg_db.models.medical.patient.patient_lab_value import PatientLabValue
4
4
  from endoreg_db.models.requirement.requirement import Requirement
5
5
  from datetime import datetime, timedelta
6
6
 
7
7
  if TYPE_CHECKING:
8
- from endoreg_db.utils.links.requirement_link import RequirementLinks # Added import
8
+ from endoreg_db.utils.links.requirement_link import RequirementLinks # Added import
9
9
  # from endoreg_db.models.requirement.requirement_operator import RequirementOperator # No longer directly used here
10
10
 
11
- def get_latest_patient_lab_value(patient_lab_values: List[PatientLabValue], lab_value_name: str) -> Optional[PatientLabValue]:
11
+
12
+ def get_latest_patient_lab_value(
13
+ patient_lab_values: List[PatientLabValue], lab_value_name: str
14
+ ) -> Optional[PatientLabValue]:
12
15
  """
13
16
  Retrieves the most recent PatientLabValue for a specific lab_value_name.
14
17
  """
15
18
  relevant_values = [
16
- plv for plv in patient_lab_values if plv.lab_value and plv.lab_value.name == lab_value_name
19
+ plv
20
+ for plv in patient_lab_values
21
+ if plv.lab_value and plv.lab_value.name == lab_value_name
17
22
  ]
18
23
  if not relevant_values:
19
24
  return None
20
25
  return max(relevant_values, key=lambda plv: plv.datetime)
21
26
 
27
+
22
28
  def get_patient_lab_values_in_timeframe(
23
29
  patient_lab_values: List[PatientLabValue],
24
30
  lab_value_name: str,
@@ -32,7 +38,9 @@ def get_patient_lab_values_in_timeframe(
32
38
  days_max: e.g., 0 for today (end of timeframe)
33
39
  """
34
40
  relevant_values = [
35
- plv for plv in patient_lab_values if plv.lab_value and plv.lab_value.name == lab_value_name
41
+ plv
42
+ for plv in patient_lab_values
43
+ if plv.lab_value and plv.lab_value.name == lab_value_name
36
44
  ]
37
45
  if not relevant_values:
38
46
  return []
@@ -57,78 +65,90 @@ def get_patient_lab_values_in_timeframe(
57
65
 
58
66
 
59
67
  def lab_latest_numeric_increased(
60
- input_links: "RequirementLinks", # Changed
68
+ input_links: "RequirementLinks", # Changed
61
69
  requirement: Requirement,
62
- operator_kwargs: Dict[str, Any]
70
+ operator_kwargs: Dict[str, Any],
63
71
  ) -> bool:
64
72
  """
65
73
  Returns True if the latest numeric lab value for all required lab values is above the normal maximum range; otherwise returns False.
66
-
74
+
67
75
  Returns False if any required lab value is missing, the latest value is unavailable, or the value does not exceed the normal maximum.
68
76
  """
69
77
  patient_lab_values = input_links.patient_lab_values
70
78
  if not requirement.lab_values.exists():
71
79
  return False
72
80
  for lab_value_model in requirement.lab_values.all():
73
- latest_plv = get_latest_patient_lab_value(patient_lab_values, lab_value_model.name)
81
+ latest_plv = get_latest_patient_lab_value(
82
+ patient_lab_values, lab_value_model.name
83
+ )
74
84
  if not (latest_plv and latest_plv.value is not None and latest_plv.lab_value):
75
85
  return False
76
86
  patient_context = input_links.get_first_patient()
77
87
  normal_range = latest_plv.lab_value.get_normal_range(
78
88
  age=patient_context.age() if patient_context else None,
79
- gender=patient_context.gender if patient_context else None
89
+ gender=patient_context.gender if patient_context else None,
80
90
  )
81
91
  if normal_range.get("max") is None or latest_plv.value <= normal_range["max"]:
82
92
  return False
83
93
  return True
84
94
 
95
+
85
96
  def lab_latest_numeric_decreased(
86
- input_links: "RequirementLinks", # Changed
97
+ input_links: "RequirementLinks", # Changed
87
98
  requirement: Requirement,
88
- operator_kwargs: Dict[str, Any]
99
+ operator_kwargs: Dict[str, Any],
89
100
  ) -> bool:
90
101
  """
91
102
  Returns True if the latest numeric lab value for all required lab values is below the normal minimum range.
92
-
103
+
93
104
  Returns False if any latest value is missing, lacks a normal range, or is not below the minimum.
94
105
  """
95
106
  patient_lab_values = input_links.patient_lab_values
96
- if not requirement.lab_values.exists(): # Changed
107
+ if not requirement.lab_values.exists(): # Changed
97
108
  return False
98
109
  for lab_value_model in requirement.lab_values.all():
99
- latest_plv = get_latest_patient_lab_value(patient_lab_values, lab_value_model.name)
100
- if not (latest_plv and latest_plv.value is not None and latest_plv.lab_value): # Added check for latest_plv.lab_value
110
+ latest_plv = get_latest_patient_lab_value(
111
+ patient_lab_values, lab_value_model.name
112
+ )
113
+ if not (
114
+ latest_plv and latest_plv.value is not None and latest_plv.lab_value
115
+ ): # Added check for latest_plv.lab_value
101
116
  return False
102
117
  patient_context = input_links.get_first_patient()
103
118
  normal_range = latest_plv.lab_value.get_normal_range(
104
- age=patient_context.age() if patient_context else None,
105
- gender=patient_context.gender if patient_context else None
119
+ age=patient_context.age() if patient_context else None,
120
+ gender=patient_context.gender if patient_context else None,
106
121
  )
107
122
  if normal_range.get("min") is None or latest_plv.value >= normal_range["min"]:
108
123
  return False
109
124
  return True
110
125
 
126
+
111
127
  def lab_latest_numeric_normal(
112
- input_links: "RequirementLinks", # Changed
128
+ input_links: "RequirementLinks", # Changed
113
129
  requirement: Requirement,
114
- operator_kwargs: Dict[str, Any]
130
+ operator_kwargs: Dict[str, Any],
115
131
  ) -> bool:
116
132
  """
117
133
  Returns True if the latest numeric lab value for all required lab values is within the normal range.
118
-
134
+
119
135
  Returns False if any latest value is missing, lacks a normal range, or falls outside the normal range.
120
136
  """
121
137
  patient_lab_values = input_links.patient_lab_values
122
- if not requirement.lab_values.exists(): # Changed
138
+ if not requirement.lab_values.exists(): # Changed
123
139
  return False
124
140
  for lab_value_model in requirement.lab_values.all():
125
- latest_plv = get_latest_patient_lab_value(patient_lab_values, lab_value_model.name)
126
- if not (latest_plv and latest_plv.value is not None and latest_plv.lab_value): # Added check for latest_plv.lab_value
141
+ latest_plv = get_latest_patient_lab_value(
142
+ patient_lab_values, lab_value_model.name
143
+ )
144
+ if not (
145
+ latest_plv and latest_plv.value is not None and latest_plv.lab_value
146
+ ): # Added check for latest_plv.lab_value
127
147
  return False
128
148
  patient_context = input_links.get_first_patient()
129
149
  normal_range = latest_plv.lab_value.get_normal_range(
130
- age=patient_context.age() if patient_context else None,
131
- gender=patient_context.gender if patient_context else None
150
+ age=patient_context.age() if patient_context else None,
151
+ gender=patient_context.gender if patient_context else None,
132
152
  )
133
153
  min_val = normal_range.get("min")
134
154
  max_val = normal_range.get("max")
@@ -138,54 +158,66 @@ def lab_latest_numeric_normal(
138
158
  return False
139
159
  return True
140
160
 
161
+
141
162
  def lab_latest_numeric_lower_than_value(
142
- input_links: "RequirementLinks", # Changed
163
+ input_links: "RequirementLinks", # Changed
143
164
  requirement: Requirement,
144
- operator_kwargs: Dict[str, Any]
165
+ operator_kwargs: Dict[str, Any],
145
166
  ) -> bool:
146
167
  """
147
168
  Returns True if the latest numeric lab value for all required lab values is lower than the specified threshold.
148
-
169
+
149
170
  Returns False if any latest value is missing or not below the requirement's numeric value.
150
171
  """
151
172
  patient_lab_values = input_links.patient_lab_values
152
- if not requirement.lab_values.exists() or requirement.numeric_value is None: # Changed
173
+ if (
174
+ not requirement.lab_values.exists() or requirement.numeric_value is None
175
+ ): # Changed
153
176
  return False
154
177
  for lab_value_model in requirement.lab_values.all():
155
- latest_plv = get_latest_patient_lab_value(patient_lab_values, lab_value_model.name)
178
+ latest_plv = get_latest_patient_lab_value(
179
+ patient_lab_values, lab_value_model.name
180
+ )
156
181
  if not (latest_plv and latest_plv.value is not None):
157
182
  return False
158
183
  if not (latest_plv.value < requirement.numeric_value):
159
184
  return False
160
185
  return True
161
186
 
187
+
162
188
  def lab_latest_numeric_greater_than_value(
163
- input_links: "RequirementLinks", # Changed
189
+ input_links: "RequirementLinks", # Changed
164
190
  requirement: Requirement,
165
- operator_kwargs: Dict[str, Any]
191
+ operator_kwargs: Dict[str, Any],
166
192
  ) -> bool:
167
193
  """
168
194
  Returns True if the latest numeric lab value for all required lab values is greater than the specified threshold.
169
-
195
+
170
196
  Returns False if any latest value is missing or not greater than the requirement's numeric value.
171
197
  """
172
198
  patient_lab_values = input_links.patient_lab_values
173
- if not requirement.lab_values.exists() or requirement.numeric_value is None: # Changed
199
+ if (
200
+ not requirement.lab_values.exists() or requirement.numeric_value is None
201
+ ): # Changed
174
202
  return False
175
203
  for lab_value_model in requirement.lab_values.all():
176
- latest_plv = get_latest_patient_lab_value(patient_lab_values, lab_value_model.name)
204
+ latest_plv = get_latest_patient_lab_value(
205
+ patient_lab_values, lab_value_model.name
206
+ )
177
207
  if not (latest_plv and latest_plv.value is not None):
178
208
  return False
179
209
  if not (latest_plv.value > requirement.numeric_value):
180
210
  return False
181
211
  return True
182
212
 
213
+
183
214
  # --- Operators with timeframe ---
184
215
 
216
+
185
217
  def lab_latest_numeric_increased_factor_in_timeframe(
186
- input_links: "RequirementLinks", # Changed
218
+ input_links: "RequirementLinks", # Changed
187
219
  requirement: Requirement,
188
- operator_kwargs: Dict[str, Any]
220
+ operator_kwargs: Dict[str, Any],
189
221
  ) -> bool:
190
222
  """
191
223
  Checks if the numeric lab value has increased by a certain factor within a timeframe.
@@ -197,21 +229,29 @@ def lab_latest_numeric_increased_factor_in_timeframe(
197
229
  More sophisticated checks (e.g., sustained increase, comparison to earliest value in overall history) might require different logic.
198
230
  """
199
231
  patient_lab_values = input_links.patient_lab_values
200
- if (not requirement.lab_values.exists() or
201
- requirement.numeric_value is None or
202
- requirement.numeric_value_min is None or
203
- requirement.numeric_value_max is None):
232
+ if (
233
+ not requirement.lab_values.exists()
234
+ or requirement.numeric_value is None
235
+ or requirement.numeric_value_min is None
236
+ or requirement.numeric_value_max is None
237
+ ):
204
238
  return False
205
239
 
206
240
  factor = requirement.numeric_value
207
- days_min = int(requirement.numeric_value_min) # Start of timeframe
208
- days_max = int(requirement.numeric_value_max) # End of timeframe
241
+ days_min = int(requirement.numeric_value_min) # Start of timeframe
242
+ days_max = int(requirement.numeric_value_max) # End of timeframe
209
243
 
210
244
  for lab_value_model in requirement.lab_values.all():
211
245
  # Get all values for this lab type, not just within the timeframe initially
212
246
  all_lab_values_for_type = sorted(
213
- [plv for plv in patient_lab_values if plv.lab_value and plv.lab_value.name == lab_value_model.name and plv.value is not None],
214
- key=lambda plv: plv.datetime
247
+ [
248
+ plv
249
+ for plv in patient_lab_values
250
+ if plv.lab_value
251
+ and plv.lab_value.name == lab_value_model.name
252
+ and plv.value is not None
253
+ ],
254
+ key=lambda plv: plv.datetime,
215
255
  )
216
256
 
217
257
  if not all_lab_values_for_type:
@@ -224,10 +264,11 @@ def lab_latest_numeric_increased_factor_in_timeframe(
224
264
  if plv.datetime.date() <= start_of_timeframe_date:
225
265
  reference_plv = plv
226
266
  break
227
-
228
- if reference_plv is None and all_lab_values_for_type: # if no value before timeframe, use earliest available
229
- reference_plv = all_lab_values_for_type[0]
230
267
 
268
+ if (
269
+ reference_plv is None and all_lab_values_for_type
270
+ ): # if no value before timeframe, use earliest available
271
+ reference_plv = all_lab_values_for_type[0]
231
272
 
232
273
  if reference_plv and reference_plv.value is not None:
233
274
  reference_value = reference_plv.value
@@ -236,14 +277,17 @@ def lab_latest_numeric_increased_factor_in_timeframe(
236
277
  patient_lab_values, lab_value_model.name, days_min, days_max
237
278
  )
238
279
  for plv_in_frame in values_in_timeframe:
239
- if plv_in_frame.value is not None and plv_in_frame.value > (reference_value * factor):
240
- return True # Found a value increased by the factor
280
+ if plv_in_frame.value is not None and plv_in_frame.value > (
281
+ reference_value * factor
282
+ ):
283
+ return True # Found a value increased by the factor
241
284
  return False
242
285
 
286
+
243
287
  def lab_latest_numeric_decreased_factor_in_timeframe(
244
288
  input_links: "RequirementLinks",
245
289
  requirement: Requirement,
246
- operator_kwargs: Dict[str, Any]
290
+ operator_kwargs: Dict[str, Any],
247
291
  ) -> bool:
248
292
  """
249
293
  Checks if the numeric lab value has decreased by a certain factor within a timeframe.
@@ -251,10 +295,12 @@ def lab_latest_numeric_decreased_factor_in_timeframe(
251
295
  Compares values in timeframe to the value at the start of (or just before) the timeframe.
252
296
  """
253
297
  patient_lab_values = input_links.patient_lab_values
254
- if (not requirement.lab_values.exists() or
255
- requirement.numeric_value is None or
256
- requirement.numeric_value_min is None or
257
- requirement.numeric_value_max is None):
298
+ if (
299
+ not requirement.lab_values.exists()
300
+ or requirement.numeric_value is None
301
+ or requirement.numeric_value_min is None
302
+ or requirement.numeric_value_max is None
303
+ ):
258
304
  return False
259
305
 
260
306
  factor = requirement.numeric_value
@@ -263,8 +309,14 @@ def lab_latest_numeric_decreased_factor_in_timeframe(
263
309
 
264
310
  for lab_value_model in requirement.lab_values.all():
265
311
  all_lab_values_for_type = sorted(
266
- [plv for plv in patient_lab_values if plv.lab_value and plv.lab_value.name == lab_value_model.name and plv.value is not None],
267
- key=lambda plv: plv.datetime
312
+ [
313
+ plv
314
+ for plv in patient_lab_values
315
+ if plv.lab_value
316
+ and plv.lab_value.name == lab_value_model.name
317
+ and plv.value is not None
318
+ ],
319
+ key=lambda plv: plv.datetime,
268
320
  )
269
321
  if not all_lab_values_for_type:
270
322
  continue
@@ -280,29 +332,36 @@ def lab_latest_numeric_decreased_factor_in_timeframe(
280
332
 
281
333
  if reference_plv and reference_plv.value is not None:
282
334
  reference_value = reference_plv.value
283
- if reference_value == 0: # Avoid division by zero or issues with zero reference
284
- continue
335
+ if (
336
+ reference_value == 0
337
+ ): # Avoid division by zero or issues with zero reference
338
+ continue
285
339
  values_in_timeframe = get_patient_lab_values_in_timeframe(
286
340
  patient_lab_values, lab_value_model.name, days_min, days_max
287
341
  )
288
342
  for plv_in_frame in values_in_timeframe:
289
- if plv_in_frame.value is not None and plv_in_frame.value < (reference_value / factor): # Decreased by factor
343
+ if plv_in_frame.value is not None and plv_in_frame.value < (
344
+ reference_value / factor
345
+ ): # Decreased by factor
290
346
  return True
291
347
  return False
292
348
 
349
+
293
350
  def lab_latest_numeric_normal_in_timeframe(
294
351
  input_links: "RequirementLinks",
295
352
  requirement: Requirement,
296
- operator_kwargs: Dict[str, Any]
353
+ operator_kwargs: Dict[str, Any],
297
354
  ) -> bool:
298
355
  """
299
356
  Checks if any numeric lab value within a timeframe is within its normal range.
300
357
  Timeframe in requirement.numeric_value_min/max.
301
358
  """
302
359
  patient_lab_values = input_links.patient_lab_values
303
- if (not requirement.lab_values.exists() or
304
- requirement.numeric_value_min is None or
305
- requirement.numeric_value_max is None):
360
+ if (
361
+ not requirement.lab_values.exists()
362
+ or requirement.numeric_value_min is None
363
+ or requirement.numeric_value_max is None
364
+ ):
306
365
  return False
307
366
 
308
367
  days_min = int(requirement.numeric_value_min)
@@ -317,7 +376,7 @@ def lab_latest_numeric_normal_in_timeframe(
317
376
  if plv.value is not None and plv.lab_value:
318
377
  normal_range = plv.lab_value.get_normal_range(
319
378
  age=patient_context.age() if patient_context else None,
320
- gender=patient_context.gender if patient_context else None
379
+ gender=patient_context.gender if patient_context else None,
321
380
  )
322
381
  min_val = normal_range.get("min")
323
382
  max_val = normal_range.get("max")
@@ -326,20 +385,23 @@ def lab_latest_numeric_normal_in_timeframe(
326
385
  return True
327
386
  return False
328
387
 
388
+
329
389
  def lab_latest_numeric_lower_than_value_in_timeframe(
330
390
  input_links: "RequirementLinks",
331
391
  requirement: Requirement,
332
- operator_kwargs: Dict[str, Any]
392
+ operator_kwargs: Dict[str, Any],
333
393
  ) -> bool:
334
394
  """
335
395
  Checks if any numeric lab value within a timeframe is lower than requirement.numeric_value.
336
396
  Timeframe in requirement.numeric_value_min/max.
337
397
  """
338
398
  patient_lab_values = input_links.patient_lab_values
339
- if (not requirement.lab_values.exists() or
340
- requirement.numeric_value is None or
341
- requirement.numeric_value_min is None or
342
- requirement.numeric_value_max is None):
399
+ if (
400
+ not requirement.lab_values.exists()
401
+ or requirement.numeric_value is None
402
+ or requirement.numeric_value_min is None
403
+ or requirement.numeric_value_max is None
404
+ ):
343
405
  return False
344
406
 
345
407
  threshold = requirement.numeric_value
@@ -355,20 +417,23 @@ def lab_latest_numeric_lower_than_value_in_timeframe(
355
417
  return True
356
418
  return False
357
419
 
420
+
358
421
  def lab_latest_numeric_greater_than_value_in_timeframe(
359
422
  input_links: "RequirementLinks",
360
423
  requirement: Requirement,
361
- operator_kwargs: Dict[str, Any]
424
+ operator_kwargs: Dict[str, Any],
362
425
  ) -> bool:
363
426
  """
364
427
  Checks if any numeric lab value within a timeframe is greater than requirement.numeric_value.
365
428
  Timeframe in requirement.numeric_value_min/max.
366
429
  """
367
430
  patient_lab_values = input_links.patient_lab_values
368
- if (not requirement.lab_values.exists() or
369
- requirement.numeric_value is None or
370
- requirement.numeric_value_min is None or
371
- requirement.numeric_value_max is None):
431
+ if (
432
+ not requirement.lab_values.exists()
433
+ or requirement.numeric_value is None
434
+ or requirement.numeric_value_min is None
435
+ or requirement.numeric_value_max is None
436
+ ):
372
437
  return False
373
438
 
374
439
  threshold = requirement.numeric_value
@@ -384,13 +449,15 @@ def lab_latest_numeric_greater_than_value_in_timeframe(
384
449
  return True
385
450
  return False
386
451
 
452
+
387
453
  # --- Categorical Operators ---
388
454
  # Assuming PatientLabValue has a 'value_string' attribute for categorical results.
389
455
 
456
+
390
457
  def lab_latest_categorical_match(
391
458
  input_links: "RequirementLinks",
392
459
  requirement: Requirement,
393
- operator_kwargs: Dict[str, Any]
460
+ operator_kwargs: Dict[str, Any],
394
461
  ) -> bool:
395
462
  """
396
463
  Checks if the latest categorical lab value matches requirement.string_value.
@@ -401,17 +468,24 @@ def lab_latest_categorical_match(
401
468
 
402
469
  match_string = requirement.string_value
403
470
  for lab_value_model in requirement.lab_values.all():
404
- latest_plv = get_latest_patient_lab_value(patient_lab_values, lab_value_model.name)
471
+ latest_plv = get_latest_patient_lab_value(
472
+ patient_lab_values, lab_value_model.name
473
+ )
405
474
  # Ensure latest_plv has value_str attribute
406
- if latest_plv and hasattr(latest_plv, 'value_str') and latest_plv.value_str is not None: # Changed value_string to value_str
475
+ if (
476
+ latest_plv
477
+ and hasattr(latest_plv, "value_str")
478
+ and latest_plv.value_str is not None
479
+ ): # Changed value_string to value_str
407
480
  if latest_plv.value_str == match_string:
408
481
  return True
409
482
  return False
410
483
 
484
+
411
485
  def lab_latest_categorical_match_substring(
412
486
  input_links: "RequirementLinks",
413
487
  requirement: Requirement,
414
- operator_kwargs: Dict[str, Any]
488
+ operator_kwargs: Dict[str, Any],
415
489
  ) -> bool:
416
490
  """
417
491
  Checks if requirement.string_value is a substring of the latest categorical lab value.
@@ -422,16 +496,23 @@ def lab_latest_categorical_match_substring(
422
496
 
423
497
  substring = requirement.string_value
424
498
  for lab_value_model in requirement.lab_values.all():
425
- latest_plv = get_latest_patient_lab_value(patient_lab_values, lab_value_model.name)
426
- if latest_plv and hasattr(latest_plv, 'value_str') and latest_plv.value_str is not None: # Changed value_string to value_str
499
+ latest_plv = get_latest_patient_lab_value(
500
+ patient_lab_values, lab_value_model.name
501
+ )
502
+ if (
503
+ latest_plv
504
+ and hasattr(latest_plv, "value_str")
505
+ and latest_plv.value_str is not None
506
+ ): # Changed value_string to value_str
427
507
  if substring in latest_plv.value_str:
428
508
  return True
429
509
  return False
430
510
 
511
+
431
512
  def lab_latest_categorical_match_regex(
432
513
  input_links: "RequirementLinks",
433
514
  requirement: Requirement,
434
- operator_kwargs: Dict[str, Any]
515
+ operator_kwargs: Dict[str, Any],
435
516
  ) -> bool:
436
517
  """
437
518
  Checks if the latest categorical lab value matches regex in requirement.string_value.
@@ -444,31 +525,41 @@ def lab_latest_categorical_match_regex(
444
525
  try:
445
526
  compiled_regex = re.compile(regex_pattern)
446
527
  except re.error:
447
- return False # Invalid regex
528
+ return False # Invalid regex
448
529
 
449
530
  for lab_value_model in requirement.lab_values.all():
450
- latest_plv = get_latest_patient_lab_value(patient_lab_values, lab_value_model.name)
451
- if latest_plv and hasattr(latest_plv, 'value_str') and latest_plv.value_str is not None: # Changed value_string to value_str
531
+ latest_plv = get_latest_patient_lab_value(
532
+ patient_lab_values, lab_value_model.name
533
+ )
534
+ if (
535
+ latest_plv
536
+ and hasattr(latest_plv, "value_str")
537
+ and latest_plv.value_str is not None
538
+ ): # Changed value_string to value_str
452
539
  if compiled_regex.search(latest_plv.value_str):
453
540
  return True
454
541
  return False
455
542
 
543
+
456
544
  # --- Categorical Operators with Timeframe ---
457
545
 
546
+
458
547
  def lab_latest_categorical_match_in_timeframe(
459
548
  input_links: "RequirementLinks",
460
549
  requirement: Requirement,
461
- operator_kwargs: Dict[str, Any]
550
+ operator_kwargs: Dict[str, Any],
462
551
  ) -> bool:
463
552
  """
464
553
  Checks if any categorical lab value in timeframe matches requirement.string_value.
465
554
  Timeframe in requirement.numeric_value_min/max.
466
555
  """
467
556
  patient_lab_values = input_links.patient_lab_values
468
- if (not requirement.lab_values.exists() or
469
- requirement.string_value is None or
470
- requirement.numeric_value_min is None or
471
- requirement.numeric_value_max is None):
557
+ if (
558
+ not requirement.lab_values.exists()
559
+ or requirement.string_value is None
560
+ or requirement.numeric_value_min is None
561
+ or requirement.numeric_value_max is None
562
+ ):
472
563
  return False
473
564
 
474
565
  match_string = requirement.string_value
@@ -480,25 +571,30 @@ def lab_latest_categorical_match_in_timeframe(
480
571
  patient_lab_values, lab_value_model.name, days_min, days_max
481
572
  )
482
573
  for plv in values_in_timeframe:
483
- if hasattr(plv, 'value_str') and plv.value_str is not None: # Changed value_string to value_str
574
+ if (
575
+ hasattr(plv, "value_str") and plv.value_str is not None
576
+ ): # Changed value_string to value_str
484
577
  if plv.value_str == match_string:
485
578
  return True
486
579
  return False
487
580
 
581
+
488
582
  def lab_latest_categorical_match_substring_in_timeframe(
489
583
  input_links: "RequirementLinks",
490
584
  requirement: Requirement,
491
- operator_kwargs: Dict[str, Any]
585
+ operator_kwargs: Dict[str, Any],
492
586
  ) -> bool:
493
587
  """
494
588
  Checks if requirement.string_value is substring of any categorical lab value in timeframe.
495
589
  Timeframe in requirement.numeric_value_min/max.
496
590
  """
497
591
  patient_lab_values = input_links.patient_lab_values
498
- if (not requirement.lab_values.exists() or
499
- requirement.string_value is None or
500
- requirement.numeric_value_min is None or
501
- requirement.numeric_value_max is None):
592
+ if (
593
+ not requirement.lab_values.exists()
594
+ or requirement.string_value is None
595
+ or requirement.numeric_value_min is None
596
+ or requirement.numeric_value_max is None
597
+ ):
502
598
  return False
503
599
 
504
600
  substring = requirement.string_value
@@ -510,25 +606,30 @@ def lab_latest_categorical_match_substring_in_timeframe(
510
606
  patient_lab_values, lab_value_model.name, days_min, days_max
511
607
  )
512
608
  for plv in values_in_timeframe:
513
- if hasattr(plv, 'value_str') and plv.value_str is not None: # Changed value_string to value_str
609
+ if (
610
+ hasattr(plv, "value_str") and plv.value_str is not None
611
+ ): # Changed value_string to value_str
514
612
  if substring in plv.value_str:
515
613
  return True
516
614
  return False
517
615
 
616
+
518
617
  def lab_latest_categorical_match_regex_in_timeframe(
519
618
  input_links: "RequirementLinks",
520
619
  requirement: Requirement,
521
- operator_kwargs: Dict[str, Any]
620
+ operator_kwargs: Dict[str, Any],
522
621
  ) -> bool:
523
622
  """
524
623
  Checks if any categorical lab value in timeframe matches regex in requirement.string_value.
525
624
  Timeframe in requirement.numeric_value_min/max.
526
625
  """
527
626
  patient_lab_values = input_links.patient_lab_values
528
- if (not requirement.lab_values.exists() or
529
- requirement.string_value is None or
530
- requirement.numeric_value_min is None or
531
- requirement.numeric_value_max is None):
627
+ if (
628
+ not requirement.lab_values.exists()
629
+ or requirement.string_value is None
630
+ or requirement.numeric_value_min is None
631
+ or requirement.numeric_value_max is None
632
+ ):
532
633
  return False
533
634
 
534
635
  regex_pattern = requirement.string_value
@@ -545,7 +646,9 @@ def lab_latest_categorical_match_regex_in_timeframe(
545
646
  patient_lab_values, lab_value_model.name, days_min, days_max
546
647
  )
547
648
  for plv in values_in_timeframe:
548
- if hasattr(plv, 'value_str') and plv.value_str is not None: # Changed value_string to value_str
649
+ if (
650
+ hasattr(plv, "value_str") and plv.value_str is not None
651
+ ): # Changed value_string to value_str
549
652
  if compiled_regex.search(plv.value_str):
550
653
  return True
551
654
  return False
@@ -558,17 +661,14 @@ LAB_VALUE_OPERATOR_FUNCTIONS: Dict[str, Callable] = {
558
661
  "lab_latest_numeric_normal": lab_latest_numeric_normal,
559
662
  "lab_latest_numeric_lower_than_value": lab_latest_numeric_lower_than_value,
560
663
  "lab_latest_numeric_greater_than_value": lab_latest_numeric_greater_than_value,
561
-
562
664
  # Aliases for backward compatibility, pointing to timeframe versions
563
665
  "lab_latest_numeric_increased_factor": lab_latest_numeric_increased_factor_in_timeframe,
564
666
  "lab_latest_numeric_decreased_factor": lab_latest_numeric_decreased_factor_in_timeframe,
565
-
566
667
  "lab_latest_numeric_increased_factor_in_timeframe": lab_latest_numeric_increased_factor_in_timeframe,
567
668
  "lab_latest_numeric_decreased_factor_in_timeframe": lab_latest_numeric_decreased_factor_in_timeframe,
568
669
  "lab_latest_numeric_normal_in_timeframe": lab_latest_numeric_normal_in_timeframe,
569
670
  "lab_latest_numeric_lower_than_value_in_timeframe": lab_latest_numeric_lower_than_value_in_timeframe,
570
671
  "lab_latest_numeric_greater_than_value_in_timeframe": lab_latest_numeric_greater_than_value_in_timeframe,
571
-
572
672
  "lab_latest_categorical_match": lab_latest_categorical_match,
573
673
  "lab_latest_categorical_match_substring": lab_latest_categorical_match_substring,
574
674
  "lab_latest_categorical_match_regex": lab_latest_categorical_match_regex,