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

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

Potentially problematic release.


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

Files changed (372) hide show
  1. endoreg_db/authz/auth.py +74 -0
  2. endoreg_db/authz/backends.py +168 -0
  3. endoreg_db/authz/management/commands/list_routes.py +18 -0
  4. endoreg_db/authz/middleware.py +83 -0
  5. endoreg_db/authz/permissions.py +127 -0
  6. endoreg_db/authz/policy.py +218 -0
  7. endoreg_db/authz/views_auth.py +66 -0
  8. endoreg_db/config/env.py +13 -8
  9. endoreg_db/data/__init__.py +8 -31
  10. endoreg_db/data/_examples/disease.yaml +55 -0
  11. endoreg_db/data/_examples/disease_classification.yaml +13 -0
  12. endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
  13. endoreg_db/data/_examples/event.yaml +64 -0
  14. endoreg_db/data/_examples/examination.yaml +72 -0
  15. endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
  16. endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
  17. endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
  18. endoreg_db/data/_examples/finding/complication.yaml +16 -0
  19. endoreg_db/data/_examples/finding/data.yaml +105 -0
  20. endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
  21. endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
  22. endoreg_db/data/_examples/finding/outcome.yaml +12 -0
  23. endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
  24. endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
  25. endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
  26. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
  27. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
  28. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
  29. endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
  30. endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
  31. endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
  32. endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
  33. endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
  34. endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
  35. endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
  36. endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
  37. endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
  38. endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
  39. endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
  40. endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
  41. endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
  42. endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
  43. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
  44. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
  45. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
  46. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
  47. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
  48. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
  49. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
  50. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
  51. endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
  52. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
  53. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
  54. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
  55. endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
  56. endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
  57. endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
  58. endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
  59. endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
  60. endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
  61. endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
  62. endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
  63. endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
  64. endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
  65. endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
  66. endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
  67. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
  68. endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
  69. endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
  70. endoreg_db/data/_examples/finding_type/data.yaml +43 -0
  71. endoreg_db/data/_examples/requirement/age.yaml +26 -0
  72. endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
  73. endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
  74. endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
  75. endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
  76. endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
  77. endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
  78. endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
  79. endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
  80. endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
  81. endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
  82. endoreg_db/data/_examples/requirement/gender.yaml +25 -0
  83. endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
  84. endoreg_db/data/_examples/requirement/medication.yaml +93 -0
  85. endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
  86. endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
  87. endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
  88. endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
  89. endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
  90. endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
  91. endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
  92. endoreg_db/data/event_classification/data.yaml +4 -0
  93. endoreg_db/data/event_classification_choice/data.yaml +9 -0
  94. endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
  95. endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
  96. endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
  97. endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
  98. endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
  99. endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
  100. endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
  101. endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
  102. endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
  103. endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
  104. endoreg_db/data/requirement_set/_old_ +109 -0
  105. endoreg_db/data/requirement_set_type/data.yaml +21 -0
  106. endoreg_db/data/setup_config.yaml +4 -4
  107. endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
  108. endoreg_db/exceptions.py +5 -2
  109. endoreg_db/helpers/data_loader.py +1 -1
  110. endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
  111. endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
  112. endoreg_db/management/commands/import_video.py +9 -10
  113. endoreg_db/management/commands/import_video_with_classification.py +1 -1
  114. endoreg_db/management/commands/init_default_ai_model.py +1 -1
  115. endoreg_db/management/commands/list_routes.py +18 -0
  116. endoreg_db/management/commands/load_ai_model_data.py +2 -1
  117. endoreg_db/management/commands/load_center_data.py +12 -12
  118. endoreg_db/management/commands/load_requirement_data.py +60 -31
  119. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  120. endoreg_db/management/commands/setup_endoreg_db.py +14 -10
  121. endoreg_db/management/commands/storage_management.py +271 -203
  122. endoreg_db/migrations/0001_initial.py +1799 -1300
  123. endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
  124. endoreg_db/migrations/_old/0001_initial.py +1857 -0
  125. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
  126. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
  127. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
  128. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
  129. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
  130. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
  131. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
  132. endoreg_db/models/__init__.py +78 -123
  133. endoreg_db/models/administration/__init__.py +21 -42
  134. endoreg_db/models/administration/ai/active_model.py +2 -2
  135. endoreg_db/models/administration/ai/ai_model.py +7 -6
  136. endoreg_db/models/administration/case/__init__.py +1 -15
  137. endoreg_db/models/administration/case/case.py +3 -3
  138. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  139. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  140. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  141. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  142. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  143. endoreg_db/models/administration/center/center.py +33 -19
  144. endoreg_db/models/administration/center/center_product.py +12 -9
  145. endoreg_db/models/administration/center/center_resource.py +25 -19
  146. endoreg_db/models/administration/center/center_shift.py +21 -17
  147. endoreg_db/models/administration/center/center_waste.py +16 -8
  148. endoreg_db/models/administration/person/__init__.py +2 -0
  149. endoreg_db/models/administration/person/employee/employee.py +10 -5
  150. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  151. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  152. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  153. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  154. endoreg_db/models/administration/person/patient/patient.py +103 -100
  155. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  156. endoreg_db/models/administration/person/person.py +4 -0
  157. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  158. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  159. endoreg_db/models/administration/product/product.py +20 -15
  160. endoreg_db/models/administration/product/product_material.py +17 -18
  161. endoreg_db/models/administration/product/product_weight.py +12 -8
  162. endoreg_db/models/administration/product/reference_product.py +23 -55
  163. endoreg_db/models/administration/qualification/qualification.py +7 -3
  164. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  165. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  166. endoreg_db/models/administration/shift/shift.py +16 -12
  167. endoreg_db/models/administration/shift/shift_type.py +23 -31
  168. endoreg_db/models/label/__init__.py +7 -8
  169. endoreg_db/models/label/annotation/image_classification.py +10 -9
  170. endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
  171. endoreg_db/models/label/label.py +15 -15
  172. endoreg_db/models/label/label_set.py +19 -6
  173. endoreg_db/models/label/label_type.py +1 -1
  174. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  175. endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
  176. endoreg_db/models/label/video_segmentation_label.py +4 -0
  177. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  178. endoreg_db/models/media/frame/frame.py +22 -22
  179. endoreg_db/models/media/pdf/raw_pdf.py +249 -177
  180. endoreg_db/models/media/pdf/report_file.py +25 -29
  181. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
  182. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  183. endoreg_db/models/media/video/__init__.py +1 -0
  184. endoreg_db/models/media/video/create_from_file.py +48 -56
  185. endoreg_db/models/media/video/pipe_1.py +30 -33
  186. endoreg_db/models/media/video/pipe_2.py +8 -9
  187. endoreg_db/models/media/video/video_file.py +359 -204
  188. endoreg_db/models/media/video/video_file_ai.py +288 -74
  189. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  190. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  191. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  192. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  193. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  194. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  195. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  196. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  197. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  198. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  199. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  200. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  201. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  202. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  203. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  204. endoreg_db/models/media/video/video_file_io.py +109 -62
  205. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  206. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  207. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  208. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  209. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  210. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  211. endoreg_db/models/media/video/video_file_segments.py +24 -17
  212. endoreg_db/models/media/video/video_metadata.py +19 -35
  213. endoreg_db/models/media/video/video_processing.py +96 -95
  214. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  215. endoreg_db/models/medical/disease.py +22 -16
  216. endoreg_db/models/medical/event.py +31 -18
  217. endoreg_db/models/medical/examination/__init__.py +13 -6
  218. endoreg_db/models/medical/examination/examination.py +17 -18
  219. endoreg_db/models/medical/examination/examination_indication.py +26 -25
  220. endoreg_db/models/medical/examination/examination_time.py +16 -6
  221. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  222. endoreg_db/models/medical/examination/examination_type.py +3 -4
  223. endoreg_db/models/medical/finding/finding.py +38 -39
  224. endoreg_db/models/medical/finding/finding_classification.py +37 -48
  225. endoreg_db/models/medical/finding/finding_intervention.py +27 -22
  226. endoreg_db/models/medical/finding/finding_type.py +13 -12
  227. endoreg_db/models/medical/hardware/endoscope.py +20 -26
  228. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  229. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  230. endoreg_db/models/medical/medication/medication.py +22 -10
  231. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  232. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  233. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  234. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  235. endoreg_db/models/medical/organ/__init__.py +15 -12
  236. endoreg_db/models/medical/patient/medication_examples.py +1 -5
  237. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  238. endoreg_db/models/medical/patient/patient_event.py +19 -22
  239. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  240. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  241. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  242. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  243. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  244. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  245. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  246. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  247. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  248. endoreg_db/models/medical/risk/risk.py +7 -6
  249. endoreg_db/models/medical/risk/risk_type.py +8 -5
  250. endoreg_db/models/metadata/model_meta.py +60 -29
  251. endoreg_db/models/metadata/model_meta_logic.py +139 -18
  252. endoreg_db/models/metadata/pdf_meta.py +19 -24
  253. endoreg_db/models/metadata/sensitive_meta.py +102 -85
  254. endoreg_db/models/metadata/sensitive_meta_logic.py +383 -43
  255. endoreg_db/models/metadata/video_meta.py +51 -31
  256. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  257. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  258. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  259. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  260. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  261. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  262. endoreg_db/models/other/emission/emission_factor.py +18 -8
  263. endoreg_db/models/other/gender.py +10 -5
  264. endoreg_db/models/other/information_source.py +25 -25
  265. endoreg_db/models/other/material.py +9 -5
  266. endoreg_db/models/other/resource.py +6 -4
  267. endoreg_db/models/other/tag.py +10 -5
  268. endoreg_db/models/other/transport_route.py +13 -8
  269. endoreg_db/models/other/unit.py +10 -6
  270. endoreg_db/models/other/waste.py +6 -5
  271. endoreg_db/models/requirement/requirement.py +580 -272
  272. endoreg_db/models/requirement/requirement_error.py +85 -0
  273. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  274. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  275. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  276. endoreg_db/models/requirement/requirement_operator.py +36 -33
  277. endoreg_db/models/requirement/requirement_set.py +74 -57
  278. endoreg_db/models/state/__init__.py +4 -4
  279. endoreg_db/models/state/abstract.py +2 -2
  280. endoreg_db/models/state/anonymization.py +12 -0
  281. endoreg_db/models/state/audit_ledger.py +46 -47
  282. endoreg_db/models/state/label_video_segment.py +9 -0
  283. endoreg_db/models/state/raw_pdf.py +40 -46
  284. endoreg_db/models/state/sensitive_meta.py +6 -2
  285. endoreg_db/models/state/video.py +58 -53
  286. endoreg_db/models/upload_job.py +32 -55
  287. endoreg_db/models/utils.py +1 -2
  288. endoreg_db/root_urls.py +21 -2
  289. endoreg_db/serializers/__init__.py +26 -57
  290. endoreg_db/serializers/anonymization.py +18 -10
  291. endoreg_db/serializers/meta/report_meta.py +1 -1
  292. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  293. endoreg_db/serializers/misc/__init__.py +1 -1
  294. endoreg_db/serializers/misc/file_overview.py +33 -91
  295. endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
  296. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  297. endoreg_db/serializers/video/segmentation.py +2 -1
  298. endoreg_db/serializers/video/video_processing_history.py +20 -5
  299. endoreg_db/serializers/video_examination.py +198 -0
  300. endoreg_db/services/anonymization.py +75 -73
  301. endoreg_db/services/lookup_service.py +256 -73
  302. endoreg_db/services/lookup_store.py +174 -30
  303. endoreg_db/services/pdf_import.py +711 -310
  304. endoreg_db/services/storage_aware_video_processor.py +140 -114
  305. endoreg_db/services/video_import.py +266 -117
  306. endoreg_db/urls/__init__.py +27 -27
  307. endoreg_db/urls/label_video_segments.py +2 -0
  308. endoreg_db/urls/media.py +108 -66
  309. endoreg_db/urls/root_urls.py +29 -0
  310. endoreg_db/utils/__init__.py +15 -5
  311. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  312. endoreg_db/utils/case_generator/__init__.py +3 -0
  313. endoreg_db/utils/dataloader.py +88 -16
  314. endoreg_db/utils/defaults/set_default_center.py +32 -0
  315. endoreg_db/utils/names.py +22 -16
  316. endoreg_db/utils/permissions.py +2 -1
  317. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  318. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
  319. endoreg_db/utils/setup_config.py +8 -5
  320. endoreg_db/utils/storage.py +115 -0
  321. endoreg_db/utils/validate_endo_roi.py +8 -2
  322. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  323. endoreg_db/views/__init__.py +5 -12
  324. endoreg_db/views/anonymization/media_management.py +198 -163
  325. endoreg_db/views/anonymization/overview.py +4 -1
  326. endoreg_db/views/anonymization/validate.py +174 -40
  327. endoreg_db/views/media/__init__.py +2 -0
  328. endoreg_db/views/media/pdf_media.py +131 -150
  329. endoreg_db/views/media/sensitive_metadata.py +46 -6
  330. endoreg_db/views/media/video_media.py +89 -82
  331. endoreg_db/views/media/video_segments.py +187 -260
  332. endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
  333. endoreg_db/views/patient/patient.py +5 -4
  334. endoreg_db/views/pdf/__init__.py +5 -8
  335. endoreg_db/views/pdf/pdf_stream.py +186 -0
  336. endoreg_db/views/pdf/pdf_stream_views.py +0 -127
  337. endoreg_db/views/pdf/reimport.py +86 -91
  338. endoreg_db/views/requirement/evaluate.py +188 -187
  339. endoreg_db/views/requirement/lookup.py +186 -288
  340. endoreg_db/views/requirement/requirement_utils.py +89 -0
  341. endoreg_db/views/video/__init__.py +0 -4
  342. endoreg_db/views/video/correction.py +2 -2
  343. endoreg_db/views/video/video_examination_viewset.py +202 -289
  344. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
  345. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +350 -255
  346. endoreg_db/models/administration/permissions/__init__.py +0 -44
  347. endoreg_db/models/media/video/refactor_plan.md +0 -0
  348. endoreg_db/models/media/video/video_file_frames.py +0 -0
  349. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  350. endoreg_db/models/rule/__init__.py +0 -13
  351. endoreg_db/models/rule/rule.py +0 -27
  352. endoreg_db/models/rule/rule_applicator.py +0 -224
  353. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  354. endoreg_db/models/rule/rule_type.py +0 -20
  355. endoreg_db/models/rule/ruleset.py +0 -17
  356. endoreg_db/serializers/video/video_metadata.py +0 -105
  357. endoreg_db/urls/report.py +0 -48
  358. endoreg_db/urls/video.py +0 -61
  359. endoreg_db/utils/case_generator/case_generator.py +0 -159
  360. endoreg_db/utils/case_generator/utils.py +0 -30
  361. endoreg_db/views/pdf/pdf_media.py +0 -239
  362. endoreg_db/views/report/__init__.py +0 -9
  363. endoreg_db/views/report/report_list.py +0 -112
  364. endoreg_db/views/report/report_with_secure_url.py +0 -28
  365. endoreg_db/views/report/start_examination.py +0 -7
  366. endoreg_db/views/video/video_media.py +0 -158
  367. endoreg_db/views.py +0 -0
  368. /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
  369. /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
  370. /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
  371. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
  372. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
