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,20 +4,24 @@ including versioning, configuration, and associated weights files.
4
4
  Logic is primarily handled in model_meta_logic.py.
5
5
  """
6
6
 
7
- from typing import Optional, TYPE_CHECKING, Tuple, Dict, Any, Type
8
- # Removed shutil import, now in logic
7
+ from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Type, cast
9
8
 
10
- from django.db import models
11
9
  from django.core.validators import FileExtensionValidator
10
+
11
+ # Removed shutil import, now in logic
12
+ from django.db import models
13
+
12
14
  # Removed torch import, now in logic
13
15
  # from torch import nn
16
+ from ..utils import WEIGHTS_DIR
14
17
 
15
- from ..utils import WEIGHTS_DIR, STORAGE_DIR
16
18
  # Import logic functions
17
19
  from . import model_meta_logic as logic
18
20
 
19
21
  if TYPE_CHECKING:
20
- from endoreg_db.models import LabelSet, AiModel # pylint: disable=import-outside-toplevel
22
+ from django.db.models.fields.files import FieldFile
23
+
24
+ from endoreg_db.models import AiModel, LabelSet # pylint: disable=import-outside-toplevel
21
25
 
22
26
 
23
27
  class ModelMetaManager(models.Manager):
@@ -26,7 +30,24 @@ class ModelMetaManager(models.Manager):
26
30
 
27
31
  Provides methods for retrieving ModelMeta instances using natural keys.
28
32
  """
29
- # ... existing code ...
33
+
34
+ def get_by_natural_key(self, name: str, version: str, model_name: str) -> "ModelMeta":
35
+ """
36
+ Retrieves a ModelMeta instance using its natural key.
37
+
38
+ This method returns the ModelMeta whose name, version, and associated model's name
39
+ match the provided natural key. It is primarily used to support Django's natural key
40
+ serialization during data import/export and deserialization processes.
41
+
42
+ Args:
43
+ name: The name of the ModelMeta.
44
+ version: The version identifier of the ModelMeta.
45
+ model_name: The name of the associated AiModel.
46
+
47
+ Returns:
48
+ The ModelMeta object corresponding to the given natural key.
49
+ """
50
+ return self.get(name=name, version=version, model__name=model_name)
30
51
 
31
52
 
32
53
  class ModelMeta(models.Model):
@@ -50,7 +71,6 @@ class ModelMeta(models.Model):
50
71
  on_delete=models.CASCADE,
51
72
  related_name="metadata_versions",
52
73
  help_text="The base AI model architecture this metadata belongs to.",
53
-
54
74
  )
55
75
 
56
76
  # --- Model Configuration ---
@@ -60,15 +80,13 @@ class ModelMeta(models.Model):
60
80
  related_name="model_metadata",
61
81
  help_text="The set of labels this model version predicts.",
62
82
  )
63
- activation = models.CharField(
64
- max_length=50, default="sigmoid", help_text="Output activation function (e.g., 'sigmoid', 'softmax', 'none')."
65
- )
83
+ activation = models.CharField(max_length=50, default="sigmoid", help_text="Output activation function (e.g., 'sigmoid', 'softmax', 'none').")
66
84
  weights = models.FileField(
67
85
  upload_to=WEIGHTS_DIR.name, # Use .name for relative path
68
- validators=[FileExtensionValidator(allowed_extensions=["ckpt"])],
86
+ validators=[FileExtensionValidator(allowed_extensions=["safetensors", "pth", "pt"])],
69
87
  null=True,
70
88
  blank=True,
71
- help_text="Path to the model weights file (.ckpt), relative to MEDIA_ROOT.",
89
+ help_text="Path to the model weights file (.safetensors), relative to MEDIA_ROOT.",
72
90
  )
73
91
 
74
92
  # --- Normalization and Input Shape ---
@@ -84,9 +102,7 @@ class ModelMeta(models.Model):
84
102
  )
85
103
  size_x = models.IntegerField(default=716, help_text="Expected input image width.")
