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
@@ -7,9 +7,7 @@ from django.db import models
7
7
 
8
8
  from endoreg_db.models.label import LabelSet
9
9
 
10
- from ..label.label_video_segment import (
11
- LabelVideoSegment,
12
- )
10
+ from ..label.label_video_segment import LabelVideoSegment
13
11
  from ..utils import find_segments_in_prediction_array
14
12
 
15
13
  logger = logging.getLogger(__name__)
@@ -49,7 +47,12 @@ class VideoPredictionMeta(models.Model):
49
47
  label_video_segments: "models.Manager[LabelVideoSegment]"
50
48
 
51
49
  class Meta:
52
- constraints = [models.UniqueConstraint(fields=["model_meta", "video_file"], name="unique_prediction_per_video_model")]
50
+ constraints = [
51
+ models.UniqueConstraint(
52
+ fields=["model_meta", "video_file"],
53
+ name="unique_prediction_per_video_model",
54
+ )
55
+ ]
53
56
  indexes = [
54
57
  models.Index(fields=["model_meta", "video_file"]),
55
58
  ]
@@ -72,11 +75,15 @@ class VideoPredictionMeta(models.Model):
72
75
  def __str__(self):
73
76
  try:
74
77
  video_obj = self.get_video()
75
- return f"Prediction Meta for Video {video_obj.uuid} - {self.model_meta.name}"
78
+ return f"Prediction Meta for Video {video_obj.video_hash} - {self.model_meta.name}"
76
79
  except ValueError:
77
80
  return f"Prediction Meta {self.pk} (Error: No VideoFile) - {self.model_meta.name}"
78
81
  except Exception as e:
79
- logger.warning("Error generating string representation for VideoPredictionMeta %s: %s", self.pk, e)
82
+ logger.warning(
83
+ "Error generating string representation for VideoPredictionMeta %s: %s",
84
+ self.pk,
85
+ e,
86
+ )
80
87
  return f"Prediction Meta {self.pk} (Error: {e})"
81
88
 
82
89
  def get_labelset(self) -> Optional["LabelSet"]:
@@ -123,19 +130,29 @@ class VideoPredictionMeta(models.Model):
123
130
  num_frames = video_obj.frame_count
124
131
 
125
132
  if num_frames is None or num_frames <= 0:
126
- logger.warning(f"Cannot calculate prediction array for {video_obj} with invalid frame count ({num_frames}).")
133
+ logger.warning(
134
+ f"Cannot calculate prediction array for {video_obj} with invalid frame count ({num_frames})."
135
+ )
127
136
  return
128
137
 
129
138
  if not label_list:
130
- logger.warning(f"No labels found for model {model_meta}. Cannot calculate prediction array.")
139
+ logger.warning(
140
+ f"No labels found for model {model_meta}. Cannot calculate prediction array."
141
+ )
131
142
  return
132
143
 
133
144
  prediction_array = np.zeros((num_frames, len(label_list)))
134
145
 
135
- base_pred_qs = ImageClassificationAnnotation.objects.filter(model_meta=model_meta, frame__video_file=video_obj)
146
+ base_pred_qs = ImageClassificationAnnotation.objects.filter(
147
+ model_meta=model_meta, frame__video_file=video_obj
148
+ )
136
149
 
137
150
  for i, label in enumerate(label_list):
138
- predictions = base_pred_qs.filter(label=label).order_by("frame__frame_number").values_list("frame__frame_number", "float_value")
151
+ predictions = (
152
+ base_pred_qs.filter(label=label)
153
+ .order_by("frame__frame_number")
154
+ .values_list("frame__frame_number", "float_value")
155
+ )
139
156
 
140
157
  confidences = np.full(num_frames, 0.5)
141
158
  found_predictions = False
@@ -144,19 +161,27 @@ class VideoPredictionMeta(models.Model):
144
161
  confidences[frame_num] = confidence
145
162
  found_predictions = True
146
163
  else:
147
- logger.warning(f"Prediction found for out-of-bounds frame number {frame_num} (max: {num_frames - 1}). Skipping.")
164
+ logger.warning(
165
+ f"Prediction found for out-of-bounds frame number {frame_num} (max: {num_frames - 1}). Skipping."
166
+ )
148
167
 