@@ -4,148 +4,149 @@ Video Processing History Model
4
4
  Tracks all video correction operations (masking, frame removal, reprocessing).
5
5
  Created as part of Phase 1.1: Video Correction API Endpoints.
6
6
  """
7
+
8
+ import logging
9
+ from pathlib import Path
10
+
11
+ from django.conf import settings
7
12
  from django.db import models
8
13
  from django.utils import timezone
14
+
9
15
  from .video_file import VideoFile
10
16
 
17
+ logger = logging.getLogger(__name__)
18
+
19
+
11
20
  class VideoProcessingHistory(models.Model):
12
21
  """
13
22
  History of all video processing operations.
14
-
23
+
15
24
  Stores configuration and results of masking, frame removal, and reprocessing
16
25
  operations for audit trail and download access.
17
26
  """
18
-
27
+
19
28
  # Operation Types
20
- OPERATION_MASKING = 'mask_overlay'
21
- OPERATION_FRAME_REMOVAL = 'frame_removal'
22
- OPERATION_ANALYSIS = 'analysis'
23
- OPERATION_REPROCESSING = 'reprocessing'
24
-
29
+ OPERATION_MASKING = "mask_overlay"
30
+ OPERATION_FRAME_REMOVAL = "frame_removal"
31
+ OPERATION_ANALYSIS = "analysis"
32
+ OPERATION_REPROCESSING = "reprocessing"
33
+
25
34
  OPERATION_CHOICES = [
26
- (OPERATION_MASKING, 'Mask Overlay'),
27
- (OPERATION_FRAME_REMOVAL, 'Frame Removal'),
28
- (OPERATION_ANALYSIS, 'Sensitivity Analysis'),
29
- (OPERATION_REPROCESSING, 'Full Reprocessing'),
35
+ (OPERATION_MASKING, "Mask Overlay"),
36
+ (OPERATION_FRAME_REMOVAL, "Frame Removal"),
37
+ (OPERATION_ANALYSIS, "Sensitivity Analysis"),
38
+ (OPERATION_REPROCESSING, "Full Reprocessing"),
30
39
  ]
31
-
40
+
32
41
  # Status Types
33
- STATUS_PENDING = 'pending'
34
- STATUS_RUNNING = 'running'
35
- STATUS_SUCCESS = 'success'
36
- STATUS_FAILURE = 'failure'
37
- STATUS_CANCELLED = 'cancelled'
38
-
42
+ STATUS_PENDING = "pending"
43
+ STATUS_RUNNING = "running"
44
+ STATUS_SUCCESS = "success"
45
+ STATUS_FAILURE = "failure"
46
+ STATUS_CANCELLED = "cancelled"
47
+
39
48
  STATUS_CHOICES = [
40
- (STATUS_PENDING, 'Pending'),
41
- (STATUS_RUNNING, 'Running'),
42
- (STATUS_SUCCESS, 'Success'),
43
- (STATUS_FAILURE, 'Failure'),
44
- (STATUS_CANCELLED, 'Cancelled'),
49
+ (STATUS_PENDING, "Pending"),
50
+ (STATUS_RUNNING, "Running"),
51
+ (STATUS_SUCCESS, "Success"),
52
+ (STATUS_FAILURE, "Failure"),
53
+ (STATUS_CANCELLED, "Cancelled"),
45
54
  ]
46
-
47
- video = models.ForeignKey(
48
- VideoFile,
49
- on_delete=models.CASCADE,
50
- related_name='processing_history',
51
- help_text="Video file this operation was performed on"
52
- )
53
-
54
- operation = models.CharField(
55
- max_length=50,
56
- choices=OPERATION_CHOICES,
57
- help_text="Type of processing operation"
58
- )
59
-
60
- status = models.CharField(
61
- max_length=20,
62
- choices=STATUS_CHOICES,
63
- default=STATUS_PENDING,
64
- help_text="Current status of the operation"
65
- )
66
-
55
+
56
+ video = models.ForeignKey(VideoFile, on_delete=models.CASCADE, related_name="processing_history", help_text="Video file this operation was performed on")
57
+
58
+ operation = models.CharField(max_length=50, choices=OPERATION_CHOICES, help_text="Type of processing operation")
59
+
60
+ status = models.CharField(max_length=20, choices=STATUS_CHOICES, default=STATUS_PENDING, help_text="Current status of the operation")
61
+
67
62
  # Configuration & Results
68
- config = models.JSONField(
69
- default=dict,
70
- help_text="Operation configuration (mask settings, frame list, etc.)"
71
- )
72
-
73
- output_file = models.CharField(
74
- max_length=500,
75
- blank=True,
76
- help_text="Path to output file (relative to MEDIA_ROOT)"
77
- )
78
-
79
- details = models.TextField(
80
- blank=True,
81
- help_text="Additional details or error messages"
82
- )
83
-
63
+ config = models.JSONField(default=dict, help_text="Operation configuration (mask settings, frame list, etc.)")
64
+
65
+ output_file = models.CharField(max_length=500, blank=True, help_text="Path to output file (relative to MEDIA_ROOT)")
66
+
67
+ details = models.TextField(blank=True, help_text="Additional details or error messages")
68
+
84
69
  # Celery Integration
85
- task_id = models.CharField(
86
- max_length=100,
87
- blank=True,
88
- help_text="Celery task ID for progress tracking"
89
- )
90
-
70
+ task_id = models.CharField(max_length=100, blank=True, help_text="Celery task ID for progress tracking")
71
+
91
72
  # Timestamps
92
- created_at = models.DateTimeField(
93
- auto_now_add=True,
94
- help_text="When the operation was started"
95
- )
96
-
97
- completed_at = models.DateTimeField(
98
- null=True,
99
- blank=True,
100
- help_text="When the operation completed (success or failure)"
101
- )
102
-
73
+ created_at = models.DateTimeField(auto_now_add=True, help_text="When the operation was started")
74
+
75
+ completed_at = models.DateTimeField(null=True, blank=True, help_text="When the operation completed (success or failure)")
76
+
103
77
  class Meta:
104
- db_table = 'video_processing_history'
105
- verbose_name = 'Video Processing History'
106
- verbose_name_plural = 'Video Processing Histories'
107
- ordering = ['-created_at']
78
+ db_table = "video_processing_history"
79
+ verbose_name = "Video Processing History"
80
+ verbose_name_plural = "Video Processing Histories"
81
+ ordering = ["-created_at"]
108
82
  indexes = [
109
- models.Index(fields=['video', '-created_at']),
110
- models.Index(fields=['task_id']),
111
- models.Index(fields=['status']),
83
+ models.Index(fields=["video", "-created_at"]),
84
+ models.Index(fields=["task_id"]),
85
+ models.Index(fields=["status"]),
112
86
  ]
113
-
87
+
114
88
  def __str__(self):
115
- return f"{self.get_operation_display()} on {self.video.uuid} - {self.get_status_display()}"
116
-
89
+ operation_display = getattr(self, "get_operation_display", None)
90
+ status_display = getattr(self, "get_status_display", None)
91
+
92
+ operation = operation_display() if callable(operation_display) else self.operation
93
+ status = status_display() if callable(status_display) else self.status
94
+ return f"{operation} on {self.video.uuid} - {status}"
95
+
117
96
  def mark_running(self, save=True):
118
97
  """Mark operation as running."""
119
98
  self.status = self.STATUS_RUNNING
120
99
  if save:
121
- self.save(update_fields=['status'])
122
-
100
+ self.save(update_fields=["status"])
101
+
102
+ @staticmethod
103
+ def _normalize_output_path(path: str | Path | None) -> str:
104
+ """Return a path relative to MEDIA_ROOT when possible."""
105
+ if not path:
106
+ return ""
107
+
108
+ raw_path = Path(path)
109
+ if not raw_path.is_absolute():
110
+ return raw_path.as_posix()
111
+ media_root = Path(settings.MEDIA_ROOT)
112
+
113
+ try:
114
+ relative = raw_path.resolve().relative_to(media_root.resolve())
115
+ return relative.as_posix()
116
+ except (ValueError, RuntimeError):
117
+ logger.warning(
118
+ "Storing absolute output path '%s' because it is outside MEDIA_ROOT (%s).",
119
+ raw_path,
120
+ media_root,
121
+ )
122
+ return raw_path.as_posix()
123
+
123
124
  def mark_success(self, output_file=None, details=None, save=True):
124
125
  """Mark operation as successful."""
125
126
  self.status = self.STATUS_SUCCESS
126
127
  self.completed_at = timezone.now()
127
128
  if output_file:
128
- self.output_file = output_file
129
+ self.output_file = self._normalize_output_path(output_file)
129
130
  if details:
130
131
  self.details = details
131
132
  if save:
132
- self.save(update_fields=['status', 'completed_at', 'output_file', 'details'])
133
-
133
+ self.save(update_fields=["status", "completed_at", "output_file", "details"])
134
+
134
135
  def mark_failure(self, error_message, save=True):
135
136
  """Mark operation as failed."""
136
137
  self.status = self.STATUS_FAILURE
137
138
  self.completed_at = timezone.now()
138
139
  self.details = error_message
139
140
  if save:
140
- self.save(update_fields=['status', 'completed_at', 'details'])
141
-
141
+ self.save(update_fields=["status", "completed_at", "details"])
142
+
142
143
  @property
143
144
  def duration(self):
144
145
  """Calculate operation duration if completed."""
145
146
  if self.completed_at and self.created_at:
146
147
  return (self.completed_at - self.created_at).total_seconds()
147
148
  return None
148
-
149
+
149
150
  @property
150
151
  def is_complete(self) -> bool:
151
152
  """Check if operation is in a terminal state."""
@@ -1,17 +1,27 @@
1
+ from typing import TYPE_CHECKING
2
+
1
3
  from django.db import models
2
4
 
5
+
3
6
  class ContraindicationManager(models.Manager):
4
7
  def get_by_natural_key(self, name):
5
8
  return self.get(name=name)
6
-
9
+
10
+
7
11
  class Contraindication(models.Model):
8
12
  name = models.CharField(max_length=100, unique=True)
9
13
  description = models.TextField(blank=True, null=True)
10
14
 
11
15
  objects = ContraindicationManager()
12
-
16
+
17
+ if TYPE_CHECKING:
18
+ from endoreg_db.models import FindingIntervention
19
+
20
+ @property
21
+ def contraindicating_finding_interventions(self) -> "models.manager.RelatedManager[FindingIntervention]": ...
22
+
13
23
  def natural_key(self):
14
24
  return (self.name,)
15
-
25
+
16
26
  def __str__(self):
17
27
  return str(self.name)
@@ -1,5 +1,6 @@
1
+ from typing import TYPE_CHECKING, List
2
+
1
3
  from django.db import models
2
- from typing import List, TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
5
6
  from .patient import PatientDisease
@@ -27,6 +28,7 @@ class Disease(models.Model):
27
28
 
28
29
  Can define associated subcategories and numerical descriptors applicable to the disease itself.
29
30
  """
