endoreg-db 0.8.8.9__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 (453) 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 +146 -83
  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/migrations/__init__.py +0 -0
  82. endoreg_db/models/__init__.py +8 -0
  83. endoreg_db/models/administration/ai/active_model.py +5 -5
  84. endoreg_db/models/administration/ai/ai_model.py +41 -18
  85. endoreg_db/models/administration/ai/model_type.py +1 -0
  86. endoreg_db/models/administration/case/case.py +22 -22
  87. endoreg_db/models/administration/center/__init__.py +5 -5
  88. endoreg_db/models/administration/center/center.py +6 -2
  89. endoreg_db/models/administration/center/center_resource.py +18 -4
  90. endoreg_db/models/administration/center/center_shift.py +3 -1
  91. endoreg_db/models/administration/center/center_waste.py +6 -2
  92. endoreg_db/models/administration/person/__init__.py +1 -1
  93. endoreg_db/models/administration/person/employee/__init__.py +1 -1
  94. endoreg_db/models/administration/person/employee/employee_type.py +3 -1
  95. endoreg_db/models/administration/person/examiner/__init__.py +1 -1
  96. endoreg_db/models/administration/person/examiner/examiner.py +10 -2
  97. endoreg_db/models/administration/person/names/first_name.py +6 -4
  98. endoreg_db/models/administration/person/names/last_name.py +4 -3
  99. endoreg_db/models/administration/person/patient/__init__.py +1 -1
  100. endoreg_db/models/administration/person/patient/patient.py +0 -1
  101. endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
  102. endoreg_db/models/administration/person/person.py +1 -1
  103. endoreg_db/models/administration/product/__init__.py +7 -6
  104. endoreg_db/models/administration/product/product.py +6 -2
  105. endoreg_db/models/administration/product/product_group.py +9 -7
  106. endoreg_db/models/administration/product/product_material.py +9 -2
  107. endoreg_db/models/administration/product/reference_product.py +64 -15
  108. endoreg_db/models/administration/qualification/qualification.py +3 -1
  109. endoreg_db/models/administration/shift/shift.py +3 -1
  110. endoreg_db/models/administration/shift/shift_type.py +12 -4
  111. endoreg_db/models/aidataset/__init__.py +5 -0
  112. endoreg_db/models/aidataset/aidataset.py +193 -0
  113. endoreg_db/models/label/__init__.py +1 -1
  114. endoreg_db/models/label/label.py +10 -2
  115. endoreg_db/models/label/label_set.py +3 -1
  116. endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
  117. endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
  118. endoreg_db/models/media/__init__.py +12 -5
  119. endoreg_db/models/media/frame/__init__.py +1 -1
  120. endoreg_db/models/media/frame/frame.py +34 -8
  121. endoreg_db/models/media/pdf/__init__.py +2 -1
  122. endoreg_db/models/media/pdf/raw_pdf.py +11 -4
  123. endoreg_db/models/media/pdf/report_file.py +6 -2
  124. endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
  125. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
  126. endoreg_db/models/media/video/create_from_file.py +20 -41
  127. endoreg_db/models/media/video/pipe_1.py +75 -30
  128. endoreg_db/models/media/video/pipe_2.py +37 -12
  129. endoreg_db/models/media/video/video_file.py +36 -24
  130. endoreg_db/models/media/video/video_file_ai.py +235 -70
  131. endoreg_db/models/media/video/video_file_anonymize.py +240 -65
  132. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
  133. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
  134. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
  135. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
  136. endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
  137. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
  138. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
  139. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
  140. endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
  141. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
  142. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
  143. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
  144. endoreg_db/models/media/video/video_file_io.py +85 -33
  145. endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
  146. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
  147. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
  148. endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
  149. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
  150. endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
  151. endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
  152. endoreg_db/models/media/video/video_file_segments.py +118 -27
  153. endoreg_db/models/media/video/video_metadata.py +25 -6
  154. endoreg_db/models/media/video/video_processing.py +54 -15
  155. endoreg_db/models/medical/__init__.py +3 -13
  156. endoreg_db/models/medical/contraindication/__init__.py +3 -1
  157. endoreg_db/models/medical/disease.py +18 -6
  158. endoreg_db/models/medical/event.py +6 -2
  159. endoreg_db/models/medical/examination/__init__.py +5 -1
  160. endoreg_db/models/medical/examination/examination.py +22 -6
  161. endoreg_db/models/medical/examination/examination_indication.py +23 -7
  162. endoreg_db/models/medical/examination/examination_time.py +6 -2
  163. endoreg_db/models/medical/finding/__init__.py +3 -1
  164. endoreg_db/models/medical/finding/finding.py +37 -12
  165. endoreg_db/models/medical/finding/finding_classification.py +27 -8
  166. endoreg_db/models/medical/finding/finding_intervention.py +19 -6
  167. endoreg_db/models/medical/finding/finding_type.py +3 -1
  168. endoreg_db/models/medical/hardware/__init__.py +1 -1
  169. endoreg_db/models/medical/hardware/endoscope.py +14 -2
  170. endoreg_db/models/medical/laboratory/__init__.py +1 -1
  171. endoreg_db/models/medical/laboratory/lab_value.py +139 -39
  172. endoreg_db/models/medical/medication/__init__.py +7 -3
  173. endoreg_db/models/medical/medication/medication.py +3 -1
  174. endoreg_db/models/medical/medication/medication_indication.py +3 -1
  175. endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
  176. endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
  177. endoreg_db/models/medical/medication/medication_schedule.py +3 -1
  178. endoreg_db/models/medical/patient/__init__.py +2 -10
  179. endoreg_db/models/medical/patient/medication_examples.py +3 -14
  180. endoreg_db/models/medical/patient/patient_disease.py +17 -5
  181. endoreg_db/models/medical/patient/patient_event.py +12 -4
  182. endoreg_db/models/medical/patient/patient_examination.py +52 -15
  183. endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
  184. endoreg_db/models/medical/patient/patient_finding.py +105 -29
  185. endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
  186. endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
  187. endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
  188. endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
  189. endoreg_db/models/medical/patient/patient_medication.py +25 -7
  190. endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
  191. endoreg_db/models/metadata/model_meta.py +40 -12
  192. endoreg_db/models/metadata/model_meta_logic.py +51 -16
  193. endoreg_db/models/metadata/sensitive_meta.py +65 -28
  194. endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
  195. endoreg_db/models/metadata/video_meta.py +146 -39
  196. endoreg_db/models/metadata/video_prediction_logic.py +70 -21
  197. endoreg_db/models/metadata/video_prediction_meta.py +80 -27
  198. endoreg_db/models/operation_log.py +63 -0
  199. endoreg_db/models/other/__init__.py +10 -10
  200. endoreg_db/models/other/distribution/__init__.py +9 -7
  201. endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
  202. endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
  203. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
  204. endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
  205. endoreg_db/models/other/emission/__init__.py +1 -1
  206. endoreg_db/models/other/emission/emission_factor.py +9 -3
  207. endoreg_db/models/other/information_source.py +15 -5
  208. endoreg_db/models/other/material.py +3 -1
  209. endoreg_db/models/other/transport_route.py +3 -1
  210. endoreg_db/models/other/unit.py +6 -2
  211. endoreg_db/models/report/report.py +0 -1
  212. endoreg_db/models/requirement/requirement.py +84 -27
  213. endoreg_db/models/requirement/requirement_error.py +5 -6
  214. endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
  215. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
  216. endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
  217. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
  218. endoreg_db/models/requirement/requirement_operator.py +28 -8
  219. endoreg_db/models/requirement/requirement_set.py +34 -11
  220. endoreg_db/models/state/__init__.py +1 -0
  221. endoreg_db/models/state/audit_ledger.py +9 -2
  222. endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
  223. endoreg_db/models/state/processing_history/processing_history.py +136 -0
  224. endoreg_db/models/state/raw_pdf.py +0 -1
  225. endoreg_db/models/state/video.py +2 -3
  226. endoreg_db/models/utils.py +4 -2
  227. endoreg_db/queries/__init__.py +2 -6
  228. endoreg_db/queries/annotations/__init__.py +1 -3
  229. endoreg_db/queries/annotations/legacy.py +37 -26
  230. endoreg_db/root_urls.py +3 -4
  231. endoreg_db/schemas/examination_evaluation.py +3 -0
  232. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
  233. endoreg_db/serializers/__init__.py +2 -8
  234. endoreg_db/serializers/administration/__init__.py +1 -2
  235. endoreg_db/serializers/administration/ai/__init__.py +0 -1
  236. endoreg_db/serializers/administration/ai/active_model.py +3 -1
  237. endoreg_db/serializers/administration/ai/ai_model.py +5 -3
  238. endoreg_db/serializers/administration/ai/model_type.py +3 -1
  239. endoreg_db/serializers/administration/center.py +7 -2
  240. endoreg_db/serializers/administration/gender.py +4 -2
  241. endoreg_db/serializers/anonymization.py +13 -13
  242. endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
  243. endoreg_db/serializers/examination/__init__.py +1 -1
  244. endoreg_db/serializers/examination/base.py +12 -13
  245. endoreg_db/serializers/examination/dropdown.py +6 -7
  246. endoreg_db/serializers/examination_serializer.py +3 -6
  247. endoreg_db/serializers/finding/__init__.py +1 -1
  248. endoreg_db/serializers/finding/finding.py +14 -7
  249. endoreg_db/serializers/finding_classification/__init__.py +3 -3
  250. endoreg_db/serializers/finding_classification/choice.py +3 -3
  251. endoreg_db/serializers/finding_classification/classification.py +2 -4
  252. endoreg_db/serializers/label_video_segment/__init__.py +5 -3
  253. endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
  254. endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
  255. endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
  256. endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
  257. endoreg_db/serializers/meta/__init__.py +1 -2
  258. endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
  259. endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
  260. endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
  261. endoreg_db/serializers/misc/__init__.py +2 -2
  262. endoreg_db/serializers/misc/file_overview.py +11 -7
  263. endoreg_db/serializers/misc/stats.py +10 -8
  264. endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
  265. endoreg_db/serializers/misc/upload_job.py +32 -29
  266. endoreg_db/serializers/patient/__init__.py +2 -1
  267. endoreg_db/serializers/patient/patient.py +32 -15
  268. endoreg_db/serializers/patient/patient_dropdown.py +11 -3
  269. endoreg_db/serializers/patient_examination/__init__.py +1 -1
  270. endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
  271. endoreg_db/serializers/patient_finding/__init__.py +1 -1
  272. endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
  273. endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
  274. endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
  275. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
  276. endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
  277. endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
  278. endoreg_db/serializers/pdf/__init__.py +1 -3
  279. endoreg_db/serializers/requirements/requirement_schema.py +1 -6
  280. endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
  281. endoreg_db/serializers/video/__init__.py +2 -2
  282. endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
  283. endoreg_db/serializers/video/video_file_brief.py +6 -2
  284. endoreg_db/serializers/video/video_file_detail.py +36 -23
  285. endoreg_db/serializers/video/video_file_list.py +4 -2
  286. endoreg_db/serializers/video/video_processing_history.py +54 -50
  287. endoreg_db/services/__init__.py +1 -1
  288. endoreg_db/services/anonymization.py +2 -2
  289. endoreg_db/services/examination_evaluation.py +40 -17
  290. endoreg_db/services/model_meta_from_hf.py +76 -0
  291. endoreg_db/services/polling_coordinator.py +101 -70
  292. endoreg_db/services/pseudonym_service.py +27 -22
  293. endoreg_db/services/report_import.py +6 -3
  294. endoreg_db/services/segment_sync.py +75 -59
  295. endoreg_db/services/video_import.py +6 -7
  296. endoreg_db/urls/__init__.py +2 -2
  297. endoreg_db/urls/ai.py +7 -25
  298. endoreg_db/urls/anonymization.py +61 -15
  299. endoreg_db/urls/auth.py +4 -4
  300. endoreg_db/urls/classification.py +4 -9
  301. endoreg_db/urls/examination.py +27 -18
  302. endoreg_db/urls/media.py +27 -34
  303. endoreg_db/urls/patient.py +11 -7
  304. endoreg_db/urls/requirements.py +3 -1
  305. endoreg_db/urls/root_urls.py +2 -3
  306. endoreg_db/urls/stats.py +24 -16
  307. endoreg_db/urls/upload.py +3 -11
  308. endoreg_db/utils/__init__.py +14 -15
  309. endoreg_db/utils/ai/__init__.py +1 -1
  310. endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
  311. endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
  312. endoreg_db/utils/ai/get.py +2 -1
  313. endoreg_db/utils/ai/inference_dataset.py +14 -15
  314. endoreg_db/utils/ai/model_training/config.py +117 -0
  315. endoreg_db/utils/ai/model_training/dataset.py +74 -0
  316. endoreg_db/utils/ai/model_training/losses.py +68 -0
  317. endoreg_db/utils/ai/model_training/metrics.py +78 -0
  318. endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
  319. endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
  320. endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
  321. endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
  322. endoreg_db/utils/ai/predict.py +4 -4
  323. endoreg_db/utils/ai/preprocess.py +19 -11
  324. endoreg_db/utils/calc_duration_seconds.py +4 -4
  325. endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
  326. endoreg_db/utils/check_video_files.py +74 -47
  327. endoreg_db/utils/cropping.py +10 -9
  328. endoreg_db/utils/dataloader.py +11 -3
  329. endoreg_db/utils/dates.py +3 -4
  330. endoreg_db/utils/defaults/set_default_center.py +7 -6
  331. endoreg_db/utils/env.py +6 -2
  332. endoreg_db/utils/extract_specific_frames.py +24 -9
  333. endoreg_db/utils/file_operations.py +30 -18
  334. endoreg_db/utils/fix_video_path_direct.py +57 -41
  335. endoreg_db/utils/frame_anonymization_utils.py +157 -157
  336. endoreg_db/utils/hashs.py +3 -18
  337. endoreg_db/utils/links/requirement_link.py +96 -52
  338. endoreg_db/utils/ocr.py +30 -25
  339. endoreg_db/utils/operation_log.py +61 -0
  340. endoreg_db/utils/parse_and_generate_yaml.py +12 -13
  341. endoreg_db/utils/paths.py +6 -6
  342. endoreg_db/utils/permissions.py +40 -24
  343. endoreg_db/utils/pipelines/process_video_dir.py +50 -26
  344. endoreg_db/utils/product/sum_emissions.py +5 -3
  345. endoreg_db/utils/product/sum_weights.py +4 -2
  346. endoreg_db/utils/pydantic_models/__init__.py +3 -4
  347. endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
  348. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
  349. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
  350. endoreg_db/utils/setup_config.py +21 -5
  351. endoreg_db/utils/storage.py +3 -1
  352. endoreg_db/utils/translation.py +19 -15
  353. endoreg_db/utils/uuid.py +1 -0
  354. endoreg_db/utils/validate_endo_roi.py +12 -4
  355. endoreg_db/utils/validate_subcategory_dict.py +26 -24
  356. endoreg_db/utils/validate_video_detailed.py +207 -149
  357. endoreg_db/utils/video/__init__.py +7 -3
  358. endoreg_db/utils/video/extract_frames.py +30 -18
  359. endoreg_db/utils/video/ffmpeg_wrapper.py +217 -52
  360. endoreg_db/utils/video/names.py +11 -6
  361. endoreg_db/utils/video/streaming_processor.py +175 -101
  362. endoreg_db/utils/video/video_splitter.py +30 -19
  363. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
  364. endoreg_db/views/__init__.py +0 -20
  365. endoreg_db/views/anonymization/__init__.py +6 -2
  366. endoreg_db/views/anonymization/media_management.py +2 -6
  367. endoreg_db/views/anonymization/overview.py +34 -1
  368. endoreg_db/views/anonymization/validate.py +79 -18
  369. endoreg_db/views/auth/__init__.py +1 -1
  370. endoreg_db/views/auth/keycloak.py +16 -14
  371. endoreg_db/views/examination/__init__.py +12 -15
  372. endoreg_db/views/examination/examination.py +5 -5
  373. endoreg_db/views/examination/examination_manifest_cache.py +5 -5
  374. endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
  375. endoreg_db/views/examination/get_finding_classifications.py +9 -7
  376. endoreg_db/views/examination/get_findings.py +8 -10
  377. endoreg_db/views/examination/get_instruments.py +3 -2
  378. endoreg_db/views/examination/get_interventions.py +1 -1
  379. endoreg_db/views/finding/__init__.py +2 -2
  380. endoreg_db/views/finding/finding.py +58 -54
  381. endoreg_db/views/finding/get_classifications.py +1 -1
  382. endoreg_db/views/finding/get_interventions.py +1 -1
  383. endoreg_db/views/finding_classification/__init__.py +5 -5
  384. endoreg_db/views/finding_classification/finding_classification.py +5 -6
  385. endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
  386. endoreg_db/views/media/__init__.py +13 -13
  387. endoreg_db/views/media/pdf_media.py +9 -9
  388. endoreg_db/views/media/sensitive_metadata.py +10 -7
  389. endoreg_db/views/media/video_media.py +4 -4
  390. endoreg_db/views/meta/__init__.py +1 -1
  391. endoreg_db/views/meta/sensitive_meta_list.py +20 -22
  392. endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
  393. endoreg_db/views/misc/__init__.py +6 -34
  394. endoreg_db/views/misc/center.py +2 -1
  395. endoreg_db/views/misc/csrf.py +2 -1
  396. endoreg_db/views/misc/gender.py +2 -1
  397. endoreg_db/views/misc/stats.py +141 -106
  398. endoreg_db/views/patient/__init__.py +1 -3
  399. endoreg_db/views/patient/patient.py +141 -99
  400. endoreg_db/views/patient_examination/__init__.py +5 -5
  401. endoreg_db/views/patient_examination/patient_examination.py +43 -42
  402. endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
  403. endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
  404. endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
  405. endoreg_db/views/patient_examination/video.py +114 -80
  406. endoreg_db/views/patient_finding/__init__.py +1 -1
  407. endoreg_db/views/patient_finding/patient_finding.py +17 -10
  408. endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
  409. endoreg_db/views/patient_finding_classification/__init__.py +1 -1
  410. endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
  411. endoreg_db/views/report/reimport.py +1 -1
  412. endoreg_db/views/report/report_stream.py +5 -8
  413. endoreg_db/views/requirement/__init__.py +2 -1
  414. endoreg_db/views/requirement/evaluate.py +7 -9
  415. endoreg_db/views/requirement/lookup.py +2 -3
  416. endoreg_db/views/requirement/lookup_store.py +0 -1
  417. endoreg_db/views/requirement/requirement_utils.py +2 -4
  418. endoreg_db/views/stats/__init__.py +4 -4
  419. endoreg_db/views/stats/stats_views.py +152 -115
  420. endoreg_db/views/video/__init__.py +18 -27
  421. endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
  422. endoreg_db/views/{ai → video/ai}/label.py +20 -16
  423. endoreg_db/views/video/correction.py +5 -6
  424. endoreg_db/views/video/reimport.py +134 -99
  425. endoreg_db/views/video/segments_crud.py +134 -44
  426. endoreg_db/views/video/video_apply_mask.py +13 -12
  427. endoreg_db/views/video/video_correction.py +2 -1
  428. endoreg_db/views/video/video_download_processed.py +15 -15
  429. endoreg_db/views/video/video_meta_stats.py +7 -6
  430. endoreg_db/views/video/video_processing_history.py +3 -2
  431. endoreg_db/views/video/video_remove_frames.py +13 -12
  432. endoreg_db/views/video/video_stream.py +110 -82
  433. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
  434. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +436 -433
  435. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +0 -119
  436. endoreg_db/management/commands/import_fallback_video.py +0 -203
  437. endoreg_db/management/commands/import_video.py +0 -422
  438. endoreg_db/management/commands/import_video_with_classification.py +0 -367
  439. endoreg_db/models/media/processing_history/processing_history.py +0 -96
  440. endoreg_db/serializers/label/__init__.py +0 -7
  441. endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
  442. endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
  443. endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
  444. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
  445. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
  446. endoreg_db/services/__old/pdf_import.py +0 -1487
  447. endoreg_db/services/__old/video_import.py +0 -1306
  448. endoreg_db/tasks/upload_tasks.py +0 -216
  449. endoreg_db/tasks/video_ingest.py +0 -161
  450. endoreg_db/tasks/video_processing_tasks.py +0 -327
  451. endoreg_db/views/misc/translation.py +0 -182
  452. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
  453. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