149
168
  if not found_predictions:
150
- logger.warning(f"No predictions found for label '{label.name}' in {video_obj}. Using default confidence.")
169
+ logger.warning(
170
+ f"No predictions found for label '{label.name}' in {video_obj}. Using default confidence."
171
+ )
151
172
 
152
- smooth_confidences = self.apply_running_mean(confidences, window_size_in_seconds)
173
+ smooth_confidences = self.apply_running_mean(
174
+ confidences, window_size_in_seconds
175
+ )
153
176
  binary_predictions = smooth_confidences > 0.5
154
177
  prediction_array[:, i] = binary_predictions
155
178
 
156
179
  self.save_prediction_array(prediction_array)
157
180
  logger.info(f"Calculated and saved prediction array for {self}")
158
181
 
159
- def apply_running_mean(self, confidence_array, window_size_in_seconds: Optional[float] = None):
182
+ def apply_running_mean(
183
+ self, confidence_array, window_size_in_seconds: Optional[float] = None
184
+ ):
160
185
  """
161
186
  Apply a running mean filter to the confidence array for smoothing, assuming a padding
162
187
  of 0.5 for the edges.
@@ -165,7 +190,9 @@ class VideoPredictionMeta(models.Model):
165
190
  fps = video_obj.get_fps()
166
191
 
167
192
  if fps is None or fps <= 0:
168
- logger.warning(f"Invalid FPS ({fps}) for {video_obj}. Cannot apply running mean. Returning original array.")
193
+ logger.warning(
194
+ f"Invalid FPS ({fps}) for {video_obj}. Cannot apply running mean. Returning original array."
195
+ )
169
196
  return confidence_array
170
197
 
171
198
  if not window_size_in_seconds:
@@ -190,19 +217,25 @@ class VideoPredictionMeta(models.Model):
190
217
  running_mean = running_mean[start_index:end_index]
191
218
 
192
219
  if running_mean.shape != confidence_array.shape:
193
- logger.warning(f"Running mean output shape {running_mean.shape} differs from input {confidence_array.shape}. Check padding/slicing.")
220
+ logger.warning(
221
+ f"Running mean output shape {running_mean.shape} differs from input {confidence_array.shape}. Check padding/slicing."
222
+ )
194
223
  return confidence_array
195
224
 
196
225
  return running_mean
197
226
 
198
- def create_video_segments_for_label(self, segments: List[Tuple[int, int]], label: "Label"):
227
+ def create_video_segments_for_label(
228
+ self, segments: List[Tuple[int, int]], label: "Label"
229
+ ):
199
230
  """
200
231
  Creates LabelVideoSegment instances for the given label and segments.
201
232
  """
202
233
  from endoreg_db.models import InformationSource
203
234
 
204
235
  video_obj = self.get_video()
205
- information_source, _ = InformationSource.objects.get_or_create(name="prediction")
236
+ information_source, _ = InformationSource.objects.get_or_create(
237
+ name="prediction"
238
+ )
206
239
 
207
240
  segments_to_create = []
208
241
  for start_frame, end_frame in segments:
@@ -215,17 +248,27 @@ class VideoPredictionMeta(models.Model):
215
248
  "video_file": video_obj,
216
249
  }
217
250
  if not LabelVideoSegment.objects.filter(
218
- video_file=video_obj, prediction_meta=self, label=label, start_frame_number=start_frame, end_frame_number=end_frame
251
+ video_file=video_obj,
252
+ prediction_meta=self,
253
+ label=label,
254
+ start_frame_number=start_frame,
255
+ end_frame_number=end_frame,
219
256
  ).exists():
220
257
  segments_to_create.append(LabelVideoSegment(**segment_data))
221
258
 
222
259
  if segments_to_create:
223
260
  LabelVideoSegment.objects.bulk_create(segments_to_create)
224
- logger.info(f"Created {len(segments_to_create)} video segments for label '{label.name}' in {video_obj}.")
261
+ logger.info(
262
+ f"Created {len(segments_to_create)} video segments for label '{label.name}' in {video_obj}."
263
+ )
225
264
  else:
226
- logger.info(f"No new video segments needed for label '{label.name}' in {video_obj}.")
265
+ logger.info(
266
+ f"No new video segments needed for label '{label.name}' in {video_obj}."
267
+ )
227
268
 
228
- def create_video_segments(self, segment_length_threshold_in_s: Optional[float] = None):
269
+ def create_video_segments(
270
+ self, segment_length_threshold_in_s: Optional[float] = None
271
+ ):
229
272
  """