31
+
30
32
  name = models.CharField(max_length=255, unique=True)
31
33
  subcategories = models.JSONField(default=dict)
32
34
  numerical_descriptors = models.JSONField(default=dict)
@@ -34,8 +36,12 @@ class Disease(models.Model):
34
36
  objects = DiseaseManager()
35
37
 
36
38
  if TYPE_CHECKING:
37
- disease_classifications: models.QuerySet["DiseaseClassification"]
38
- patient_diseases: models.QuerySet["PatientDisease"]
39
+
40
+ @property
41
+ def disease_classifications(self) -> models.QuerySet["DiseaseClassification"]: ...
42
+
43
+ @property
44
+ def patient_diseases(self) -> models.QuerySet["PatientDisease"]: ...
39
45
 
40
46
  def natural_key(self):
41
47
  """Returns the natural key (name) as a tuple."""
@@ -52,9 +58,7 @@ class Disease(models.Model):
52
58
  Returns:
53
59
  List[DiseaseClassification]: A list of related disease classification objects.
54
60
  """
55
- classifications: List[DiseaseClassification] = [
56
- _ for _ in self.disease_classifications.all()
57
- ]
61
+ classifications: List[DiseaseClassification] = [_ for _ in self.disease_classifications.all()]
58
62
  return classifications
59
63
 
60
64
 
@@ -78,17 +82,18 @@ class DiseaseClassification(models.Model):
78
82
  """
