endoreg-db 0.8.6.1__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 (360) 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_center_data.py +12 -12
  117. endoreg_db/management/commands/load_requirement_data.py +60 -31
  118. endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
  119. endoreg_db/management/commands/setup_endoreg_db.py +3 -3
  120. endoreg_db/management/commands/storage_management.py +271 -203
  121. endoreg_db/migrations/0001_initial.py +1799 -1300
  122. endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
  123. endoreg_db/migrations/_old/0001_initial.py +1857 -0
  124. endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
  125. endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
  126. endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
  127. endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
  128. endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
  129. endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
  130. endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
  131. endoreg_db/models/__init__.py +78 -123
  132. endoreg_db/models/administration/__init__.py +21 -42
  133. endoreg_db/models/administration/ai/active_model.py +2 -2
  134. endoreg_db/models/administration/ai/ai_model.py +7 -6
  135. endoreg_db/models/administration/case/__init__.py +1 -15
  136. endoreg_db/models/administration/case/case.py +3 -3
  137. endoreg_db/models/administration/case/case_template/__init__.py +2 -14
  138. endoreg_db/models/administration/case/case_template/case_template.py +2 -124
  139. endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
  140. endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
  141. endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
  142. endoreg_db/models/administration/center/center.py +33 -19
  143. endoreg_db/models/administration/center/center_product.py +12 -9
  144. endoreg_db/models/administration/center/center_resource.py +25 -19
  145. endoreg_db/models/administration/center/center_shift.py +21 -17
  146. endoreg_db/models/administration/center/center_waste.py +16 -8
  147. endoreg_db/models/administration/person/__init__.py +2 -0
  148. endoreg_db/models/administration/person/employee/employee.py +10 -5
  149. endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
  150. endoreg_db/models/administration/person/employee/employee_type.py +12 -6
  151. endoreg_db/models/administration/person/examiner/examiner.py +13 -11
  152. endoreg_db/models/administration/person/patient/__init__.py +2 -0
  153. endoreg_db/models/administration/person/patient/patient.py +103 -100
  154. endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
  155. endoreg_db/models/administration/person/person.py +4 -0
  156. endoreg_db/models/administration/person/profession/__init__.py +8 -4
  157. endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
  158. endoreg_db/models/administration/product/product.py +20 -15
  159. endoreg_db/models/administration/product/product_material.py +17 -18
  160. endoreg_db/models/administration/product/product_weight.py +12 -8
  161. endoreg_db/models/administration/product/reference_product.py +23 -55
  162. endoreg_db/models/administration/qualification/qualification.py +7 -3
  163. endoreg_db/models/administration/qualification/qualification_type.py +7 -3
  164. endoreg_db/models/administration/shift/scheduled_days.py +8 -5
  165. endoreg_db/models/administration/shift/shift.py +16 -12
  166. endoreg_db/models/administration/shift/shift_type.py +23 -31
  167. endoreg_db/models/label/__init__.py +7 -8
  168. endoreg_db/models/label/annotation/image_classification.py +10 -9
  169. endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
  170. endoreg_db/models/label/label.py +15 -15
  171. endoreg_db/models/label/label_set.py +19 -6
  172. endoreg_db/models/label/label_type.py +1 -1
  173. endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
  174. endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
  175. endoreg_db/models/label/video_segmentation_label.py +4 -0
  176. endoreg_db/models/label/video_segmentation_labelset.py +4 -3
  177. endoreg_db/models/media/frame/frame.py +22 -22
  178. endoreg_db/models/media/pdf/raw_pdf.py +110 -182
  179. endoreg_db/models/media/pdf/report_file.py +25 -29
  180. endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
  181. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
  182. endoreg_db/models/media/video/__init__.py +1 -0
  183. endoreg_db/models/media/video/create_from_file.py +48 -56
  184. endoreg_db/models/media/video/pipe_2.py +8 -9
  185. endoreg_db/models/media/video/video_file.py +150 -108
  186. endoreg_db/models/media/video/video_file_ai.py +288 -74
  187. endoreg_db/models/media/video/video_file_anonymize.py +38 -38
  188. endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
  189. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
  190. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
  191. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
  192. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
  193. endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
  194. endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
  195. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
  196. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
  197. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
  198. endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
  199. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
  200. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
  201. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
  202. endoreg_db/models/media/video/video_file_io.py +109 -62
  203. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
  204. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
  205. endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
  206. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
  207. endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
  208. endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
  209. endoreg_db/models/media/video/video_file_segments.py +24 -17
  210. endoreg_db/models/media/video/video_metadata.py +19 -35
  211. endoreg_db/models/media/video/video_processing.py +96 -95
  212. endoreg_db/models/medical/contraindication/__init__.py +13 -3
  213. endoreg_db/models/medical/disease.py +22 -16
  214. endoreg_db/models/medical/event.py +31 -18
  215. endoreg_db/models/medical/examination/__init__.py +13 -6
  216. endoreg_db/models/medical/examination/examination.py +17 -18
  217. endoreg_db/models/medical/examination/examination_indication.py +26 -25
  218. endoreg_db/models/medical/examination/examination_time.py +16 -6
  219. endoreg_db/models/medical/examination/examination_time_type.py +9 -6
  220. endoreg_db/models/medical/examination/examination_type.py +3 -4
  221. endoreg_db/models/medical/finding/finding.py +38 -39
  222. endoreg_db/models/medical/finding/finding_classification.py +37 -48
  223. endoreg_db/models/medical/finding/finding_intervention.py +27 -22
  224. endoreg_db/models/medical/finding/finding_type.py +13 -12
  225. endoreg_db/models/medical/hardware/endoscope.py +20 -26
  226. endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
  227. endoreg_db/models/medical/laboratory/lab_value.py +62 -91
  228. endoreg_db/models/medical/medication/medication.py +22 -10
  229. endoreg_db/models/medical/medication/medication_indication.py +29 -3
  230. endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
  231. endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
  232. endoreg_db/models/medical/medication/medication_schedule.py +27 -16
  233. endoreg_db/models/medical/organ/__init__.py +15 -12
  234. endoreg_db/models/medical/patient/medication_examples.py +1 -5
  235. endoreg_db/models/medical/patient/patient_disease.py +20 -23
  236. endoreg_db/models/medical/patient/patient_event.py +19 -22
  237. endoreg_db/models/medical/patient/patient_examination.py +48 -54
  238. endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
  239. endoreg_db/models/medical/patient/patient_finding.py +122 -139
  240. endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
  241. endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
  242. endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
  243. endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
  244. endoreg_db/models/medical/patient/patient_medication.py +27 -38
  245. endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
  246. endoreg_db/models/medical/risk/risk.py +7 -6
  247. endoreg_db/models/medical/risk/risk_type.py +8 -5
  248. endoreg_db/models/metadata/model_meta.py +60 -29
  249. endoreg_db/models/metadata/model_meta_logic.py +125 -18
  250. endoreg_db/models/metadata/pdf_meta.py +19 -24
  251. endoreg_db/models/metadata/sensitive_meta.py +102 -85
  252. endoreg_db/models/metadata/sensitive_meta_logic.py +192 -173
  253. endoreg_db/models/metadata/video_meta.py +51 -31
  254. endoreg_db/models/metadata/video_prediction_logic.py +16 -23
  255. endoreg_db/models/metadata/video_prediction_meta.py +29 -33
  256. endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
  257. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
  258. endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
  259. endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
  260. endoreg_db/models/other/emission/emission_factor.py +18 -8
  261. endoreg_db/models/other/gender.py +10 -5
  262. endoreg_db/models/other/information_source.py +25 -25
  263. endoreg_db/models/other/material.py +9 -5
  264. endoreg_db/models/other/resource.py +6 -4
  265. endoreg_db/models/other/tag.py +10 -5
  266. endoreg_db/models/other/transport_route.py +13 -8
  267. endoreg_db/models/other/unit.py +10 -6
  268. endoreg_db/models/other/waste.py +6 -5
  269. endoreg_db/models/requirement/requirement.py +580 -272
  270. endoreg_db/models/requirement/requirement_error.py +85 -0
  271. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
  272. endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
  273. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
  274. endoreg_db/models/requirement/requirement_operator.py +36 -33
  275. endoreg_db/models/requirement/requirement_set.py +74 -57
  276. endoreg_db/models/state/__init__.py +4 -4
  277. endoreg_db/models/state/abstract.py +2 -2
  278. endoreg_db/models/state/anonymization.py +12 -0
  279. endoreg_db/models/state/audit_ledger.py +46 -47
  280. endoreg_db/models/state/label_video_segment.py +9 -0
  281. endoreg_db/models/state/raw_pdf.py +40 -46
  282. endoreg_db/models/state/sensitive_meta.py +6 -2
  283. endoreg_db/models/state/video.py +58 -53
  284. endoreg_db/models/upload_job.py +32 -55
  285. endoreg_db/models/utils.py +1 -2
  286. endoreg_db/root_urls.py +21 -2
  287. endoreg_db/serializers/__init__.py +0 -2
  288. endoreg_db/serializers/anonymization.py +18 -10
  289. endoreg_db/serializers/meta/report_meta.py +1 -1
  290. endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
  291. endoreg_db/serializers/misc/file_overview.py +11 -99
  292. endoreg_db/serializers/requirements/requirement_sets.py +92 -22
  293. endoreg_db/serializers/video/segmentation.py +2 -1
  294. endoreg_db/serializers/video/video_processing_history.py +20 -5
  295. endoreg_db/services/anonymization.py +75 -73
  296. endoreg_db/services/lookup_service.py +37 -24
  297. endoreg_db/services/pdf_import.py +166 -68
  298. endoreg_db/services/storage_aware_video_processor.py +140 -114
  299. endoreg_db/services/video_import.py +193 -283
  300. endoreg_db/urls/__init__.py +7 -20
  301. endoreg_db/urls/media.py +108 -67
  302. endoreg_db/urls/root_urls.py +29 -0
  303. endoreg_db/utils/__init__.py +15 -5
  304. endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
  305. endoreg_db/utils/case_generator/__init__.py +3 -0
  306. endoreg_db/utils/dataloader.py +88 -16
  307. endoreg_db/utils/defaults/set_default_center.py +32 -0
  308. endoreg_db/utils/names.py +22 -16
  309. endoreg_db/utils/permissions.py +2 -1
  310. endoreg_db/utils/pipelines/process_video_dir.py +1 -1
  311. endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
  312. endoreg_db/utils/setup_config.py +8 -5
  313. endoreg_db/utils/storage.py +115 -0
  314. endoreg_db/utils/validate_endo_roi.py +8 -2
  315. endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
  316. endoreg_db/views/__init__.py +0 -10
  317. endoreg_db/views/anonymization/media_management.py +198 -163
  318. endoreg_db/views/anonymization/overview.py +4 -1
  319. endoreg_db/views/anonymization/validate.py +174 -40
  320. endoreg_db/views/media/__init__.py +2 -0
  321. endoreg_db/views/media/pdf_media.py +131 -152
  322. endoreg_db/views/media/sensitive_metadata.py +46 -6
  323. endoreg_db/views/media/video_media.py +89 -82
  324. endoreg_db/views/media/video_segments.py +2 -3
  325. endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
  326. endoreg_db/views/patient/patient.py +5 -4
  327. endoreg_db/views/pdf/pdf_stream.py +20 -21
  328. endoreg_db/views/pdf/reimport.py +11 -32
  329. endoreg_db/views/requirement/evaluate.py +188 -187
  330. endoreg_db/views/requirement/lookup.py +17 -3
  331. endoreg_db/views/requirement/requirement_utils.py +89 -0
  332. endoreg_db/views/video/__init__.py +0 -2
  333. endoreg_db/views/video/correction.py +2 -2
  334. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
  335. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +341 -245
  336. endoreg_db/models/administration/permissions/__init__.py +0 -44
  337. endoreg_db/models/media/video/video_file_frames.py +0 -0
  338. endoreg_db/models/metadata/frame_ocr_result.py +0 -0
  339. endoreg_db/models/rule/__init__.py +0 -13
  340. endoreg_db/models/rule/rule.py +0 -27
  341. endoreg_db/models/rule/rule_applicator.py +0 -224
  342. endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
  343. endoreg_db/models/rule/rule_type.py +0 -20
  344. endoreg_db/models/rule/ruleset.py +0 -17
  345. endoreg_db/serializers/video/video_metadata.py +0 -105
  346. endoreg_db/urls/report.py +0 -48
  347. endoreg_db/urls/video.py +0 -61
  348. endoreg_db/utils/case_generator/case_generator.py +0 -159
  349. endoreg_db/utils/case_generator/utils.py +0 -30
  350. endoreg_db/views/report/__init__.py +0 -9
  351. endoreg_db/views/report/report_list.py +0 -112
  352. endoreg_db/views/report/report_with_secure_url.py +0 -28
  353. endoreg_db/views/report/start_examination.py +0 -7
  354. endoreg_db/views.py +0 -0
  355. /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
  356. /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
  357. /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
  358. /endoreg_db/{models/media/video/refactor_plan.md → views/pdf/pdf_stream_views.py} +0 -0
  359. {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
  360. {endoreg_db-0.8.6.1.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
 
@@ -62,6 +61,7 @@ def create_from_file_logic(
62
61
  model_name: str,
63
62
  labelset_name: str,
64
63
  weights_file: str,
64
+ labelset_version: Optional[int | str] = None,
65
65
  requested_version: Optional[str] = None,
66
66
  bump_if_exists: bool = False,
67
67
  **kwargs: Any,
@@ -77,10 +77,23 @@ def create_from_file_logic(
77
77
  except AiModel.DoesNotExist as exc:
78
78
  raise ValueError(f"AiModel with name '{model_name}' not found.") from exc
79
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
+
80
88
  try:
81
- label_set = LabelSet.objects.get(name=labelset_name)
89
+ label_set = labelset_qs.get()
82
90
  except LabelSet.DoesNotExist as exc:
83
- 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.")
84
97
 
85
98
  # --- Determine Version ---
86
99
  target_version: str
@@ -172,16 +185,92 @@ def get_activation_function_logic(activation_name: str):
172
185
 
173
186
 
174
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
+
175
262
  def get_inference_dataset_config_logic(model_meta: "ModelMeta") -> dict:
176
263
  # This would typically extract relevant fields from model_meta
177
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
+
178
268
  return {
179
- "mean": [float(x) for x in model_meta.mean.split(",")],
180
- "std": [float(x) for x in model_meta.std.split(",")],
181
- "size_y": model_meta.size_y, # Add size_y key
182
- "size_x": model_meta.size_x, # Add size_x key
183
- "axes": [int(x) for x in model_meta.axes.split(",")],
184
- # 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),
185
274
  }
186
275
 
187
276
 
@@ -301,18 +390,26 @@ def infer_default_model_meta_from_hf(model_id: str) -> dict[str, Any]:
301
390
  }
302
391
 
303
392
 
304
- 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
+ ):
305
399
  """
306
400
  Downloads model weights from Hugging Face and auto-fills ModelMeta fields.
307
401
  """
308
402
  meta = infer_default_model_meta_from_hf(model_id)
309
403
 
310
- # Download weights
311
- weights_path = hf_hub_download(
312
- repo_id=model_id,
313
- filename="colo_segmentation_RegNetX800MF_base.ckpt",
314
- local_dir=WEIGHTS_DIR,
315
- )
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
316
413
 
317
414
  ai_model, _ = AiModel.objects.get_or_create(name=meta["name"])
318
415
  if not labelset_name:
@@ -320,7 +417,16 @@ def setup_default_from_huggingface_logic(cls, model_id: str, labelset_name: str
320
417
  if not labelset:
321
418
  raise ValueError("No labelset found and no labelset_name provided")
322
419
  else:
323
- labelset = LabelSet.objects.get(name=labelset_name)
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.")
324
430
 
325
431
  ModelMeta = _get_model_meta_class()
326
432
  model_meta = ModelMeta.objects.filter(name=meta["name"], model=ai_model).first()
@@ -333,6 +439,7 @@ def setup_default_from_huggingface_logic(cls, model_id: str, labelset_name: str
333
439
  meta_name=meta["name"],
334
440
  model_name=ai_model.name,
335
441
  labelset_name=labelset.name,
442
+ labelset_version=labelset.version,
336
443
  weights_file=weights_path,
337
444
  activation=meta["activation"],
338
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)