230
273
  Generates LabelVideoSegments based on the stored prediction array.
231
274
  """
@@ -236,7 +279,9 @@ class VideoPredictionMeta(models.Model):
236
279
  fps = video_obj.get_fps()
237
280
 
238
281
  if fps is None or fps <= 0:
239
- logger.warning(f"Cannot create video segments for {video_obj} with invalid FPS ({fps}).")
282
+ logger.warning(
283
+ f"Cannot create video segments for {video_obj} with invalid FPS ({fps})."
284
+ )
240
285
  return
241
286
 
242
287
  min_frame_length = int(segment_length_threshold_in_s * fps)
@@ -250,17 +295,25 @@ class VideoPredictionMeta(models.Model):
250
295
  self.calculate_prediction_array()
251
296
  prediction_array = self.get_prediction_array()
252
297
  if prediction_array is None:
253
- logger.error(f"Failed to get or calculate prediction array for {self}. Cannot create segments.")
298
+ logger.error(
299
+ f"Failed to get or calculate prediction array for {self}. Cannot create segments."
300
+ )
254
301
  return
255
302
 
256
303
  if prediction_array.shape[1] != len(label_list):
257
- logger.warning(f"Prediction array shape {prediction_array.shape} incompatible with label list length {len(label_list)} for {self}.")
304
+ logger.warning(
305
+ f"Prediction array shape {prediction_array.shape} incompatible with label list length {len(label_list)} for {self}."
306
+ )
258
307
  return
259
308
 
260
- logger.info(f"Creating video segments for {self} (min length: {min_frame_length} frames)...")
309
+ logger.info(
310
+ f"Creating video segments for {self} (min length: {min_frame_length} frames)..."
311
+ )
261
312
  for i, label in enumerate(label_list):
262
313
  binary_predictions = prediction_array[:, i].astype(bool)
263
- segments = find_segments_in_prediction_array(binary_predictions, min_frame_length)
314
+ segments = find_segments_in_prediction_array(
315
+ binary_predictions, min_frame_length
316
+ )
264
317
  if segments:
265
318
  self.create_video_segments_for_label(segments, label)
266
319
  logger.info(f"Finished creating video segments for {self}.")
@@ -0,0 +1,63 @@
1
+ from django.conf import settings
2
+ from django.db import models
3
+
4
+
5
+ class OperationLog(models.Model):
6
+ """
7
+ Lightweight log of user-triggered operations (audit-like).
8
+ """
9
+
10
+ # actor_id – internal Django user ID (primary key)
11
+ # Who did it
12
+ actor_user = models.ForeignKey(
13
+ settings.AUTH_USER_MODEL,
14
+ on_delete=models.SET_NULL,
15
+ null=True,
16
+ blank=True,
17
+ related_name="operation_logs",
18
+ )
19
+ actor_username = models.CharField(max_length=150, blank=True)
20
+ actor_email = models.EmailField(blank=True)
21
+ actor_keycloak_id = models.CharField(
22
+ max_length=255,
23
+ blank=True,
24
+ help_text="Keycloak subject/ID if you later want to store it.",
25
+ )
26
+
27
+ # What happened
28
+ action = models.CharField(
29
+ max_length=100,
30
+ help_text="e.g. 'anonymization.start', 'anonymization.validate'",
31
+ )
32
+ http_method = models.CharField(max_length=10, blank=True)
33
+ path = models.CharField(max_length=512, blank=True)
34
+
35
+ # On what resource
36
+ resource_type = models.CharField(
37
+ max_length=50,
38
+ blank=True,
39
+ help_text="e.g. 'video', 'pdf'",
40
+ )
41
+ resource_id = models.IntegerField(
42
+ null=True,
43
+ blank=True,
44
+ help_text="ID of VideoFile / RawPdfFile etc.",
45
+ )
46
+
47
+ # State before/after
48
+ status_before = models.CharField(max_length=50, blank=True)
49
+ status_after = models.CharField(max_length=50, blank=True)
50
+
51
+ # Extra info
52
+ meta = models.JSONField(null=True, blank=True)
53
+
54
+ created_at = models.DateTimeField(auto_now_add=True)
55
+
56
+ #
57
+ class Meta:
58
+ verbose_name = "Operation Log"
59
+ verbose_name_plural = "Operation Logs"
60
+ ordering = ["-created_at"]
61
+
62
+ def __str__(self) -> str:
63
+ return f"[{self.created_at.isoformat()}] {self.action} by {self.actor_username or 'unknown'}"
@@ -22,19 +22,19 @@ from .unit import Unit
22
22
  from .tag import Tag
23
23
 
24
24
  __all__ = [
25
- 'Material',
26
- 'Resource',
27
- 'TransportRoute',
28
- 'Waste',
29
- 'BaseValueDistribution',
30
- 'NumericValueDistribution',
31
- 'SingleCategoricalValueDistribution',
32
- 'MultipleCategoricalValueDistribution',
33
- 'DateValueDistribution',
25
+ "Material",
26
+ "Resource",
27
+ "TransportRoute",
28
+ "Waste",
29
+ "BaseValueDistribution",
30
+ "NumericValueDistribution",
31
+ "SingleCategoricalValueDistribution",
32
+ "MultipleCategoricalValueDistribution",
33
+ "DateValueDistribution",
34
34
  "Gender",
35
35
  "InformationSource",
36
36
  "InformationSourceType",
37
37
  "Unit",
38
38
  "EmissionFactor",
39
39
  "Tag",
40
- ]
40
+ ]
@@ -1,18 +1,20 @@
1
- '''Module for distribution models.'''
1
+ """Module for distribution models."""
2
2
 
3
3
  from .base_value_distribution import BaseValueDistribution
4
4
  from .numeric_value_distribution import NumericValueDistribution
5
5
  from .single_categorical_value_distribution import SingleCategoricalValueDistribution
6
- from .multiple_categorical_value_distribution import MultipleCategoricalValueDistribution
6
+ from .multiple_categorical_value_distribution import (
7
+ MultipleCategoricalValueDistribution,
8
+ )
7
9
  from .date_value_distribution import DateValueDistribution
8
10
 
9
11
 
10
12
  __all__ = [
11
- 'BaseValueDistribution',
12
- 'NumericValueDistribution',
13
- 'SingleCategoricalValueDistribution',
14
- 'MultipleCategoricalValueDistribution',
15
- 'DateValueDistribution',
13
+ "BaseValueDistribution",
14
+ "NumericValueDistribution",
15
+ "SingleCategoricalValueDistribution",
16
+ "MultipleCategoricalValueDistribution",
17
+ "DateValueDistribution",
16
18
  ]
17
19
 
18
20
  # Example Usage
@@ -1,9 +1,11 @@
1
1
  from django.db import models
2
2
 
3
+
3
4
  class BaseValueDistribution(models.Model):
4
5
  """