@@ -16,32 +16,62 @@ if TYPE_CHECKING:
16
16
 
17
17
 
18
18
  class PatientFinding(models.Model):
19
- patient_examination = models.ForeignKey("PatientExamination", on_delete=models.CASCADE, related_name="patient_findings")
20
- finding = models.ForeignKey("Finding", on_delete=models.CASCADE, related_name="finding_patient_findings")
19
+ patient_examination = models.ForeignKey(
20
+ "PatientExamination", on_delete=models.CASCADE, related_name="patient_findings"
21
+ )
22
+ finding = models.ForeignKey(
23
+ "Finding", on_delete=models.CASCADE, related_name="finding_patient_findings"
24
+ )
21
25
 
22
26
  # Audit-Felder für medizinische Nachverfolgung
23
27
  created_at = models.DateTimeField(auto_now_add=True)
24
28
  updated_at = models.DateTimeField(auto_now=True)
25
- created_by = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="created_findings", null=True, blank=True)
26
- updated_by = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="updated_findings", null=True, blank=True)
29
+ created_by = models.ForeignKey(
30
+ "auth.User",
31
+ on_delete=models.PROTECT,
32
+ related_name="created_findings",
33
+ null=True,
34
+ blank=True,
35
+ )
36
+ updated_by = models.ForeignKey(
37
+ "auth.User",
38
+ on_delete=models.PROTECT,
39
+ related_name="updated_findings",
40
+ null=True,
41
+ blank=True,
42
+ )
27
43
 
