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
@@ -5,7 +5,7 @@ Django management command to create ModelMeta from Hugging Face model.
5
5
  from pathlib import Path
6
6
 
7
7
  from django.core.files.base import ContentFile
8
- from django.core.management.base import BaseCommand
8
+ from django.core.management.base import BaseCommand, CommandError
9
9
  from huggingface_hub import hf_hub_download
10
10
 
11
11
  from endoreg_db.models import AiModel, LabelSet, ModelMeta
@@ -39,12 +39,19 @@ class Command(BaseCommand):
39
39
  default="1",
40
40
  help="Version for the model meta",
41
41
  )
42
+ parser.add_argument(
43
+ "--labelset_version",
44
+ type=int,
45
+ default=None,
46
+ help="LabelSet version; if omitted, the latest by name is used",
47
+ )
42
48
 
43
49
  def handle(self, *args, **options):
44
50
  model_id = options["model_id"]
45
51
  model_name = options["model_name"]
46
52
  labelset_name = options["labelset_name"]
47
53
  version = options["meta_version"]
54
+ labelset_version = options.get("labelset_version")
48
55
 
49
56
  self.stdout.write(f"Downloading model {model_id} from Hugging Face...")
50
57
 
@@ -52,7 +59,7 @@ class Command(BaseCommand):
52
59
  # Download the model weights
53
60
  weights_path = hf_hub_download(
54
61
  repo_id=model_id,
55
- filename="colo_segmentation_RegNetX800MF_base.ckpt",
62
+ filename="colo_segmentation_RegNetX800MF_base.safetensors",
56
63
  local_dir="/tmp",
57
64
  )
58
65
  self.stdout.write(f"Downloaded weights to: {weights_path}")
@@ -64,14 +71,17 @@ class Command(BaseCommand):
64
71
  if created:
65
72
  self.stdout.write(f"Created AI model: {ai_model.name}")
66
73
 