5
6
  Abstract base class for value distributions.
6
7
  """
8
+
7
9
  name = models.CharField(max_length=100)
8
10
 
9
11
  class Meta:
@@ -15,6 +17,6 @@ class BaseValueDistribution(models.Model):
15
17
  Must be implemented by subclasses.
16
18
  """
17
19
  raise NotImplementedError("Subclasses must implement this method")
18
-
20
+
19
21
  def natural_key(self):
20
22
  return (self.name,)
@@ -43,7 +43,9 @@ class DateValueDistribution(BaseValueDistribution):
43
43
  date_min = models.DateField(blank=True, null=True)
44
44
  date_max = models.DateField(blank=True, null=True)
45
45
  date_mean = models.DateField(blank=True, null=True)
46
- date_std_dev = models.IntegerField(blank=True, null=True) # Standard deviation in days
46
+ date_std_dev = models.IntegerField(
47
+ blank=True, null=True
48
+ ) # Standard deviation in days
47
49
 
48
50
  # Timedelta-related fields
49
51
  timedelta_days_min = models.IntegerField(blank=True, null=True)
@@ -127,18 +129,30 @@ class DateValueDistribution(BaseValueDistribution):
127
129
  mean_ordinal = self.date_mean_safe.toordinal()
128
130
  std_dev_days = self.date_std_dev_safe