86
104
  size_y = models.IntegerField(default=716, help_text="Expected input image height.")
87
- axes = models.CharField(
88
- max_length=10, default="2,0,1", help_text="Comma-separated target axis order (e.g., '2,0,1' for CHW)."
89
- )
105
+ axes = models.CharField(max_length=10, default="2,0,1", help_text="Comma-separated target axis order (e.g., '2,0,1' for CHW).")
90
106
 
91
107
  # --- Inference Parameters ---
92
108
  batchsize = models.IntegerField(default=16, help_text="Default batch size for inference.")
@@ -96,17 +112,21 @@ class ModelMeta(models.Model):
96
112
  description = models.TextField(blank=True, null=True, help_text="Optional description.")
97
113
  date_created = models.DateTimeField(auto_now_add=True)
98
114
 
99
-
100
115
  objects = ModelMetaManager()
101
116
 
102
117
  # --- Type Hinting for Related Fields ---
103
118
  if TYPE_CHECKING:
104
- labelset: "LabelSet"
105
- model: "AiModel" # Corrected from ai_model to match field name
119
+ labelset: models.ForeignKey["LabelSet"]
120
+ model: models.ForeignKey["AiModel"] # Corrected from ai_model to match field name
121
+ weights = cast(FieldFile, weights)
106
122
 
107
123
  class Meta:
108
124
  """Metadata options for the ModelMeta model."""
109
- # ... existing code ...
125
+
126
+ unique_together = (("name", "version", "model"),)
127
+ ordering = ["-date_created"]
128
+ verbose_name = "Model Metadata"
129
+ verbose_name_plural = "Model Metadata"
110
130
 
111
131
  @classmethod