79
83
  Represents a classification system applicable to a specific disease (e.g., Forrest classification for ulcers).
80
84
  """
85
+
81
86
  name = models.CharField(max_length=255, unique=True)
82
87
 
83
- disease = models.ForeignKey(
84
- Disease, on_delete=models.CASCADE, related_name="disease_classifications"
85
- )
88
+ disease = models.ForeignKey(Disease, on_delete=models.CASCADE, related_name="disease_classifications")
86
89
 
87
90
  objects = DiseaseClassificationManager()
88
91
 
89
92
  if TYPE_CHECKING:
90
- disease: "Disease"
91
- disease_classification_choices: models.QuerySet["DiseaseClassificationChoice"]
93
+ disease: models.ForeignKey["Disease"]
94
+
95
+ @property
96
+ def disease_classification_choices(self) -> models.manager.RelatedManager["DiseaseClassificationChoice"]: ...
92
97
 
93
98
  def natural_key(self):
94
99
  """Returns the natural key (name) as a tuple."""
@@ -105,9 +110,7 @@ class DiseaseClassification(models.Model):
105
110
  Returns:
106
111
  List[DiseaseClassificationChoice]: A list of related disease classification choices.
107
112
  """
108
- choices: List[DiseaseClassificationChoice] = [
109
- _ for _ in self.disease_classification_choices.all()
110
- ]
113
+ choices: List[DiseaseClassificationChoice] = [_ for _ in self.disease_classification_choices.all()]
111
114
  return choices