28
44
  # Soft Delete für historische Daten
29
- is_active = models.BooleanField(default=True, help_text="Deaktiviert statt gelöscht für Audit-Trail")
45
+ is_active = models.BooleanField(
46
+ default=True, help_text="Deaktiviert statt gelöscht für Audit-Trail"
47
+ )
30
48
  deactivated_at = models.DateTimeField(null=True, blank=True)
31
- deactivated_by = models.ForeignKey("auth.User", on_delete=models.PROTECT, related_name="deactivated_findings", null=True, blank=True)
49
+ deactivated_by = models.ForeignKey(
50
+ "auth.User",
51
+ on_delete=models.PROTECT,
52
+ related_name="deactivated_findings",
53
+ null=True,
54
+ blank=True,
55
+ )
32
56
 
33
57
  if TYPE_CHECKING:
34
58
  patient_examination: models.ForeignKey["PatientExamination"]
35
59
  finding: models.ForeignKey["Finding"]
36
60
 
37
61
  @property
38
- def video_segments(self) -> models.manager.RelatedManager["LabelVideoSegment"]: ...
62
+ def video_segments(
63
+ self,
64
+ ) -> models.manager.RelatedManager["LabelVideoSegment"]: ...
39
65
 
40
66
  @property
41
- def interventions(self) -> models.manager.RelatedManager["PatientFindingIntervention"]: ...
67
+ def interventions(
68
+ self,
69
+ ) -> models.manager.RelatedManager["PatientFindingIntervention"]: ...
42
70
 