129
131
  random_ordinal = int(np.random.normal(mean_ordinal, std_dev_days))
130
- random_ordinal = np.clip(random_ordinal, self.date_min_safe.toordinal(), self.date_max_safe.toordinal())
132
+ random_ordinal = np.clip(
133
+ random_ordinal,
134
+ self.date_min_safe.toordinal(),
135
+ self.date_max_safe.toordinal(),
136
+ )
131
137
  return date.fromordinal(random_ordinal)
132
138
  else:
133
139
  raise ValueError("Unsupported distribution type")
134
140
 
135
141
  def _generate_timedelta_value(self):
136
142
  if self.distribution_type == "uniform":
137
- random_days = np.random.randint(self.timedelta_days_min_safe, self.timedelta_days_max_safe + 1)
143
+ random_days = np.random.randint(
144
+ self.timedelta_days_min_safe, self.timedelta_days_max_safe + 1
145
+ )
138
146
 
139
147
  elif self.distribution_type == "normal":
140
- random_days = int(np.random.normal(self.timedelta_days_mean_safe, self.timedelta_days_std_dev_safe))
141
- random_days = np.clip(random_days, self.timedelta_days_min_safe, self.timedelta_days_max_safe)
148
+ random_days = int(
149
+ np.random.normal(
150
+ self.timedelta_days_mean_safe, self.timedelta_days_std_dev_safe
151
+ )
152
+ )
153
+ random_days = np.clip(
154
+ random_days, self.timedelta_days_min_safe, self.timedelta_days_max_safe
155
+ )
142
156
 
143
157
  else:
144
158
  raise ValueError("Unsupported distribution type")
@@ -19,7 +19,9 @@ class MultipleCategoricalValueDistribution(BaseValueDistribution):
19
19
  categories = models.JSONField() # { "category": "probability", ... }
20
20
  min_count = models.IntegerField()
21
21
  max_count = models.IntegerField()
22
- count_distribution_type = models.CharField(max_length=20, choices=[("uniform", "Uniform"), ("normal", "Normal")])
22
+ count_distribution_type = models.CharField(
23
+ max_length=20, choices=[("uniform", "Uniform"), ("normal", "Normal")]
24
+ )
23
25
  count_mean = models.FloatField(null=True, blank=True)
24
26
  count_std_dev = models.FloatField(null=True, blank=True)
25
27
 
@@ -35,11 +35,27 @@ class NumericValueDistribution(BaseValueDistribution):
35
35
  distribution_type = models.CharField(max_length=20, choices=DISTRIBUTION_CHOICES)
36
36
  min_descriptor = models.CharField(max_length=20)
37
37
  max_descriptor = models.CharField(max_length=20)
38
- min_value = models.FloatField(blank=True, null=True, help_text="Lower hard limit for generated values")
39
- max_value = models.FloatField(blank=True, null=True, help_text="Upper hard limit for generated values")
40
- mean = models.FloatField(blank=True, null=True, help_text="Mean used for normal or skewed normal distributions")
41
- std_dev = models.FloatField(blank=True, null=True, help_text="Standard deviation for bell-shaped distributions")
42
- skewness = models.FloatField(blank=True, null=True, help_text="Shape parameter for skewed normal distributions")
38
+ min_value = models.FloatField(
39
+ blank=True, null=True, help_text="Lower hard limit for generated values"
40
+ )
41
+ max_value = models.FloatField(
42
+ blank=True, null=True, help_text="Upper hard limit for generated values"
43
+ )
44
+ mean = models.FloatField(
45
+ blank=True,
46
+ null=True,
47
+ help_text="Mean used for normal or skewed normal distributions",
48
+ )
49
+ std_dev = models.FloatField(
50
+ blank=True,
51
+ null=True,
52
+ help_text="Standard deviation for bell-shaped distributions",
53
+ )
54
+ skewness = models.FloatField(
55
+ blank=True,
56
+ null=True,
57
+ help_text="Shape parameter for skewed normal distributions",
58
+ )
43
59
 