112
115
 
113
116
 
@@ -133,6 +136,7 @@ class DiseaseClassificationChoice(models.Model):
133
136
  """
134
137
  Represents a specific choice within a disease classification system (e.g., Forrest IIa).
135
138
  """
139
+
136
140
  name = models.CharField(max_length=255, unique=True)
137
141
 
138
142
  disease_classification = models.ForeignKey(
@@ -144,8 +148,10 @@ class DiseaseClassificationChoice(models.Model):
144
148
  objects = DiseaseClassificationChoiceManager()
145
149
 
146
150
  if TYPE_CHECKING:
147
- disease_classification: "DiseaseClassification"
148
- patient_diseases: models.QuerySet["PatientDisease"]
151
+ disease_classification: models.ForeignKey["DiseaseClassification"]
152
+
153
+ @property
154
+ def patient_diseases(self) -> models.manager.RelatedManager["PatientDisease"]: ...
149
155
 
150
156
  def natural_key(self):
151
157
  """Returns the natural key (name) as a tuple."""
@@ -1,5 +1,6 @@
1
+ from typing import TYPE_CHECKING, List
2
+
1
3
  from django.db import models
2
- from typing import List, TYPE_CHECKING
3
4
 
4
5
  if TYPE_CHECKING:
5
6
  from endoreg_db.models import PatientEvent
@@ -13,14 +14,14 @@ class EventManager(models.Manager):
13
14
  def get_by_natural_key(self, name: str) -> "Event":
14
15
  """
15
16
  Retrieves an Event instance using its natural key.
16
-
17
+
17
18
  This method returns the event whose name matches the provided natural key.
18
- It is primarily used to support Django's natural key serialization during
19
+ It is primarily used to support Django's natural key serialization during
19
20
  data import/export and deserialization processes.
20
-
21
+
21
22
  Args:
22
23
  name: The unique event name serving as the natural key.
23
-
24
+
24
25
  Returns:
25
26
  The Event object corresponding to the given name.
26
27
  """
@@ -38,22 +39,19 @@ class Event(models.Model):
38
39
 
39
40
  name = models.CharField(max_length=100, unique=True)
40
41
  description = models.TextField(blank=True, null=True)
41
- event_classification: models.ForeignKey["EventClassification"] = models.ForeignKey(
42
- "EventClassification",
43
- on_delete=models.CASCADE,
44
- related_name="events",
45
- null=True,
46
- blank=True,
47
- )
42
+
48
43
  objects = EventManager()
49
44
 
50
45
  if TYPE_CHECKING:
51
- patient_events: models.QuerySet["PatientEvent"]
46
+ patient_events: models.ForeignKey["PatientEvent"]
47
+
48
+ @property
49
+ def event_classifications(self) -> models.QuerySet["EventClassification"]: ...
52
50
 
53
51
  def natural_key(self):
54
52
  """
55
53
  Returns a tuple representing the natural key for this instance.
56
-
54
+
57
55
  The natural key consists of the instance's unique name.
58
56
  """
59
57
  return (self.name,)
@@ -61,7 +59,7 @@ class Event(models.Model):
61
59
  def __str__(self):
62
60
  """
63
61
  Return a string representation of the instance's name.
64
-
62
+
65
63
  This method converts the 'name' attribute to a string, providing a human-readable
66
64
  representation of the model instance.
67
65
  """
@@ -85,6 +83,18 @@ class EventClassification(models.Model):
85
83
 
86
84
  objects = EventClassificationManager()
87
85
 
86
+ event = models.ForeignKey(
87
+ Event,
88
+ on_delete=models.CASCADE,
89
+ related_name="event_classifications",
90
+ )
91
+
92
+ if TYPE_CHECKING:
93
+ event: models.ForeignKey["Event"]
94
+
95
+ @property
96
+ def event_classification_choices(self) -> models.QuerySet["EventClassificationChoice"]: ...
97
+
88
98
  def natural_key(self):
89
99
  """Returns the natural key (name) as a tuple."""
90
100
  return (self.name,)
@@ -95,9 +105,7 @@ class EventClassification(models.Model):
95
105
 
96
106
  def get_choices(self) -> List["EventClassificationChoice"]:
97
107
  """Retrieves all choices associated with this classification."""
98
- choices: List[EventClassificationChoice] = [
99
- _ for _ in self.event_classification_choices.all()
100
- ]
108
+ choices: List[EventClassificationChoice] = [_ for _ in self.event_classification_choices.all()]
101
109
  return choices
102
110
 
103
111
 
@@ -128,6 +136,11 @@ class EventClassificationChoice(models.Model):
128
136
 
129
137
  objects = EventClassificationChoiceManager()
130
138
 
139
+ @property
140
+ def event(self):
141
+ """Returns the associated Event instance."""
142
+ return self.event_classification.event
143
+
131
144
  def natural_key(self):
132
145
  """Returns the natural key (name) as a tuple."""
133
146
  return (self.name,)
@@ -1,9 +1,16 @@
1
1
  from .examination import Examination, ExaminationRequirementSet
2
- from .examination_type import ExaminationType
2
+ from .examination_indication import ExaminationIndication, ExaminationIndicationClassification, ExaminationIndicationClassificationChoice
3
3
  from .examination_time import ExaminationTime
4
4
  from .examination_time_type import ExaminationTimeType
5
- from .examination_indication import(
6
- ExaminationIndication,
7
- ExaminationIndicationClassification,
8
- ExaminationIndicationClassificationChoice
9
- )
5
+ from .examination_type import ExaminationType
6
+
7
+ __all__ = [
8
+ "Examination",
9
+ "ExaminationRequirementSet",
10
+ "ExaminationType",
11
+ "ExaminationTime",
12
+ "ExaminationTimeType",
13
+ "ExaminationIndication",
14
+ "ExaminationIndicationClassification",
15
+ "ExaminationIndicationClassificationChoice",
16
+ ]
@@ -1,6 +1,6 @@
1
+ from typing import TYPE_CHECKING, List
2
+
1
3
  from django.db import models
2
- from typing import List
3
- from typing import TYPE_CHECKING
4
4
 
5
5
  if TYPE_CHECKING:
6
6
  from endoreg_db.models import Finding
@@ -33,19 +33,18 @@ class Examination(models.Model):
33
33
 
34
34
  if TYPE_CHECKING:
35
35
  from endoreg_db.models import ExaminationIndication, Finding, FindingClassification
36
- finding_classifications: models.QuerySet['FindingClassification']
37
- indications: models.QuerySet[ExaminationIndication]
38
- findings: models.QuerySet["Finding"]
39
- exam_reqset_links: models.QuerySet["ExaminationRequirementSet"]
40
36
 
41
- # # Requirement sets associated with the examination.
42
- # requirement_sets = models.ManyToManyField(
43
- # "RequirementSet",
44
- # through="ExaminationRequirementSet",
45
- # related_name="examinations",
46
- # blank=True,
47
- # )
37
+ @property
38
+ def finding_classifications(self) -> "models.manager.RelatedManager[FindingClassification]": ...
39
+
40
+ @property
41
+ def indications(self) -> "models.manager.RelatedManager[ExaminationIndication]": ...
42
+
43
+ @property
44
+ def findings(self) -> "models.manager.RelatedManager[Finding]": ...
48
45
 
46
+ @property
47
+ def exam_reqset_links(self) -> "models.manager.RelatedManager[ExaminationRequirementSet]": ...
49
48
 
50
49
  @property
51
50
  def links(self) -> "RequirementLinks":
@@ -61,7 +60,7 @@ class Examination(models.Model):
61
60
  examinations=[self],
62
61
  findings=list(self.findings.all()),
63
62
  finding_classifications=list(self.finding_classifications.all()),
64
- examination_indications=list(self.indications.all())
63
+ examination_indications=list(self.indications.all()),
65
64
  )