67
- # Get labelset
68
- try:
69
- labelset = LabelSet.objects.get(name=labelset_name)
70
- except LabelSet.DoesNotExist:
71
- self.stdout.write(
72
- self.style.ERROR(f"LabelSet '{labelset_name}' not found")
74
+ # Get labelset (optionally by version); fail with non-zero exit
75
+ labelset_qs = LabelSet.objects.filter(name=labelset_name)
76
+ if labelset_version is not None:
77
+ labelset_qs = labelset_qs.filter(version=labelset_version)
78
+ labelset = labelset_qs.order_by("-version").first()
79
+ if labelset is None:
80
+ raise CommandError(
81
+ f"LabelSet '{labelset_name}'"
82
+ + (f" v{labelset_version}" if labelset_version is not None else "")
83
+ + " not found"
73
84
  )
74
- return
75
85
 
76
86
  # Create ModelMeta
77
87
  model_meta, created = ModelMeta.objects.get_or_create(
@@ -95,7 +105,7 @@ class Command(BaseCommand):
95
105
  # Save the weights file to the model
96
106
  with open(weights_path, "rb") as f:
97
107
  model_meta.weights.save(
98
- f"{model_name}_v{version}_pytorch_model.bin", ContentFile(f.read())
108
+ f"{model_name}_v{version}.safetensors", ContentFile(f.read())
99
109
  )
100
110
 
101
111
  # Set as active meta
@@ -113,3 +123,4 @@ class Command(BaseCommand):
113
123
  import traceback
114
124
 
115
125
  traceback.print_exc()
126
+ raise CommandError("ModelMeta creation failed") from e
@@ -1,214 +1,384 @@
1
1
  """
2
- This script creates a new ModelMeta object for a multilabel classification model.
2
+ Management command for creating ModelMeta entries for multilabel classification models.
3
+
4
+ Supports two workflows:
5
+ 1. Registering a local `.safetensors` weights file.
6
+ 2. Generating metadata from a YAML template, downloading weights from Hugging Face.
3
7
  """
4
8
 
9
+ import logging
10
+ import tempfile
5
11
  from pathlib import Path
6
- from django.core.management import BaseCommand
7
- from endoreg_db.models import LabelSet, AiModel, ModelMeta
12
+ from typing import Any, Dict, Iterable, List
13
+
14
+ import yaml
15
+ from django.core.management import BaseCommand, CommandError
16
+ from huggingface_hub import hf_hub_download
8
17
 
18
+ from endoreg_db.data import AI_MODEL_META_DATA_DIR
19
+ from endoreg_db.models import AiModel, LabelSet, ModelMeta
9
20
 
10
- # Example usage:
11
- # python manage.py create_multilabel_model_meta --model_path "./data/models/colo_segmentation_RegNetX800MF_6.ckpt"
12
- # python manage.py create_multilabel_model_meta --model_path "./libs/endoreg-db/tests/assets/colo_segmentation_RegNetX800MF_6.ckpt"
21
+ logger = logging.getLogger(__name__)
13
22
 
14
- FPS = 50
15
- SMOOTH_WINDOW_SIZE_S = 1
16
- MIN_SEQ_LEN_S = 0.5
17
- crop_template = [0, 1080, 550, 1920 - 20] # [top, bottom, left, right]
23
+ DEFAULT_MODEL_PATH = (
24
+ Path(__file__).resolve().parents[3]
25
+ / "tests"
26
+ / "assets"
27
+ / "colo_segmentation_RegNetX800MF_6.safetensors"
28
+ )
18
29
 
19
30
 
20
31
  class Command(BaseCommand):
21
- help = """
22
- Creates a new ModelMeta object for a multilabel classification model.
23
- 1. Validates the existence of the specified LabelSet and AiModel
24
- 2. Checks that the model file exists on disk
25
- 3. Creates or updates a ModelMeta entry with the specified parameters
26
- """
27
-
28
- def add_arguments(self, parser):
29
- """
30
- Adds command-line arguments for configuring model meta creation.
31
-
32
- This method defines all necessary arguments for specifying model details, label set selection, normalization parameters, image dimensions, axes configuration, batch size, worker count, and versioning options for the Django management command.
33
- """
32
+ help = (
33
+ "Create or update ModelMeta entries for multilabel classification models using "
34
+ "either a local safetensor file or a YAML template with Hugging Face download support."
35
+ )
36
+
37
+ def add_arguments(self, parser): # noqa: D401 - inherited docstring is sufficient
34
38
  parser.add_argument(
35
39
  "--model_name",
36
40
  type=str,
37
41
  default="image_multilabel_classification_colonoscopy_default",
38
- help="Name of the model to use for segmentation",
42
+ help="Name of the AiModel to attach metadata to.",
39
43
  )
40
-
41
- # model_path
42
44
  parser.add_argument(
43
45
  "--model_path",
44
46
  type=str,
45
- #default="./data/models/colo_segmentation_RegNetX800MF_6.ckpt", #model not yet here
46
- default="./tests/assets/colo_segmentation_RegNetX800MF_6.ckpt", # model currently still here
47
- help="Path to the model file",
47
+ default=str(DEFAULT_MODEL_PATH),
48
+ help=(
49
+ "Path to a local .safetensors weights file. If provided (or left as default) "
50
+ "the command registers the local weights."
51
+ ),
52
+ )
53
+ parser.add_argument(
54
+ "--template_path",
55
+ type=str,
56
+ default=None,
57
+ help="Absolute or relative path to a model meta YAML template.",
58
+ )
59
+ parser.add_argument(
60
+ "--template_name",
61
+ type=str,
62
+ default=None,
63
+ help=(
64
+ "Name of a built-in template file in endoreg_db/data/ai_model_meta (without extension)."
65
+ ),
66
+ )
67
+ parser.add_argument(
68
+ "--template_entry_name",
69
+ type=str,
70
+ default=None,
71
+ help="Optional entry selector when the template file defines multiple models.",
48
72
  )
49
-
50
- # model_meta_version: int = 1
51
73
  parser.add_argument(
52
74
  "--model_meta_version",
53
- type=int,
54
- default=1,
55
- help="Version of the model meta",
75
+ type=str,
76
+ default=None,
77
+ help=(
78
+ "Version to assign to the metadata. When omitted the command uses the template value "
79
+ "or defaults to '1' for local registrations."
80
+ ),
56
81
  )
57
-
58
82
  parser.add_argument(
59
83
  "--image_classification_labelset_name",
60
84
  type=str,
61
85
  default="multilabel_classification_colonoscopy_default",
62
- help="Name of the LabelSet for image classification",
86
+ help="Name of the LabelSet used by the model.",
63
87
  )
64
-
65
88
  parser.add_argument(
66
89
  "--image_classification_labelset_version",
67
90
  type=int,
68
91
  default=-1,
69
- help="Version of the LabelSet for image classification, default is -1 which invokes the highest version",
92
+ help="Specific LabelSet version. Use -1 to select the latest available version.",
70
93
  )
71
-
72
- # activation: str = "sigmoid"
73
94
  parser.add_argument(
74
95
  "--activation_function_name",
75
96
  type=str,
76
97
  default="sigmoid",
77
- help="Activation function for the model",
98
+ help="Activation function applied to model outputs.",
78
99
  )
79
-
80
- # mean: str = "0.45211223,0.27139644,0.19264949"
81
100
  parser.add_argument(
82
101
  "--mean",
83
102
  type=str,
84
103
  default="0.45211223,0.27139644,0.19264949",
85
- help="Mean for normalization",
104
+ help="Comma-separated mean values for input normalization.",
86
105
  )
87
-
88
106
  parser.add_argument(
89
107
  "--std",
90
108
  type=str,
91
109
  default="0.31418097,0.21088019,0.16059452",
92
- help="Std for normalization",
110
+ help="Comma-separated std values for input normalization.",
93
111
  )
94
-
95
- # size_x: int = 716
96
112
  parser.add_argument(
97
113
  "--size_x",
98
114
  type=int,
99
115
  default=716,
100
- help="Size of the image in x direction",
116
+ help="Input width expected by the model.",
101
117
  )
102
-
103
- # size_y: int = 716
104
118
  parser.add_argument(
105
119
  "--size_y",
106
120
  type=int,
107
121
  default=716,
108
- help="Size of the image in y direction",
122
+ help="Input height expected by the model.",
109
123
  )
110
-
111
124
  parser.add_argument(
112
125
  "--axes",
113
126
  type=str,
114
127
  default="2,0,1",
115
- help="Axes of the image",
128
+ help="Comma-separated axis order expected by the model (e.g. '2,0,1' for CHW).",
116
129
  )
117
-
118
- # batchsize: int = 16
119
130
  parser.add_argument(
120
131
  "--batchsize",
121
132
  type=int,
122
133
  default=16,
123
- help="Batchsize for the model",
134
+ help="Default batch size for inference.",
124
135
  )
125
-
126
- # num_workers: int = 0
127
136
  parser.add_argument(
128
137
  "--num_workers",
129
138
  type=int,
130
139
  default=0,
131
- help="Number of workers for the model",
140
+ help="Default number of data loading workers.",
141
+ )
142
+ parser.add_argument(
143
+ "--description",
144
+ type=str,
145
+ default="",
146
+ help="Optional description to store on the ModelMeta record.",
132
147
  )
133
-
134
- # bump_version: bool = False
135
148
  parser.add_argument(
136
149
  "--bump_version",
137
150
  action="store_true",
138
- help="Bump the version of the model",
151
+ help="If the requested version exists, bump to the next available version instead of failing.",
152
+ )
153
+ parser.add_argument(
154
+ "--huggingface_token",
155
+ type=str,
156
+ default=None,
157
+ help="Optional Hugging Face token for private repositories.",
158
+ )
159
+
160
+ def handle(self, *args, **options): # noqa: D401 - inherited docstring is sufficient
161
+ use_template = options.get("template_path") or options.get("template_name")
162
+
163
+ try:
164
+ if use_template:
165
+ model_meta = self._create_from_template(options)
166
+ else:
167
+ model_meta = self._create_from_local_file(options)
168
+ except CommandError:
169
+ raise
170
+ except Exception as exc: # pragma: no cover - defensive logging
171
+ logger.exception("Failed to create ModelMeta", exc_info=exc)
172
+ raise CommandError(str(exc)) from exc
173
+
174
+ self.stdout.write(
175
+ self.style.SUCCESS(f"ModelMeta ready: {model_meta.name} (v{model_meta.version}) for {model_meta.model.name}")
139
176
  )
140
177
 
141
- def handle(self, *args, **options):
142
- """
143
- Processes command-line arguments to create or update a ModelMeta object for a multilabel classification model.
144
-
145
- Retrieves and validates the specified LabelSet and AiModel, ensures the model file exists, and invokes ModelMeta.create_from_file with the provided configuration. Raises a ValueError if the requested LabelSet does not exist.
146
- """
178
+ def _create_from_local_file(self, options: Dict[str, Any]) -> ModelMeta:
179
+ weights_path = Path(options["model_path"]).expanduser().resolve()
180
+ self._validate_safetensors_path(weights_path)
181
+
147
182
  model_name = options["model_name"]
183
+ self._ensure_ai_model_exists(model_name)
184
+
185
+ labelset = self._resolve_labelset(
186
+ options["image_classification_labelset_name"],
187
+ options.get("image_classification_labelset_version"),
188
+ )
148
189
 
149
- model_path = options["model_path"]
150
- activation_function_name = options["activation_function_name"]
190
+ requested_version = options.get("model_meta_version") or "1"
151
191
 
152
- mean = options["mean"]
153
- std = options["std"]
192
+ model_meta = ModelMeta.create_from_file(
193
+ meta_name=model_name,
194
+ model_name=model_name,
195
+ labelset_name=labelset.name,
196
+ labelset_version=labelset.version,
197
+ weights_file=weights_path.as_posix(),
198
+ requested_version=str(requested_version),
199
+ bump_if_exists=options.get("bump_version", False),
200
+ **self._collect_local_kwargs(options),
201
+ )
154
202
 
155
- size_x = options["size_x"]
156
- size_y = options["size_y"]
203
+ return model_meta
157
204
 
158
- axes = options["axes"]
205
+ def _create_from_template(self, options: Dict[str, Any]) -> ModelMeta:
206
+ template_path = self._resolve_template_path(options)
207
+ entries = self._load_template_entries(template_path)
208
+ entry = self._select_template_entry(entries, options)
159
209
 
160
- image_classification_labelset_name = options[
161
- "image_classification_labelset_name"
162
- ]
210
+ fields = entry.get("fields", {})
211
+ if not fields:
212
+ raise CommandError("Template entry is missing a 'fields' section.")
163
213
 
164
- image_classification_labelset_version = options[
165
- "image_classification_labelset_version"
166
- ]
167
- if image_classification_labelset_version == -1:
168
- # If version is -1, we want the latest version of the labelset
169
- labelset = LabelSet.objects.filter(
170
- name=image_classification_labelset_name
171
- ).order_by("-version").first()
172
- if labelset:
173
- image_classification_labelset_version = labelset.version
174
- else:
175
- raise ValueError(
176
- f"No LabelSet found with name: {image_classification_labelset_name}"
177
- )
178
- else:
179
- # If a specific version is provided, we use that
180
- labelset = LabelSet.objects.filter(
181
- name=image_classification_labelset_name,
182
- version=image_classification_labelset_version
183
- ).first()
184
- if not labelset:
185
- raise ValueError(
186
- f"No LabelSet found with name: {image_classification_labelset_name} and version: {image_classification_labelset_version}"
187
- )
188
-
189
- # assert model exists
190
- db_ai_model = AiModel.objects.filter(name=model_name).first()
191
- assert db_ai_model, f"Model not found: {model_name}"
192
-
193
- model_path = Path(model_path).expanduser().resolve().as_posix()
194
-
195
- assert Path(model_path).exists(), f"Model file not found: {model_path}"
196
-
197
- _model_meta = ModelMeta.create_from_file(
198
- meta_name=model_name, # Name of the new metadata object
199
- model_name=model_name, # name of the ai_model to use; This also defines the models VideoSegmentationLabelSet
200
- labelset_name=image_classification_labelset_name,
201
- weights_file=model_path,
202
- # Use the correct keyword arguments matching the method signature
203
- requested_version=options["model_meta_version"],
204
- bump_if_exists=options["bump_version"],
205
- # Pass other options via **kwargs
206
- activation=activation_function_name,
207
- mean=mean,
208
- std=std,
209
- size_x=size_x,
210
- size_y=size_y,
211
- axes=axes,
212
- batchsize=options["batchsize"],
213
- num_workers=options["num_workers"],
214
+ meta_name = fields.get("name") or options["model_name"]
215
+ model_name = fields.get("model") or options["model_name"]
216
+ labelset_name = fields.get("labelset") or options["image_classification_labelset_name"]
217
+ labelset_version = fields.get("labelset_version", options.get("image_classification_labelset_version"))
218
+
219
+ self._ensure_ai_model_exists(model_name)
220
+ labelset = self._resolve_labelset(labelset_name, labelset_version)
221
+
222
+ requested_version = options.get("model_meta_version") or fields.get("version")
223
+ if not requested_version:
224
+ raise CommandError("Provide --model_meta_version or include a 'version' in the template entry.")
225
+
226
+ hf_config = entry.get("setup_config", {}).get("huggingface_fallback", {})
227
+ repo_id = hf_config.get("repo_id")
228
+ filename = hf_config.get("filename")
229
+
230
+ if not repo_id or not filename:
231
+ raise CommandError(
232
+ "Template entry must define setup_config.huggingface_fallback.repo_id and filename for weight download."
233
+ )
234
+
235
+ if not filename.endswith(".safetensors"):
236
+ raise CommandError("Only .safetensors files are supported when downloading from Hugging Face.")
237
+
238
+ token = options.get("huggingface_token")
239
+
240
+ with tempfile.TemporaryDirectory(prefix="hf-multilabel-") as download_dir:
241
+ download_kwargs = {
242
+ "repo_id": repo_id,
243
+ "filename": filename,
244
+ "local_dir": download_dir,
245
+ "local_dir_use_symlinks": False,
246
+ }
247
+ if token:
248
+ download_kwargs["token"] = token
249
+
250
+ weights_path = Path(hf_hub_download(**download_kwargs)).resolve()
251
+
252
+ self._validate_safetensors_path(weights_path)
253
+
254
+ model_meta = ModelMeta.create_from_file(
255
+ meta_name=meta_name,
256
+ model_name=model_name,
257
+ labelset_name=labelset.name,
258
+ labelset_version=labelset.version,
259
+ weights_file=weights_path.as_posix(),
260
+ requested_version=str(requested_version),
261
+ bump_if_exists=options.get("bump_version", False),
262
+ **self._collect_template_kwargs(fields, options),
263
+ )
264
+
265
+ return model_meta
266
+
267
+ def _resolve_template_path(self, options: Dict[str, Any]) -> Path:
268
+ template_path = options.get("template_path")
269
+ template_name = options.get("template_name")
270
+
271
+ if template_path:
272
+ resolved = Path(template_path).expanduser().resolve()
273
+ elif template_name:
274
+ resolved = (AI_MODEL_META_DATA_DIR / f"{template_name}.yaml").resolve()
275
+ else: # pragma: no cover - guarded by caller
276
+ raise CommandError("Template mode requires --template_path or --template_name.")
277
+
278
+ if not resolved.exists():
279
+ raise CommandError(f"Template file not found: {resolved}")
280
+
281
+ return resolved
282
+
283
+ @staticmethod
284
+ def _load_template_entries(template_path: Path) -> List[Dict[str, Any]]:
285
+ with template_path.open("r", encoding="utf-8") as handle:
286
+ data = yaml.safe_load(handle) or []
287
+
288
+ if isinstance(data, dict):
289
+ return [data]
290
+ if isinstance(data, list):
291
+ return [entry for entry in data if isinstance(entry, dict)]
292
+
293
+ raise CommandError(f"Template {template_path} must define a mapping or list of mappings.")
294
+
295
+ def _select_template_entry(self, entries: Iterable[Dict[str, Any]], options: Dict[str, Any]) -> Dict[str, Any]:
296
+ target = options.get("template_entry_name") or options.get("model_name")
297
+
298
+ for entry in entries:
299
+ fields = entry.get("fields", {})
300
+ if not fields:
301
+ continue
302
+ if target and (fields.get("name") == target or fields.get("model") == target):
303
+ return entry
304
+
305
+ entries = list(entries)
306
+ if len(entries) == 1:
307
+ return entries[0]
308
+
309
+ raise CommandError(
310
+ "Unable to determine which template entry to use. Specify --template_entry_name to disambiguate."
311
+ )
312
+
313
+ def _collect_local_kwargs(self, options: Dict[str, Any]) -> Dict[str, Any]:
314
+ return self._filter_none(
315
+ {
316
+ "activation": options.get("activation_function_name"),
317
+ "mean": options.get("mean"),
318
+ "std": options.get("std"),
319
+ "size_x": options.get("size_x"),
320
+ "size_y": options.get("size_y"),
321
+ "axes": options.get("axes"),
322
+ "batchsize": options.get("batchsize"),
323
+ "num_workers": options.get("num_workers"),
324
+ "description": options.get("description"),
325
+ }
326
+ )
327
+
328
+ def _collect_template_kwargs(self, fields: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
329
+ def numeric(value):
330
+ return int(value) if value is not None else value
331
+
332
+ return self._filter_none(
333
+ {
334
+ "activation": fields.get("activation") or options.get("activation_function_name"),
335
+ "mean": self._normalise_sequence(fields.get("mean")) or options.get("mean"),
336
+ "std": self._normalise_sequence(fields.get("std")) or options.get("std"),
337
+ "size_x": numeric(fields.get("size_x")) if fields.get("size_x") is not None else options.get("size_x"),
338
+ "size_y": numeric(fields.get("size_y")) if fields.get("size_y") is not None else options.get("size_y"),
339
+ "axes": fields.get("axes") or options.get("axes"),
340
+ "batchsize": numeric(fields.get("batchsize")) if fields.get("batchsize") is not None else options.get("batchsize"),
341
+ "num_workers": numeric(fields.get("num_workers")) if fields.get("num_workers") is not None else options.get("num_workers"),
342
+ "description": fields.get("description") or options.get("description"),
343
+ }
214
344
  )
345
+
346
+ @staticmethod
347
+ def _normalise_sequence(value: Any) -> str | None:
348
+ if value is None:
349
+ return None
350
+ if isinstance(value, str):
351
+ return value
352
+ if isinstance(value, (list, tuple)):
353
+ return ",".join(str(item) for item in value)
354
+ return str(value)
355
+
356
+ @staticmethod
357
+ def _filter_none(payload: Dict[str, Any]) -> Dict[str, Any]:
358
+ return {key: value for key, value in payload.items() if value not in (None, "")}
359
+
360
+ @staticmethod
361
+ def _validate_safetensors_path(path: Path) -> None:
362
+ if path.suffix != ".safetensors":
363
+ raise CommandError(f"Expected a .safetensors file, got: {path}")
364
+ if not path.exists():
365
+ raise CommandError(f"Weights file not found: {path}")
366
+
367
+ @staticmethod
368
+ def _ensure_ai_model_exists(model_name: str) -> None:
369
+ if not AiModel.objects.filter(name=model_name).exists():
370
+ raise CommandError(f"AiModel not found: {model_name}. Load ai model data before running this command.")
371
+
372
+ @staticmethod
373
+ def _resolve_labelset(name: str, version: Any) -> LabelSet:
374
+ queryset = LabelSet.objects.filter(name=name)
375
+
376
+ if version in (None, -1):
377
+ labelset = queryset.order_by("-version").first()
378
+ else:
379
+ labelset = queryset.filter(version=version).first()
380
+
381
+ if not labelset:
382
+ raise CommandError(f"LabelSet not found for name='{name}' and version='{version}'.")
383
+
384
+ return labelset
@@ -87,7 +87,7 @@ class Command(BaseCommand):
87
87
  help="Display verbose output",
88
88
  )
89
89
  parser.add_argument(
90
- "--center_name",
90
+ "--",
91
91
  type=str,
92
92
  default="university_hospital_wuerzburg",
93
93
  help="Name of the center to associate with video",
@@ -188,7 +188,6 @@ class Command(BaseCommand):
188
188
  center_name = options["center_name"]
189
189
  video_file = options["video_file"]
190
190
  frame_dir_root = options["frame_dir_root"]
191
- video_dir_root = options["video_dir_root"]
192
191
  delete_source = options["delete_source"]
193
192
  save_video_file = options["save_video_file"]
194
193
  model_name = options["model_name"]
@@ -205,7 +204,7 @@ class Command(BaseCommand):
205
204
  # Assert Center exists -> Does not exist methods are deprecated
206
205
  try:
207
206
  center = Center.objects.get(name=center_name)
208
- self.stdout.write(self.style.SUCCESS(f"Using center: {center.name_en}"))
207
+ self.stdout.write(self.style.SUCCESS(f"Using center: {center.name}"))
209
208
  except Center.DoesNotExist:
210
209
  self.stdout.write(self.style.ERROR(f"Center not found: {center_name}"))
211
210
  return
@@ -295,7 +294,7 @@ class Command(BaseCommand):
295
294
  # Updated to handle new return signature (path, metadata)
296
295
  cleaned_video_path, extracted_metadata = frame_cleaner.clean_video(
297
296
  video_path=Path(video_file_obj.raw_file.path),
298
- endoscope_roi=video_file_obj.processor.get_roi_endoscope_image() if video_file_obj.processor else None,
297
+ endoscope_image_roi=video_file_obj.processor.get_roi_endoscope_image() if video_file_obj.processor else None,
299
298
  endoscope_data_roi_nested=video_file_obj.processor.get_rois() if video_file_obj.processor else None,
300
299
  output_path=video_file_obj.get_processed_file_path(),
301
300
  technique="mask_overlay" # Use mask overlay technique as default, if not set this will be inferred.
@@ -336,11 +335,11 @@ class Command(BaseCommand):
336
335
 
337
336
  # Create default SensitiveMeta with placeholder data
338
337
  default_data = {
339
- "patient_first_name": "Patient",
340
- "patient_last_name": "Unknown",
341
- "patient_dob": date(1990, 1, 1), # Default DOB
342
- "examination_date": date.today(),
343
- "center_name": video_file.center.name if video_file.center else "university_hospital_wuerzburg"
338
+ "patient_first_name": None,
339
+ "patient_last_name": None,
340
+ "patient_dob": None,
341
+ "examination_date": None,
342
+ "center_name": video_file.center.name if video_file.center else "unknown",
344
343
  }
345
344
 
346
345
  try:
@@ -420,4 +419,4 @@ class Command(BaseCommand):
420
419
  self.stderr.write("❌ Please enter a number in the list above.\n")
421
420
  continue
422
421
 
423
- return processors[index] # ← the chosen object
422
+ return processors[index] # ← the chosen object
@@ -99,7 +99,7 @@ class Command(BaseCommand):
99
99
  parser.add_argument(
100
100
  "--model_path",
101
101
  type=str,
102
- default="./data/models/colo_segmentation_RegNetX800MF_6.ckpt",
102
+ default="./data/models/colo_segmentation_RegNetX800MF_6.safetensors",
103
103
  help="Path to the model file",
104
104
  )
105
105