43
71
  @property
44
- def classifications(self) -> models.manager.RelatedManager["PatientFindingClassification"]: ...
72
+ def classifications(
73
+ self,
74
+ ) -> models.manager.RelatedManager["PatientFindingClassification"]: ...
45
75
 
46
76
  class Meta:
47
77
  verbose_name = "Patient Finding"
@@ -51,13 +81,19 @@ class PatientFinding(models.Model):
51
81
  # Wichtige Constraints für Datenintegrität
52
82
  constraints = [
53
83
  models.UniqueConstraint(
54
- fields=["patient_examination", "finding"], condition=models.Q(is_active=True), name="unique_active_finding_per_examination"
84
+ fields=["patient_examination", "finding"],
85
+ condition=models.Q(is_active=True),
86
+ name="unique_active_finding_per_examination",
55
87
  ),
56
88
  models.CheckConstraint(
57
89
  condition=models.Q( # called .check in future?
58
90
  deactivated_at__isnull=True, deactivated_by__isnull=True
59
91
  )
60
- | models.Q(deactivated_at__isnull=False, deactivated_by__isnull=False, is_active=False),
92
+ | models.Q(
93
+ deactivated_at__isnull=False,
94
+ deactivated_by__isnull=False,
95
+ is_active=False,
96
+ ),
61
97
  name="deactivation_fields_consistency",
62
98
  ),
63
99
  ]
@@ -89,10 +125,14 @@ class PatientFinding(models.Model):
89
125
 
90
126
  # Prüfe ob Finding für diese Examination erlaubt ist
91
127
  if self.finding and self.patient_examination:
92
- available_findings = self.patient_examination.examination_safe.get_available_findings()
128
+ available_findings = (
129
+ self.patient_examination.examination_safe.get_available_findings()
130
+ )
93
131
  if self.finding not in available_findings:
94
132
  raise ValidationError(
95
- {"finding": f'Finding "{self.finding.name}" ist nicht für Examination "{self.patient_examination.examination_safe.name}" erlaubt.'}
133
+ {
134
+ "finding": f'Finding "{self.finding.name}" ist nicht für Examination "{self.patient_examination.examination_safe.name}" erlaubt.'
135
+ }
96
136
  )
97
137
 
98
138
  # Prüfe Required Findings Logic
@@ -118,10 +158,15 @@ class PatientFinding(models.Model):
118
158
  required_findings = getattr(examination, "required_findings", None)
119
159
  if required_findings and required_findings.exists():
120
160
  # Prüfe ob alle Required Findings vorhanden sind
121
- existing_findings = self.patient_examination.patient_findings.filter(is_active=True).values_list("finding", flat=True)
161
+ existing_findings = self.patient_examination.patient_findings.filter(
162
+ is_active=True
163
+ ).values_list("finding", flat=True)
122
164
 
123
165
  missing_required = required_findings.exclude(id__in=existing_findings)
124
- if missing_required.exists() and self.finding not in required_findings.all():
166
+ if (
167
+ missing_required.exists()
168
+ and self.finding not in required_findings.all()
169
+ ):
125
170
  missing_names = ", ".join([f.name for f in missing_required])
126
171
  raise ValidationError(f"Erforderliche Findings fehlen: {missing_names}")
127
172
 
@@ -151,7 +196,14 @@ class PatientFinding(models.Model):
151
196
  self.deactivated_at = None
152
197
  self.deactivated_by = None
153
198
  self.updated_by = user
154
- self.save(update_fields=["is_active", "deactivated_at", "deactivated_by", "updated_by"])
199
+ self.save(
200
+ update_fields=[
201
+ "is_active",
202
+ "deactivated_at",
203
+ "deactivated_by",
204
+ "updated_by",
205
+ ]
206
+ )
155
207
  except ValidationError as e:
156
208
  raise ValidationError(f"Reaktivierung nicht möglich: {e}")
157
209
 
@@ -189,20 +241,30 @@ class PatientFinding(models.Model):
189
241
 
190
242
  try:
191
243
  classification = FindingClassification.objects.get(id=classification_id)
192
- classification_choice = FindingClassificationChoice.objects.filter(id=classification_choice_id).first()
244
+ classification_choice = FindingClassificationChoice.objects.filter(
245
+ id=classification_choice_id
246
+ ).first()
193
247
 
194
248
  if not classification.choices.filter(id=classification_choice_id).exists():
195
- raise ValidationError(f"Classification Choice {classification_choice_id} gehört nicht zu Classification {classification_id}")
249
+ raise ValidationError(
250
+ f"Classification Choice {classification_choice_id} gehört nicht zu Classification {classification_id}"
251
+ )
196
252
 
197
- existing = self.classifications.filter(classification=classification, classification_choice=classification_choice, is_active=True).first()
253
+ existing = self.classifications.filter(
254
+ classification=classification,
255
+ classification_choice=classification_choice,
256
+ is_active=True,
257
+ ).first()
198
258
 
199
259
  if existing:
200
260
  return existing
201
261
 