112
132
  def create_from_file(
@@ -115,6 +135,7 @@ class ModelMeta(models.Model):
115
135
  model_name: str,
116
136
  labelset_name: str,
117
137
  weights_file: str,
138
+ labelset_version: Optional[int | str] = None,
118
139
  requested_version: Optional[str] = None,
119
140
  bump_if_exists: bool = False,
120
141
  **kwargs: Any,
@@ -124,23 +145,34 @@ class ModelMeta(models.Model):
124
145
  """
125
146
  # Delegate to logic function, passing the class (cls)
126
147
  return logic.create_from_file_logic(
127
- cls, meta_name, model_name, labelset_name, weights_file,
128
- requested_version, bump_if_exists, **kwargs
148
+ cls,
149
+ meta_name,
150
+ model_name,
151
+ labelset_name,
152
+ weights_file,
153
+ labelset_version=labelset_version,
154
+ requested_version=requested_version,
155
+ bump_if_exists=bump_if_exists,
156
+ **kwargs,
129
157
  )
130
-
158
+
131
159
  @classmethod
132
160
  def setup_default_from_huggingface(
133
161
  cls: Type["ModelMeta"],
134
162
  model_id: str = "wg-lux/colo_segmentation_RegNetX800MF_base",
135
163
  labelset_name: Optional[str] = None,
164
+ labelset_version: Optional[int | str] = None,
136
165
  ) -> "ModelMeta":
137
166
  """
138
167
  Downloads a pretrained model from Hugging Face and initializes ModelMeta automatically.
139
168
  """
140
169
  # If labelset_name is not provided, handle default logic here if needed
141
- return logic.setup_default_from_huggingface_logic(cls, model_id, labelset_name)
142
-
143
-
170
+ return logic.setup_default_from_huggingface_logic(
171
+ cls,
172
+ model_id=model_id,
173
+ labelset_name=labelset_name,
174
+ labelset_version=labelset_version,
175
+ )
144
176
 
145
177
  @classmethod
146
178
  def get_latest_version_number(cls: Type["ModelMeta"], meta_name: str, model_name: str) -> int:
@@ -150,9 +182,8 @@ class ModelMeta(models.Model):
150
182
  # Delegate to logic function
151
183
  return logic.get_latest_version_number_logic(cls, meta_name, model_name)
152
184
 
153
-
154
185
  @staticmethod
155
- def get_activation_function(activation_name: str) -> "TorchModule":
186
+ def get_activation_function(activation_name: str):
156
187
  """
157
188
  Retrieves a PyTorch activation function using external logic.
158
189
  """
@@ -166,12 +197,12 @@ class ModelMeta(models.Model):
166
197
  # Delegate to logic function
167
198
  return logic.get_inference_dataset_config_logic(self)
168
199
 
169
- def natural_key(self) -> Tuple[str, str]:
200
+ def natural_key(self) -> Tuple[str, str, str]:
170
201
  """
171
202
  Returns the natural key for serialization.
172
203
  """
173
204
  # Assuming natural key is based on name and version, linked to model name
174
- return (self.name, self.version, self.model.natural_key())
205
+ return (self.name, self.version, self.model.natural_key()[0])
175
206
 
176
207
  def __str__(self) -> str:
177
208
  """String representation of the ModelMeta instance."""
@@ -1,9 +1,8 @@
1
1
  import shutil
2
2
  from logging import getLogger
3
3
  from pathlib import Path
4
- from typing import TYPE_CHECKING, Any, Optional, Type
4
+ from typing import TYPE_CHECKING, Any, Iterable, Optional, Type
5
5
 
6
- from django.core.files import File
7
6
  from django.db import transaction
8
7
  from huggingface_hub import hf_hub_download
9
8
 
@@ -19,6 +18,13 @@ if TYPE_CHECKING:
19
18
  from .model_meta import ModelMeta # Import ModelMeta for type hinting
20
19
 
21
20
 
21
+ def _get_model_meta_class():
22
+ """Lazy import to avoid circular imports"""
23
+ from .model_meta import ModelMeta
24
+
25
+ return ModelMeta
26
+
27
+
22
28
  def get_latest_version_number_logic(cls: Type["ModelMeta"], meta_name: str, model_name: str) -> int:
23
29
  """
24
30
  Finds the highest numerical version for a given meta_name and model_name.
@@ -55,6 +61,7 @@ def create_from_file_logic(
55
61
  model_name: str,
56
62
  labelset_name: str,
57
63
  weights_file: str,
64
+ labelset_version: Optional[int | str] = None,
58
65
  requested_version: Optional[str] = None,
59
66
  bump_if_exists: bool = False,
60
67
  **kwargs: Any,
@@ -70,10 +77,23 @@ def create_from_file_logic(
70
77
  except AiModel.DoesNotExist as exc:
71
78
  raise ValueError(f"AiModel with name '{model_name}' not found.") from exc
72
79
 
80
+ labelset_qs = LabelSet.objects.filter(name=labelset_name)
81
+ if labelset_version not in (None, "", -1):
82
+ try:
83
+ version_value = int(labelset_version)
84
+ except (TypeError, ValueError):
85
+ version_value = labelset_version
86
+ labelset_qs = labelset_qs.filter(version=version_value)
87
+
73
88
  try:
74
- label_set = LabelSet.objects.get(name=labelset_name)
89
+ label_set = labelset_qs.get()
75
90
  except LabelSet.DoesNotExist as exc:
76
- raise ValueError(f"LabelSet with name '{labelset_name}' not found.") from exc
91
+ raise ValueError(f"LabelSet '{labelset_name}' with version '{labelset_version}' not found.") from exc
92
+ except LabelSet.MultipleObjectsReturned:
93
+ # Prefer the highest version when duplicates remain and no explicit version requested
94
+ label_set = labelset_qs.order_by("-version").first()
95
+ if not label_set:
96
+ raise ValueError(f"LabelSet '{labelset_name}' could not be resolved.")
77
97
 
78
98
  # --- Determine Version ---
79
99
  target_version: str
@@ -165,16 +185,92 @@ def get_activation_function_logic(activation_name: str):
165
185
 
166
186
 
167
187
  # Placeholder for get_inference_dataset_config_logic
188
+ def _normalise_scalar_sequence(value: Any) -> str | None:
189
+ """Serialise sequence-like values into the canonical comma-separated form."""
190
+
191
+ if value in (None, ""):
192
+ return None
193
+ if isinstance(value, str):
194
+ return value
195
+ if isinstance(value, (list, tuple)):
196
+ return ",".join(str(item) for item in value)
197
+ return str(value)
198
+
199
+
200
+ def _parse_float_sequence(value: Any, *, fallback: Iterable[float]) -> list[float]:
201
+ """Coerce stored normalisation strings/iterables to float lists."""
202
+
203
+ if value in (None, ""):
204
+ return list(fallback)
205
+
206
+ tokens: Iterable[Any]
207
+ if isinstance(value, str):
208
+ tokens = [tok.strip() for tok in value.split(",") if tok.strip()]
209
+ elif isinstance(value, (list, tuple)):
210
+ tokens = value
211
+ else:
212
+ tokens = [value]
213
+
214
+ parsed: list[float] = []
215
+ for token in tokens:
216
+ try:
217
+ parsed.append(float(token))
218
+ except (TypeError, ValueError):
219
+ logger.warning("Failed to parse normalisation value %r; using fallback", token)
220
+ return list(fallback)
221
+
222
+ return parsed or list(fallback)
223
+
224
+
225
+ def _parse_axes(axes_value: str | Iterable[Any]) -> list[int]:
226
+ """Normalise stored axis notation to integer indices.
227
+
228
+ Historic metadata mixes numeric strings ("2,0,1") with symbolic tokens
229
+ ("CHW", "HWC"). The inference code ultimately expects a list of integers,
230
+ so we coerce common symbolic forms to their numeric equivalents and fall
231
+ back to sequential indices when the format is unknown.
232
+ """
233
+
234
+ if not axes_value:
235
+ return [0, 1, 2]
236
+
237
+ if isinstance(axes_value, str):
238
+ token_source = axes_value.strip()
239
+ if "," in token_source:
240
+ tokens = [token.strip() for token in token_source.split(",") if token.strip()]
241
+ else:
242
+ tokens = [char for char in token_source if char.strip()]
243
+ else:
244
+ tokens = [str(token).strip() for token in axes_value if str(token).strip()]
245
+
246
+ symbol_map = {"C": 0, "H": 1, "W": 2}
247
+ parsed_axes: list[int] = []
248
+
249
+ for idx, token in enumerate(tokens):
250
+ normalised = token.upper()
251
+ if normalised.lstrip("+-").isdigit():
252
+ parsed_axes.append(int(normalised))
253
+ elif normalised in symbol_map:
254
+ parsed_axes.append(symbol_map[normalised])
255
+ else:
256
+ logger.warning("Unknown axes token '%s'; defaulting to index order", token)
257
+ parsed_axes.append(idx)
258
+
259
+ return parsed_axes or [0, 1, 2]
260
+
261
+
168
262
  def get_inference_dataset_config_logic(model_meta: "ModelMeta") -> dict:
169
263
  # This would typically extract relevant fields from model_meta
170
264
  # for configuring a dataset during inference
265
+ DEFAULT_MEAN = (0.45211223, 0.27139644, 0.19264949)
266
+ DEFAULT_STD = (0.31418097, 0.21088019, 0.16059452)
267
+
171
268
  return {
172
- "mean": [float(x) for x in model_meta.mean.split(",")],
173
- "std": [float(x) for x in model_meta.std.split(",")],
174
- "size_y": model_meta.size_y, # Add size_y key
175
- "size_x": model_meta.size_x, # Add size_x key
176
- "axes": [int(x) for x in model_meta.axes.split(",")],
177
- # Add other relevant config like normalization type, etc.
269
+ "mean": _parse_float_sequence(model_meta.mean, fallback=DEFAULT_MEAN),
270
+ "std": _parse_float_sequence(model_meta.std, fallback=DEFAULT_STD),
271
+ "size_y": int(model_meta.size_y) if model_meta.size_y else 716,
272
+ "size_x": int(model_meta.size_x) if model_meta.size_x else 716,
273
+ "axes": _parse_axes(model_meta.axes),
178
274
  }
179
275
 
180
276
 
@@ -294,21 +390,45 @@ def infer_default_model_meta_from_hf(model_id: str) -> dict[str, Any]:
294
390
  }
295
391
 
296
392
 
297
- def setup_default_from_huggingface_logic(cls, model_id: str, labelset_name: str | None = None):
393
+ def setup_default_from_huggingface_logic(
394
+ cls,
395
+ model_id: str,
396
+ labelset_name: str | None = None,
397
+ labelset_version: Optional[int | str] = None,
398
+ ):
298
399
  """
299
400
  Downloads model weights from Hugging Face and auto-fills ModelMeta fields.
300
401
  """
301
402
  meta = infer_default_model_meta_from_hf(model_id)
302
403
 
303
- # Download weights
304
- weights_path = hf_hub_download(
305
- repo_id=model_id,
306
- filename="colo_segmentation_RegNetX800MF_base.ckpt",
307
- local_dir=WEIGHTS_DIR,
308
- )
404
+ # Download safetensor weights; raise a clear error if unavailable
405
+ try:
406
+ weights_path = hf_hub_download(
407
+ repo_id=model_id,
408
+ filename="colo_segmentation_RegNetX800MF_base.safetensors",
409
+ local_dir=WEIGHTS_DIR,
410
+ )
411
+ except Exception as exc: # pragma: no cover - network errors
412
+ raise RuntimeError("Failed to download safetensor weights from Hugging Face; ensure the repository provides a .safetensors artifact.") from exc
309
413
 
310
414
  ai_model, _ = AiModel.objects.get_or_create(name=meta["name"])
311
- labelset = LabelSet.objects.first() if not labelset_name else LabelSet.objects.get(name=labelset_name)
415
+ if not labelset_name:
416
+ labelset = LabelSet.objects.first()
417
+ if not labelset:
418
+ raise ValueError("No labelset found and no labelset_name provided")
419
+ else:
420
+ labelset_qs = LabelSet.objects.filter(name=labelset_name)
421
+ if labelset_version not in (None, "", -1):
422
+ try:
423
+ version_value = int(labelset_version)
424
+ except (TypeError, ValueError):
425
+ version_value = labelset_version
426
+ labelset_qs = labelset_qs.filter(version=version_value)
427
+ labelset = labelset_qs.order_by("-version").first()
428
+ if not labelset:
429
+ raise ValueError(f"LabelSet '{labelset_name}' with version '{labelset_version}' not found.")
430
+
431
+ ModelMeta = _get_model_meta_class()
312
432
  model_meta = ModelMeta.objects.filter(name=meta["name"], model=ai_model).first()
313
433
  if model_meta:
314
434
  logger.info(f"ModelMeta {meta['name']} for model {ai_model.name} already exists. Skipping creation.")
@@ -319,6 +439,7 @@ def setup_default_from_huggingface_logic(cls, model_id: str, labelset_name: str
319
439
  meta_name=meta["name"],
320
440
  model_name=ai_model.name,
321
441
  labelset_name=labelset.name,
442
+ labelset_version=labelset.version,
322
443
  weights_file=weights_path,
323
444
  activation=meta["activation"],
324
445
  mean=meta["mean"],
@@ -1,48 +1,44 @@
1
- from django.db import models
1
+ from typing import TYPE_CHECKING, cast
2
+
2
3
  from django.core.files import File
3
- from typing import TYPE_CHECKING
4
+ from django.db import models
4
5
 
5
6
  if TYPE_CHECKING:
6
7
  from ..media.pdf.report_reader.report_reader_flag import ReportReaderFlag
7
8
 
9
+
8
10
  class PdfType(models.Model):
9
11
  """
10
12
  Defines a specific type or format of PDF report, linking to flags used for parsing.
11
13
 
12
14
  Used to configure how different PDF report layouts are processed.
13
15
  """
16
+
14
17
  name = models.CharField(max_length=255)
15
18
 
16
- patient_info_line = models.ForeignKey(
17
- "ReportReaderFlag",
18
- related_name="pdf_type_patient_info_line",
19
- on_delete=models.CASCADE
20
- )
19
+ patient_info_line = models.ForeignKey("ReportReaderFlag", related_name="pdf_type_patient_info_line", on_delete=models.CASCADE)
21
20
  endoscope_info_line = models.ForeignKey(
22
21
  "ReportReaderFlag",
23
- related_name="pdf_type_endoscopy_info_line",
22
+ related_name="pdf_type_endoscopy_info_line",
24
23
  on_delete=models.CASCADE,
25
24
  )
26
- examiner_info_line = models.ForeignKey(
27
- "ReportReaderFlag",
28
- related_name="pdf_type_examiner_info_line",
29
- on_delete=models.CASCADE
30
- )
25
+ examiner_info_line = models.ForeignKey("ReportReaderFlag", related_name="pdf_type_examiner_info_line", on_delete=models.CASCADE)
31
26
  cut_off_above_lines = models.ManyToManyField(
32
27
  "ReportReaderFlag",
33
- related_name="pdf_type_cut_off_above_lines",
28
+ related_name="pdf_type_cut_off_above_lines",
34
29
  )
35
30
  cut_off_below_lines = models.ManyToManyField(
36
31
  "ReportReaderFlag",
37
- related_name="pdf_type_cut_off_below_lines",
32
+ related_name="pdf_type_cut_off_below_lines",
38
33
  )
39
34
 
40
35
  if TYPE_CHECKING:
41
- patient_info_line: "ReportReaderFlag"
42
- endoscope_info_line: "ReportReaderFlag"
43
- examiner_info_line: "ReportReaderFlag"
44
- cut_off_above_lines: models.QuerySet["ReportReaderFlag"]
45
- cut_off_below_lines: models.QuerySet["ReportReaderFlag"]
36
+ patient_info_line: models.ForeignKey["ReportReaderFlag"]
37
+ endoscope_info_line: models.ForeignKey["ReportReaderFlag"]
38
+ examiner_info_line: models.ForeignKey["ReportReaderFlag"]
39
+
40
+ cut_off_above_lines = cast(models.manager.RelatedManager["ReportReaderFlag"], cut_off_above_lines)
41
+ cut_off_below_lines = cast(models.manager.RelatedManager["ReportReaderFlag"], cut_off_below_lines)
46
42
 
47
43
  def __str__(self):
48
44
  """Returns a string summary of the PDF type and its associated flags."""
@@ -53,7 +49,7 @@ class PdfType(models.Model):
53
49
  summary += f"\nExaminer Info Line: {self.examiner_info_line.value}"
54
50
  summary += f"\nCut Off Above Lines: {[_.value for _ in self.cut_off_above_lines.all()]}"
55
51
  summary += f"\nCut Off Below Lines: {[_.value for _ in self.cut_off_below_lines.all()]}"
56
-
52
+
57
53
  return summary
58
54
 
59
55
  @classmethod
@@ -61,18 +57,17 @@ class PdfType(models.Model):
61
57
  """Returns a default PdfType instance, typically used as a fallback."""
62
58
  return PdfType.objects.get(name="ukw-endoscopy-examination-report-generic")
63
59
 
60
+
64
61
  class PdfMeta(models.Model):
65
62
  """
66
63
  Stores metadata associated with a specific PDF document file.
67
64
  """
65
+
68
66
  pdf_type = models.ForeignKey(PdfType, on_delete=models.CASCADE)
69
67
  date = models.DateField()
70
68
  time = models.TimeField()
71
69
  pdf_hash = models.CharField(max_length=255, unique=True)
72
70
 
73
- if TYPE_CHECKING:
74
- pdf_type: "PdfType"
75
-
76
71
  def __str__(self):
77
72
  """Returns the PDF hash as its string representation."""
78
73
  return str(self.pdf_hash)