44
60
  @property
45
61
  def min_value_safe(self):
@@ -74,7 +90,9 @@ class NumericValueDistribution(BaseValueDistribution):
74
90
  def generate_value(self, lab_value: "LabValue", patient: "Patient"):
75
91
  """Generate a value based on the distribution rules."""
76
92
 
77
- default_normal_range_dict = lab_value.get_normal_range(patient.age_safe, patient.gender)
93
+ default_normal_range_dict = lab_value.get_normal_range(
94
+ patient.age_safe, patient.gender
95
+ )
78
96
  assert isinstance(default_normal_range_dict, dict)
79
97
 
80
98
  if self.distribution_type == "uniform":
@@ -150,7 +168,10 @@ class NumericValueDistribution(BaseValueDistribution):
150
168
  value_function_dict = self.parse_value_descriptor(self.min_descriptor)
151
169
  value_function_dict.update(self.parse_value_descriptor(self.max_descriptor))
152
170
 
153
- result_dict = {key: value_function(default_normal_range_dict[key]) for key, value_function in value_function_dict.items()}
171
+ result_dict = {
172
+ key: value_function(default_normal_range_dict[key])
173
+ for key, value_function in value_function_dict.items()
174
+ }
154
175
 
155
176
  # generate value
156
177
  return float(np.random.uniform(result_dict["min"], result_dict["max"]))
@@ -179,8 +200,12 @@ class NumericValueDistribution(BaseValueDistribution):
179
200
 
180
201
  def _validate_normal_parameters(self) -> None:
181
202
  if self.mean is None or self.std_dev is None:
182
- raise ValueError(f"Normal distribution '{getattr(self, 'name', self.pk)}' requires both mean and std_dev.")
203
+ raise ValueError(
204
+ f"Normal distribution '{getattr(self, 'name', self.pk)}' requires both mean and std_dev."
205
+ )
183
206
 
184
207
  def _validate_skewed_normal_parameters(self) -> None:
185
208
  if self.mean is None or self.std_dev is None or self.skewness is None:
186
- raise ValueError(f"Skewed normal distribution '{getattr(self, 'name', self.pk)}' requires mean, std_dev, and skewness.")
209
+ raise ValueError(
210
+ f"Skewed normal distribution '{getattr(self, 'name', self.pk)}' requires mean, std_dev, and skewness."
211
+ )
@@ -1,5 +1,5 @@
1
1
  from .emission_factor import EmissionFactor
2
2
 
3
3
  __all__ = [
4
- 'EmissionFactor',
4
+ "EmissionFactor",
5
5
  ]
@@ -86,9 +86,15 @@ class EmissionFactor(models.Model):
86
86
  from ...administration.product import ReferenceProduct
87
87
 
88
88
  reference_products = []
89
- reference_products += ReferenceProduct.objects.filter(emission_factor_total=self)
90
- reference_products += ReferenceProduct.objects.filter(emission_factor_package=self)
91
- reference_products += ReferenceProduct.objects.filter(emission_factor_product=self)
89
+ reference_products += ReferenceProduct.objects.filter(
90
+ emission_factor_total=self
91
+ )
92
+ reference_products += ReferenceProduct.objects.filter(
93
+ emission_factor_package=self
94
+ )
95
+ reference_products += ReferenceProduct.objects.filter(
96
+ emission_factor_product=self
97
+ )
92
98
 
93
99
  return reference_products
94
100
 
@@ -58,19 +58,27 @@ class InformationSource(models.Model):
58
58
  def examinations(self) -> "models.manager.RelatedManager[Examination]": ...
59
59
 
60
60
  @property