202
- patient_finding_classification = PatientFindingClassification.objects.create(
203
- finding=self,
204
- classification_id=classification_id,
205
- classification_choice_id=classification_choice_id,
262
+ patient_finding_classification = (
263
+ PatientFindingClassification.objects.create(
264
+ finding=self,
265
+ classification_id=classification_id,
266
+ classification_choice_id=classification_choice_id,
267
+ )
206
268
  )
207
269
 
208
270
  return patient_finding_classification
@@ -233,7 +295,11 @@ class PatientFinding(models.Model):
233
295
  intervention = FindingIntervention.objects.get(id=intervention_id)
234
296
 
235
297
  patient_finding_intervention = PatientFindingIntervention.objects.create(
236
- patient_finding=self, intervention=intervention, state=state, date=date or timezone.now(), created_by=user
298
+ patient_finding=self,
299
+ intervention=intervention,
300
+ state=state,
301
+ date=date or timezone.now(),
302
+ created_by=user,
237
303
  )
238
304
 
239
305
  return patient_finding_intervention
@@ -273,7 +339,9 @@ class PatientFinding(models.Model):
273
339
  Returns:
274
340
  QuerySet: Classifications related to this finding filtered by the "location" classification type.
275
341
  """
276
- classifications = self.classifications.filter(classification__classification_types__name__iexact="location")
342
+ classifications = self.classifications.filter(
343
+ classification__classification_types__name__iexact="location"
344
+ )
277
345
  return classifications
278
346
 
279
347
  @property
@@ -284,7 +352,9 @@ class PatientFinding(models.Model):
284
352
  Returns:
285
353
  QuerySet: Classifications related to this finding filtered by the "morphology" classification type.
286
354
  """
287
- classifications = self.classifications.filter(classification__classification_types__name__iexact="morphology")
355
+ classifications = self.classifications.filter(
356
+ classification__classification_types__name__iexact="morphology"
357
+ )
288
358
  return classifications
289
359
 
290
360
  @property
@@ -318,7 +388,9 @@ class PatientFinding(models.Model):
318
388
  if pf_classification.classification:
319
389
  finding_classifications_list.append(pf_classification.classification)
320
390
  if pf_classification.classification_choice:
321
- finding_classification_choices_list.append(pf_classification.classification_choice)
391
+ finding_classification_choices_list.append(
392
+ pf_classification.classification_choice
393
+ )
322
394
 
323
395
  # Get all active finding interventions
324
396
  finding_interventions_list = []
@@ -327,8 +399,12 @@ class PatientFinding(models.Model):
327
399
  finding_interventions_list.append(pf_intervention.intervention)
328
400
 
329
401
  # Include patient examination and patient for context
330
- patient_examinations_list = [self.patient_examination] if self.patient_examination else []
331
- patient_findings_list = cast("List[PatientFinding]", [self]) # Include self for direct patient finding evaluations
402
+ patient_examinations_list = (
403
+ [self.patient_examination] if self.patient_examination else []
404
+ )
405
+ patient_findings_list = cast(
406
+ "List[PatientFinding]", [self]
407
+ ) # Include self for direct patient finding evaluations
332
408
 
333
409
  return RequirementLinks(
334
410
  findings=findings_list,
@@ -18,11 +18,23 @@ class PatientFindingClassification(models.Model):
18
18
  Links a PatientFinding to a specific classification and choice, with optional subcategory values.
19
19
  """
20
20
 
21
- finding = models.ForeignKey("PatientFinding", on_delete=models.CASCADE, related_name="classifications")
22
- classification = models.ForeignKey("FindingClassification", on_delete=models.CASCADE, related_name="patient_finding_classifications")
23
- classification_choice = models.ForeignKey("FindingClassificationChoice", on_delete=models.CASCADE, related_name="patient_finding_classifications")
21
+ finding = models.ForeignKey(
22
+ "PatientFinding", on_delete=models.CASCADE, related_name="classifications"
23
+ )
24
+ classification = models.ForeignKey(
25
+ "FindingClassification",
26
+ on_delete=models.CASCADE,
27
+ related_name="patient_finding_classifications",
28
+ )
29
+ classification_choice = models.ForeignKey(
30
+ "FindingClassificationChoice",
31
+ on_delete=models.CASCADE,
32
+ related_name="patient_finding_classifications",
33
+ )
24
34
 
25
- is_active = models.BooleanField(default=True, help_text="Indicates if the classification is currently active.")
35
+ is_active = models.BooleanField(
36
+ default=True, help_text="Indicates if the classification is currently active."
37
+ )
26
38
  subcategories = models.JSONField(blank=True, null=True)
27
39
  numerical_descriptors = models.JSONField(blank=True, null=True)
28
40
 
@@ -55,7 +67,9 @@ class PatientFindingClassification(models.Model):
55
67
  self.subcategories = self.classification_choice.subcategories
56
68
 
57
69
  if not self.numerical_descriptors:
58
- self.numerical_descriptors = self.classification_choice.numerical_descriptors
70
+ self.numerical_descriptors = (
71
+ self.classification_choice.numerical_descriptors
72
+ )
59
73
 
60
74
  super().save(*args, **kwargs)
61
75
 
@@ -80,7 +94,9 @@ class PatientFindingClassification(models.Model):
80
94
  self.save()
81
95
  return self.numerical_descriptors
82
96
 
83
- def set_subcategory(self, subcategory_name: str, subcategory_value: Dict[str, dict]):
97
+ def set_subcategory(
98
+ self, subcategory_name: str, subcategory_value: Dict[str, dict]
99
+ ):
84
100
  """
85
101
  Update the value of a specified subcategory and save the classification.
86
102
 
@@ -92,7 +108,9 @@ class PatientFindingClassification(models.Model):
92
108
  dict: The updated subcategory dictionary.
93
109
  """
94
110
  assert self.subcategories, "Subcategories must be initialized."
95
- assert subcategory_name in self.subcategories, "Subcategory must be in subcategories."
111
+ assert subcategory_name in self.subcategories, (
112
+ "Subcategory must be in subcategories."
113
+ )
96
114
  self.subcategories[subcategory_name]["value"] = subcategory_value
97
115
  self.save()
98
116
 
@@ -136,8 +154,12 @@ class PatientFindingClassification(models.Model):
136
154
  Raises:
137
155
  ValueError: If the descriptor's distribution type is not supported.
138
156
  """
139
- assert self.numerical_descriptors is not None, "Numerical descriptors must be initialized."
140
- assert descriptor_name in self.numerical_descriptors, "Descriptor must be in numerical descriptors."
157
+ assert self.numerical_descriptors is not None, (
158
+ "Numerical descriptors must be initialized."
159
+ )
160
+ assert descriptor_name in self.numerical_descriptors, (
161
+ "Descriptor must be in numerical descriptors."
162
+ )
141
163
  descriptor = self.numerical_descriptors[descriptor_name]
142
164
  min_val = descriptor.get("min", 0)
143
165
  max_val = descriptor.get("max", 1)
@@ -173,7 +195,9 @@ class PatientFindingClassification(models.Model):
173
195
  raise ValueError("Descriptor name must be in numerical descriptors.")
174
196
 
175
197
  value = self.get_random_value_for_numerical_descriptor(descriptor_name)
176
- assert self.numerical_descriptors is not None, "Numerical descriptors must be initialized."
198
+ assert self.numerical_descriptors is not None, (
199
+ "Numerical descriptors must be initialized."
200
+ )
177
201
  self.numerical_descriptors[descriptor_name]["value"] = value
178
202
  if save:
179
203
  self.save()
@@ -190,11 +214,16 @@ class PatientFindingClassification(models.Model):
190
214
  if not self.subcategories or not self.numerical_descriptors:
191
215
  self.save()
192
216
 
193
- assert self.numerical_descriptors is not None, "Numerical descriptors must be initialized."
217
+ assert self.numerical_descriptors is not None, (
218
+ "Numerical descriptors must be initialized."
219
+ )
194
220
 
195
221
  numerical_descriptors = self.numerical_descriptors
196
222
 
197
- for numerical_descriptor_name, _numerical_descriptor_dict in numerical_descriptors.items():
223
+ for (
224
+ numerical_descriptor_name,
225
+ _numerical_descriptor_dict,
226
+ ) in numerical_descriptors.items():
198
227
  self.set_random_numerical_descriptor(numerical_descriptor_name, save=False)
199
228
 
200
229
  self.save()
@@ -8,9 +8,17 @@ if TYPE_CHECKING:
8
8
 
9
9
 
10
10
  class PatientFindingIntervention(models.Model):
11
- finding = models.ForeignKey("PatientFinding", on_delete=models.CASCADE, related_name="interventions")
12
- intervention = models.ForeignKey("FindingIntervention", on_delete=models.CASCADE, related_name="patient_finding_interventions")
13
- is_active = models.BooleanField(default=True, help_text="Indicates if the intervention is currently active.")
11
+ finding = models.ForeignKey(
12
+ "PatientFinding", on_delete=models.CASCADE, related_name="interventions"
13
+ )
14
+ intervention = models.ForeignKey(
15
+ "FindingIntervention",
16
+ on_delete=models.CASCADE,
17
+ related_name="patient_finding_interventions",
18
+ )
19
+ is_active = models.BooleanField(
20
+ default=True, help_text="Indicates if the intervention is currently active."
21
+ )
14
22
  state = models.CharField(max_length=100, blank=True, null=True)
15
23
  time_start = models.DateTimeField(blank=True, null=True)
16
24
  time_end = models.DateTimeField(blank=True, null=True)
@@ -60,7 +60,9 @@ class PatientLabSample(models.Model):
60
60
  values (PatientLabValue; One2Many): The value of the lab sample.
61
61
  """
62
62
 
63
- patient = models.ForeignKey("Patient", on_delete=models.CASCADE, related_name="lab_samples")
63
+ patient = models.ForeignKey(
64
+ "Patient", on_delete=models.CASCADE, related_name="lab_samples"
65
+ )
64
66
  sample_type = models.ForeignKey("PatientLabSampleType", on_delete=models.CASCADE)
65
67
  date = models.DateTimeField()
66
68
 
@@ -125,7 +127,9 @@ class PatientLabSample(models.Model):
125
127
  if not date:
126
128
  date = dt.now(timezone.utc)
127
129
 
128
- patient_lab_sample = cls.objects.create(patient=patient, sample_type=sample_type, date=date)
130
+ patient_lab_sample = cls.objects.create(
131
+ patient=patient, sample_type=sample_type, date=date
132
+ )
129
133
 
130
134
  if save:
131
135
  patient_lab_sample.save()
@@ -23,11 +23,23 @@ class PatientLabValue(models.Model):
23
23
  date (datetime): The date of the lab value.
24
24
  """
25
25
 
26
- patient = models.ForeignKey("Patient", on_delete=models.CASCADE, related_name="lab_values", blank=True, null=True)
26
+ patient = models.ForeignKey(
27
+ "Patient",
28
+ on_delete=models.CASCADE,
29
+ related_name="lab_values",
30
+ blank=True,
31
+ null=True,
32
+ )
27
33
  lab_value = models.ForeignKey("LabValue", on_delete=models.CASCADE)
28
34
  value = models.FloatField(blank=True, null=True)
29
35
  value_str = models.CharField(max_length=255, blank=True, null=True)
30
- sample = models.ForeignKey("PatientLabSample", on_delete=models.CASCADE, blank=True, null=True, related_name="values")
36
+ sample = models.ForeignKey(
37
+ "PatientLabSample",
38
+ on_delete=models.CASCADE,
39
+ blank=True,
40
+ null=True,
41
+ related_name="values",
42
+ )
31
43
  datetime = models.DateTimeField( # if not set, use now
32
44
  auto_now_add=True
33
45
  )
@@ -57,7 +69,12 @@ class PatientLabValue(models.Model):
57
69
 
58
70
  @classmethod
59
71
  def create_lab_value_by_sample(
60
- cls, sample: "PatientLabSample", lab_value_name: str, value: Optional[float] = None, value_str: Optional[str] = None, unit: Optional["Unit"] = None
72
+ cls,
73
+ sample: "PatientLabSample",
74
+ lab_value_name: str,
75
+ value: Optional[float] = None,
76
+ value_str: Optional[str] = None,
77
+ unit: Optional["Unit"] = None,
61
78
  ):
62
79
  from ..laboratory import LabValue
63
80
 
@@ -80,7 +97,9 @@ class PatientLabValue(models.Model):
80
97
  def __str__(self):
81
98
  formatted_datetime = self.datetime.strftime("%Y-%m-%d %H:%M")
82
99
  # normal_range = self.get_normal_range()
83
- norm_range_string = f"[{self.normal_range.get('min', '')} - {self.normal_range.get('max', '')}]"
100
+ norm_range_string = (
101
+ f"[{self.normal_range.get('min', '')} - {self.normal_range.get('max', '')}]"
102
+ )
84
103
  _str = f"{self.lab_value} - {self.value} {self.unit} - {norm_range_string} ({formatted_datetime})"
85
104
  return _str
86
105
 
@@ -157,16 +176,29 @@ class PatientLabValue(models.Model):
157
176
  distribution = lab_value.get_default_default_distribution()
158
177
 
159
178
  if not distribution:
160
- warnings.warn(f"No distribution set for lab value {lab_value}, assuming uniform numeric distribution based on normal values")
179
+ warnings.warn(
180
+ f"No distribution set for lab value {lab_value}, assuming uniform numeric distribution based on normal values"
181
+ )
161
182
 
162
- if not self.normal_range.get("min", None) or not self.normal_range.get("max", None):
183
+ if not self.normal_range.get("min", None) or not self.normal_range.get(
184
+ "max", None
185
+ ):
163
186
  self.set_norm_values_from_default()
164
187
  _min = self.normal_range.get("min", 0.0001)
165
188
  _max = self.normal_range.get("max", 100)
166
- _name = "auto-" + self.lab_value_safe.name + "-distribution-default-uniform"
167
- distribution = NumericValueDistribution(name=_name, min_descriptor=_min, max_max_desciptor=_max, distribution_type="uniform")
168
-
169
- value = distribution.generate_value(lab_value=lab_value, patient=patient)
189
+ _name = (
190
+ "auto-" + self.lab_value_safe.name + "-distribution-default-uniform"
191
+ )
192
+ distribution = NumericValueDistribution(
193
+ name=_name,
194
+ min_descriptor=_min,
195
+ max_max_desciptor=_max,
196
+ distribution_type="uniform",
197
+ )
198
+
199
+ value = distribution.generate_value(
200
+ lab_value=lab_value, patient=patient
201
+ )
170
202
  self.value = value
171
203
  if save:
172
204
  self.save()
@@ -1,10 +1,12 @@
1
- from typing import TYPE_CHECKING, List, Optional, cast # Added List
1
+ from typing import TYPE_CHECKING, List, cast # Added List
2
2
 
3
3
  from django.db import models
4
4
 
5
5
  # Added imports for type hints
6
6
  if TYPE_CHECKING:
7
- from ....utils.links.requirement_link import RequirementLinks # Added RequirementLinks
7
+ from ....utils.links.requirement_link import (
8
+ RequirementLinks,
9
+ ) # Added RequirementLinks
8
10
  from ...administration.person.patient import Patient
9
11
  from ...other.unit import Unit
10
12
  from ..medication import Medication, MedicationIndication, MedicationIntakeTime
@@ -18,9 +20,19 @@ class PatientMedication(models.Model):
18
20
  """
19
21
 
20
22
  patient = models.ForeignKey("Patient", on_delete=models.CASCADE)
21
- medication_indication = models.ForeignKey("MedicationIndication", on_delete=models.CASCADE, related_name="indication_patient_medications", null=True)
23
+ medication_indication = models.ForeignKey(
24
+ "MedicationIndication",
25
+ on_delete=models.CASCADE,
26
+ related_name="indication_patient_medications",
27
+ null=True,
28
+ )
22
29
 
23
- medication = models.ForeignKey("Medication", on_delete=models.CASCADE, blank=True, related_name="medication_patient_medications")
30
+ medication = models.ForeignKey(
31
+ "Medication",
32
+ on_delete=models.CASCADE,
33
+ blank=True,
34
+ related_name="medication_patient_medications",
35
+ )
24
36
 
25
37
  intake_times = models.ManyToManyField(
26
38
  "MedicationIntakeTime",
@@ -39,7 +51,9 @@ class PatientMedication(models.Model):
39
51
  medication_indication: models.ForeignKey["MedicationIndication|None"]
40
52
  medication: models.ForeignKey["Medication|None"]
41
53
 
42
- intake_times = cast(models.manager.RelatedManager["MedicationIntakeTime"], intake_times)
54
+ intake_times = cast(
55
+ models.manager.RelatedManager["MedicationIntakeTime"], intake_times
56
+ )
43
57
  unit: models.ForeignKey["Unit|None"]
44
58
  dosage = cast(models.JSONField, dosage)
45
59
 
@@ -73,10 +87,14 @@ class PatientMedication(models.Model):
73
87
  verbose_name_plural = "Patient Medications"
74
88
 
75
89
  @classmethod
76
- def create_by_patient_and_indication(cls, patient, medication_indication: "MedicationIndication"):
90
+ def create_by_patient_and_indication(
91
+ cls, patient, medication_indication: "MedicationIndication"
92
+ ):
77
93
  """Creates a PatientMedication instance linking a patient and an indication."""
78
94
 
79
- patient_medication = cls.objects.create(patient=patient, medication_indication=medication_indication)
95
+ patient_medication = cls.objects.create(
96
+ patient=patient, medication_indication=medication_indication
97
+ )
80
98
  patient_medication.save()
81
99
 
82
100
  return patient_medication
@@ -7,7 +7,11 @@ if TYPE_CHECKING:
7
7
  from .patient_medication import PatientMedication
8
8
  from ..medication import MedicationSchedule
9
9
  from ....utils.links.requirement_link import RequirementLinks # Added
10
- from ..medication import Medication, MedicationIndication, MedicationIntakeTime # Added
10
+ from ..medication import (
11
+ Medication,
12
+ MedicationIndication,
13
+ MedicationIntakeTime,
14
+ ) # Added
11
15
 
12
16
 
13
17
  class PatientMedicationSchedule(models.Model):
@@ -16,7 +20,9 @@ class PatientMedicationSchedule(models.Model):
16
20
  """
17
21
 
18
22
  patient = models.ForeignKey("Patient", on_delete=models.CASCADE)
19
- medication = models.ManyToManyField("PatientMedication", related_name="patient_medication_schedules", blank=True)
23
+ medication = models.ManyToManyField(
24
+ "PatientMedication", related_name="patient_medication_schedules", blank=True
25
+ )
20
26
 
21
27
  created_at = models.DateTimeField(auto_now_add=True)
22
28
  updated_at = models.DateTimeField(auto_now=True)
@@ -24,7 +30,9 @@ class PatientMedicationSchedule(models.Model):
24
30
  if TYPE_CHECKING:
25
31
  patient: models.ForeignKey["Patient"]
26
32
 
27
- medication = cast(models.manager.RelatedManager["PatientMedication"], medication)
33
+ medication = cast(
34
+ models.manager.RelatedManager["PatientMedication"], medication
35
+ )
28
36
 
29
37
  @property
30
38
  def links(self) -> "RequirementLinks":
@@ -37,14 +45,20 @@ class PatientMedicationSchedule(models.Model):
37
45
  aggregated_medication_indications: List["MedicationIndication"] = []
38
46
  aggregated_medication_intake_times: List["MedicationIntakeTime"] = []
39
47
 
40
- patient_meds_in_schedule: List["PatientMedication"] = list(self.medication.all())
48
+ patient_meds_in_schedule: List["PatientMedication"] = list(
49
+ self.medication.all()
50
+ )
41
51
 
42
52
  for pm_instance in patient_meds_in_schedule:
43
53
  pm_links_obj = pm_instance.links
44
54
 
45
55
  aggregated_medications.extend(pm_links_obj.medications)
46
- aggregated_medication_indications.extend(pm_links_obj.medication_indications)
47
- aggregated_medication_intake_times.extend(pm_links_obj.medication_intake_times)
56
+ aggregated_medication_indications.extend(
57
+ pm_links_obj.medication_indications
58
+ )
59
+ aggregated_medication_intake_times.extend(
60
+ pm_links_obj.medication_intake_times
61
+ )
48
62
 
49
63
  return RequirementLinks(
50
64
  medications=list(set(aggregated_medications)),
@@ -64,12 +78,16 @@ class PatientMedicationSchedule(models.Model):
64
78
  from ..medication import MedicationIndicationType
65
79
  from .patient_medication import PatientMedication
66
80
 
67
- medication_indication = MedicationIndicationType.get_random_indication_by_type(name=indication_type)
81
+ medication_indication = MedicationIndicationType.get_random_indication_by_type(
82
+ name=indication_type
83
+ )
68
84
 
69
85
  patient_medication_schedule = cls.objects.create(patient=patient)
70
86
  patient_medication_schedule.save()
71
87
 
72
- patient_medication = PatientMedication.create_by_patient_and_indication(patient, medication_indication)
88
+ patient_medication = PatientMedication.create_by_patient_and_indication(
89
+ patient, medication_indication
90
+ )
73
91
  patient_medication_schedule.medication.add(patient_medication)
74
92
  patient_medication_schedule.save()
75
93
 
@@ -87,7 +105,9 @@ class PatientMedicationSchedule(models.Model):
87
105
  patient_medication_schedule = cls.objects.create(patient=patient)
88
106
  patient_medication_schedule.save()
89
107
 
90
- patient_medication = PatientMedication.create_by_patient_and_indication(patient, medication_indication)
108
+ patient_medication = PatientMedication.create_by_patient_and_indication(
109
+ patient, medication_indication
110
+ )
91
111
  patient_medication_schedule.medication.add(patient_medication)
92
112
  patient_medication_schedule.save()
93
113
 
@@ -112,7 +132,11 @@ class PatientMedicationSchedule(models.Model):
112
132
  intake_times = medication_schedule.get_intake_times()
113
133
 
114
134
  patient_medication = PatientMedication.objects.create(
115
- patient=self.patient, medication=drug, medication_indication=medication_indication, unit=unit, dosage=dosage
135
+ patient=self.patient,
136
+ medication=drug,
137
+ medication_indication=medication_indication,
138
+ unit=unit,
139
+ dosage=dosage,
116
140
  )
117
141
 
118
142
  patient_medication.intake_times.set(intake_times)