66
65
 
67
66
  def __str__(self) -> str:
@@ -108,12 +107,13 @@ class ExaminationRequirementSetManager(models.Manager):
108
107
  def get_by_natural_key(self, name: str) -> "ExaminationRequirementSet":
109
108
  return self.get(name=name)
110
109
 
111
-
110
+
112
111
  class ExaminationRequirementSet(models.Model):
113
112
  """
114
113
  Through table for Examination ↔ RequirementSet link.
115
114
  Lets you store per-link metadata (order, default, etc.)
116
115
  """
116
+
117
117
  examinations = models.ManyToManyField(
118
118
  "Examination",
119
119
  related_name="exam_reqset_links",
@@ -126,7 +126,7 @@ class ExaminationRequirementSet(models.Model):
126
126
  # )
127
127
  # Optional metadata
128
128
  enabled_by_default = models.BooleanField(default=False)
129
-
129
+
130
130
  name = models.CharField(max_length=100, unique=True)
131
131
 
132
132
  objects = ExaminationRequirementSetManager()
@@ -136,7 +136,7 @@ class ExaminationRequirementSet(models.Model):
136
136
 
137
137
  # def __str__(self) -> str:
138
138
  # return f"{self.examination} ↔ {self.requirement_set} (prio={self.priority})"
139
-
139
+
140
140
  def natural_key(self) -> tuple:
141
141
  """
142
142
  Returns the natural key for the ExaminationRequirementSet.
@@ -145,4 +145,3 @@ class ExaminationRequirementSet(models.Model):
145
145
  tuple: The natural key consisting of the name.
146
146
  """
147
147
  return (self.name,)
148
-