61
- def examination_indications(self) -> "models.manager.RelatedManager[ExaminationIndication]": ...
61
+ def examination_indications(
62
+ self,
63
+ ) -> "models.manager.RelatedManager[ExaminationIndication]": ...
62
64
 
63
65
  @property
64
- def examination_times(self) -> "models.manager.RelatedManager[ExaminationTime]": ...
66
+ def examination_times(
67
+ self,
68
+ ) -> "models.manager.RelatedManager[ExaminationTime]": ...
65
69
 
66
70
  @property
67
71
  def findings(self) -> "models.manager.RelatedManager[Finding]": ...
68
72
 
69
73
  @property
70
- def finding_interventions(self) -> "models.manager.RelatedManager[FindingIntervention]": ...
74
+ def finding_interventions(
75
+ self,
76
+ ) -> "models.manager.RelatedManager[FindingIntervention]": ...
71
77
 
72
78
  @property
73
- def finding_classifications(self) -> "models.manager.RelatedManager[FindingClassification]": ...
79
+ def finding_classifications(
80
+ self,
81
+ ) -> "models.manager.RelatedManager[FindingClassification]": ...
74
82
 
75
83
  class Meta:
76
84
  verbose_name = "Information Source"
@@ -145,7 +153,9 @@ class InformationSourceType(models.Model):
145
153
  try:
146
154
  return cls.objects.get(name="prediction")
147
155
  except cls.DoesNotExist as e:
148
- raise cls.DoesNotExist("The 'prediction' InformationSourceType was not found. Please check your data fixtures or initial data migrations.") from e
156
+ raise cls.DoesNotExist(
157
+ "The 'prediction' InformationSourceType was not found. Please check your data fixtures or initial data migrations."
158
+ ) from e
149
159
 
150
160
  @classmethod
151
161
  def get_manual_annotation_type(cls) -> "InformationSourceType":
@@ -16,7 +16,9 @@ class Material(models.Model):
16
16
  objects = MaterialManager()
17
17
 
18
18
  name = models.CharField(max_length=255)
19
- emission_factor = models.ForeignKey("EmissionFactor", on_delete=models.SET_NULL, null=True)
19
+ emission_factor = models.ForeignKey(
20
+ "EmissionFactor", on_delete=models.SET_NULL, null=True
21
+ )
20
22
 
21
23
  if TYPE_CHECKING:
22
24
  emission_factor: models.ForeignKey["EmissionFactor|None"]
@@ -20,7 +20,9 @@ class TransportRoute(models.Model):
20
20
 
21
21
  distance = models.FloatField()
22
22
  name = models.CharField(max_length=255)
23
- emission_factor = models.ForeignKey("EmissionFactor", on_delete=models.SET_NULL, null=True)
23
+ emission_factor = models.ForeignKey(
24
+ "EmissionFactor", on_delete=models.SET_NULL, null=True
25
+ )
24
26
  unit = models.ForeignKey("Unit", on_delete=models.SET_NULL, null=True)
25
27
 
26
28
  if TYPE_CHECKING:
@@ -15,8 +15,12 @@ class Unit(models.Model):
15
15
  objects = UnitManager()
16
16
 
17
17
  name = models.CharField(max_length=100) # e.g. "Centimeter"
18
- description = models.CharField(max_length=100, blank=True, null=True) # e.g. "centimeters", "milimeters", "inches"
19
- abbreviation = models.CharField(max_length=25, blank=True, null=True) # e.g. "cm", "mm", "in"
18
+ description = models.CharField(
19
+ max_length=100, blank=True, null=True
20
+ ) # e.g. "centimeters", "milimeters", "inches"
21
+ abbreviation = models.CharField(
22
+ max_length=25, blank=True, null=True
23
+ ) # e.g. "cm", "mm", "in"
20
24
 
21
25
  if TYPE_CHECKING:
22
26
  unit_product_materials: models.QuerySet["ProductMaterial"]
@@ -3,4 +3,3 @@ from django.db import models
3
3
 
4
4
  class Report(models.Model):
5
5
  name = models.CharField(max_length=100, unique=True)
6
-