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
@@ -3,14 +3,14 @@ from django.conf.urls.static import static
3
3
  from django.urls import include, path
4
4
  from rest_framework.routers import DefaultRouter
5
5
 
6
+ from endoreg_db.authz.views_auth import auth_bootstrap
7
+
6
8
  from endoreg_db.views import (
7
9
  ExaminationViewSet,
8
10
  FindingClassificationViewSet,
9
11
  FindingViewSet,
10
12
  PatientExaminationViewSet,
11
13
  PatientFindingViewSet,
12
- VideoExaminationViewSet,
13
- VideoViewSet,
14
14
  )
15
15
 
16
16
  from .anonymization import url_patterns as anonymization_url_patterns
@@ -29,11 +29,9 @@ from .patient import urlpatterns as patient_url_patterns
29
29
 
30
30
  # TODO Phase 1.2: Implement VideoMediaView and PDFMediaView before enabling
31
31
  # from .media import urlpatterns as media_url_patterns
32
- from .report import url_patterns as report_url_patterns
33
32
  from .requirements import urlpatterns as requirements_url_patterns
34
33
  from .stats import url_patterns as stats_url_patterns
35
34
  from .upload import urlpatterns as upload_url_patterns
36
- from .video import url_patterns as video_url_patterns
37
35
 
38
36
  api_urls = []
39
37
  api_urls += classification_url_patterns
@@ -42,22 +40,16 @@ api_urls += auth_url_patterns
42
40
  api_urls += examination_url_patterns
43
41
  api_urls += files_url_patterns
44
42
  api_urls += label_video_segments_url_patterns
45
- api_urls += label_video_segment_validate_url_patterns # Neue Validierungs-Endpunkte
46
- # Phase 1.2: Enable media_url_patterns ✅ IMPLEMENTED
43
+ api_urls += label_video_segment_validate_url_patterns
47
44
  api_urls += media_url_patterns
48
- api_urls += report_url_patterns
49
45
  api_urls += upload_url_patterns
50
- api_urls += video_url_patterns
51
46
  api_urls += requirements_url_patterns
52
47
  api_urls += patient_url_patterns
53
48
  api_urls += stats_url_patterns
54
49
 
55
50
  router = DefaultRouter()
56
- router.register(r"videos", VideoViewSet, basename="videos")
57
51
  router.register(r"examinations", ExaminationViewSet)
58
- router.register(
59
- r"video-examinations", VideoExaminationViewSet, basename="video-examinations"
60
- )
52
+
61
53
  router.register(r"findings", FindingViewSet)
62
54
  router.register(r"classifications", FindingClassificationViewSet)
63
55
  router.register(r"patient-findings", PatientFindingViewSet)
@@ -65,17 +57,12 @@ router.register(r"patient-examinations", PatientExaminationViewSet)
65
57
 
66
58
  # Additional custom video examination routes
67
59
  # Frontend expects: GET /api/video/{id}/examinations/
68
- video_examinations_list = VideoExaminationViewSet.as_view({"get": "by_video"})
69
60
 
70
61
  # Export raw API urlpatterns (no prefix). The project-level endoreg_db/urls.py mounts these under /api/.
71
62
  urlpatterns = [
72
- path(
73
- "video/<int:video_id>/examinations/",
74
- video_examinations_list,
75
- name="video-examinations-by-video",
76
- ),
77
- path("", include(api_urls)), # Specific routes first
78
- path("", include(router.urls)), # Generic router routes second
63
+ path("auth/bootstrap", auth_bootstrap, name="auth-bootstrap"),
64
+ path('', include(router.urls)),
65
+ path('', include(api_urls)),
79
66
  ]
80
67
 
81
68
  if settings.DEBUG:
endoreg_db/urls/media.py CHANGED
@@ -1,37 +1,36 @@
1
- from PIL.PdfParser import PdfStream
2
1
  from django.urls import path
3
2
 
3
+ from endoreg_db.views import VideoStreamView
4
4
  from endoreg_db.views.media import (
5
- VideoMediaView,
6
5
  PdfMediaView, # Alias to avoid conflict with legacy pdf.PDFMediaView
6
+ VideoMediaView,
7
+ pdf_sensitive_metadata,
8
+ pdf_sensitive_metadata_list,
9
+ pdf_sensitive_metadata_verify,
10
+ sensitive_metadata_list,
11
+ video_segment_detail,
12
+ video_segment_validate,
7
13
  video_segments_by_pk,
8
- video_segments_collection,
9
14
  video_segments_by_video,
10
- video_segment_detail,
15
+ video_segments_collection,
11
16
  video_segments_stats,
12
- video_segment_validate,
13
17
  video_segments_validate_bulk,
14
18
  video_segments_validation_status,
15
19
  video_sensitive_metadata,
16
20
  video_sensitive_metadata_verify,
17
- pdf_sensitive_metadata,
18
- pdf_sensitive_metadata_verify,
19
- sensitive_metadata_list,
20
- pdf_sensitive_metadata_list,
21
- )
22
- from endoreg_db.views import (
23
- VideoStreamView,
21
+ get_sensitive_metadata_pk
24
22
  )
25
- from endoreg_db.views.pdf.reimport import PdfReimportView
26
23
  from endoreg_db.views.pdf.pdf_stream import PdfStreamView
27
- from endoreg_db.views.video.reimport import VideoReimportView
24
+ from endoreg_db.views.pdf.reimport import PdfReimportView
28
25
  from endoreg_db.views.video.correction import (
26
+ VideoApplyMaskView,
27
+ VideoCorrectionView,
29
28
  VideoMetadataView,
30
29
  VideoProcessingHistoryView,
31
- VideoApplyMaskView,
32
30
  VideoRemoveFramesView,
33
- VideoCorrectionView,
34
31
  )
32
+ from endoreg_db.views.video.reimport import VideoReimportView
33
+
35
34
  # ---------------------------------------------------------------------------------------
36
35
  # ANNOTATION API ENDPOINTS
37
36
  #
@@ -41,19 +40,32 @@ from endoreg_db.views.video.correction import (
41
40
  # ---------------------------------------------------------------------------------------
42
41
 
43
42
  # Simplified Meta and Validation Endpoints
44
-
43
+
45
44
  urlpatterns = [
45
+ path(
46
+ "media/sensitive-media-id/<int:pk>/<str:mediaType>/",
47
+ get_sensitive_metadata_pk,
48
+ name="sm-pk"
49
+ ),
46
50
  # Video media endpoints
47
51
  path("media/videos/", VideoMediaView.as_view(), name="video-list"),
48
- path("media/videos/<int:pk>/", VideoStreamView.as_view(), name="video-detail-stream"), # Support ?type= params
49
- path("media/videos/<int:pk>/details/", VideoMediaView.as_view(), name="video-detail"), # JSON metadata
50
- path("media/videos/<int:pk>/stream/", VideoStreamView.as_view(), name="video-stream"), # Legacy support
51
-
52
+ path(
53
+ "media/videos/<int:pk>/", VideoStreamView.as_view(), name="video-detail-stream"
54
+ ), # Support ?type= params
55
+ path(
56
+ "media/videos/<int:pk>/details/", VideoMediaView.as_view(), name="video-detail"
57
+ ), # JSON metadata
58
+ path(
59
+ "media/videos/<int:pk>/stream/", VideoStreamView.as_view(), name="video-stream"
60
+ ), # Legacy support
52
61
  # Video Re-import API endpoint (modern media framework)
53
62
  # POST /api/media/videos/<int:pk>/reimport/
54
63
  # Re-imports a video file to regenerate metadata when OCR failed or data is incomplete
55
- path("media/videos/<int:pk>/reimport/", VideoReimportView.as_view(), name="video-reimport"),
56
-
64
+ path(
65
+ "media/videos/<int:pk>/reimport/",
66
+ VideoReimportView.as_view(),
67
+ name="video-reimport",
68
+ ),
57
69
  # ---------------------------------------------------------------------------------------
58
70
  # VIDEO CORRECTION API ENDPOINTS (Modern Media Framework - October 14, 2025)
59
71
  #
@@ -66,38 +78,51 @@ urlpatterns = [
66
78
  # - Metadata: View analysis results
67
79
  # - History: Track all correction operations
68
80
  # ---------------------------------------------------------------------------------------
69
-
70
81
  # Video Correction API
71
82
  # GET /api/media/videos/video-correction/{id}/ - Get video details for correction
72
- path("media/videos/video-correction/<int:pk>", VideoCorrectionView.as_view(), name="video-correction"),
73
-
83
+ path(
84
+ "media/videos/video-correction/<int:pk>",
85
+ VideoCorrectionView.as_view(),
86
+ name="video-correction",
87
+ ),
74
88
  # Video Metadata API
75
89
  # GET /api/media/videos/<int:pk>/metadata/
76
90
  # Returns analysis results (sensitive frame count, ratio, frame IDs)
77
- path("media/videos/<int:pk>/metadata/", VideoMetadataView.as_view(), name="video-metadata"),
78
-
91
+ path(
92
+ "media/videos/<int:pk>/metadata/",
93
+ VideoMetadataView.as_view(),
94
+ name="video-metadata",
95
+ ),
79
96
  # Video Processing History API
80
97
  # GET /api/media/videos/<int:pk>/processing-history/
81
98
  # Returns history of all processing operations (masking, frame removal, analysis)
82
- path("media/videos/<int:pk>/processing-history/", VideoProcessingHistoryView.as_view(), name="video-processing-history"),
83
-
99
+ path(
100
+ "media/videos/<int:pk>/processing-history/",
101
+ VideoProcessingHistoryView.as_view(),
102
+ name="video-processing-history",
103
+ ),
84
104
  # Video Analysis API
85
105
  # POST /api/media/videos/<int:pk>/analyze/
86
106
  # Analyzes video for sensitive frames using MiniCPM-o 2.6 or OCR+LLM
87
107
  # Body: { detection_method: 'minicpm'|'ocr_llm'|'hybrid', sample_interval: 30 }
88
-
89
108
  # Video Masking API
90
109
  # POST /api/media/videos/<int:pk>/apply-mask/
91
110
  # Applies device mask or custom ROI mask to video
92
111
  # Body: { mask_type: 'device'|'custom', device_name: 'olympus', roi: {...} }
93
- path("media/videos/<int:pk>/apply-mask/", VideoApplyMaskView.as_view(), name="video-apply-mask"),
94
-
112
+ path(
113
+ "media/videos/<int:pk>/apply-mask/",
114
+ VideoApplyMaskView.as_view(),
115
+ name="video-apply-mask",
116
+ ),
95
117
  # Video Frame Removal API
96
118
  # POST /api/media/videos/<int:pk>/remove-frames/
97
119
  # Removes specified frames from video
98
120
  # Body: { frame_list: [10,20,30] OR frame_ranges: '10-20,30' OR detection_method: 'automatic' }
99
- path("media/videos/<int:pk>/remove-frames/", VideoRemoveFramesView.as_view(), name="video-remove-frames"),
100
-
121
+ path(
122
+ "media/videos/<int:pk>/remove-frames/",
123
+ VideoRemoveFramesView.as_view(),
124
+ name="video-remove-frames",
125
+ ),
101
126
  # ---------------------------------------------------------------------------------------
102
127
  # VIDEO SEGMENT API ENDPOINTS (Modern Media Framework - October 14, 2025)
103
128
  #
@@ -106,29 +131,40 @@ urlpatterns = [
106
131
  # Video-scoped: GET/POST segments for specific video
107
132
  # Detail: GET/PATCH/DELETE individual segment
108
133
  # ---------------------------------------------------------------------------------------
109
-
110
134
  # Video Segments Collection API
111
135
  # GET/POST /api/media/videos/segments/
112
136
  # List all video segments across videos or create new segment
113
- path("media/videos/segments/", video_segments_collection, name="video-segments-collection"),
114
-
137
+ path(
138
+ "media/videos/segments/",
139
+ video_segments_collection,
140
+ name="video-segments-collection",
141
+ ),
115
142
  # Video Segments Stats API
116
143
  # GET /api/media/videos/segments/stats/
117
144
  # Get statistics about video segments
118
- path("media/videos/segments/stats/", video_segments_stats, name="video-segments-stats"),
119
-
145
+ path(
146
+ "media/videos/segments/stats/",
147
+ video_segments_stats,
148
+ name="video-segments-stats",
149
+ ),
120
150
  # Video-Specific Segments API
121
151
  # GET/POST /api/media/videos/<int:pk>/segments/
122
152
  # List segments for specific video or create segment for video
123
- path("media/videos/<int:pk>/segments/", video_segments_by_video, name="video-segments-by-video"),
124
-
153
+ path(
154
+ "media/videos/<int:pk>/segments/",
155
+ video_segments_by_video,
156
+ name="video-segments-by-video",
157
+ ),
125
158
  # Segment Detail API
126
159
  # GET /api/media/videos/<int:pk>/segments/<int:segment_id>/
127
160
  # PATCH /api/media/videos/<int:pk>/segments/<int:segment_id>/
128
161
  # DELETE /api/media/videos/<int:pk>/segments/<int:segment_id>/
129
162
  # Manages individual segment operations
130
- path("media/videos/<int:pk>/segments/<int:segment_id>/", video_segment_detail, name="video-segment-detail"),
131
-
163
+ path(
164
+ "media/videos/<int:pk>/segments/<int:segment_id>/",
165
+ video_segment_detail,
166
+ name="video-segment-detail",
167
+ ),
132
168
  # ---------------------------------------------------------------------------------------
133
169
  # VIDEO SEGMENT VALIDATION API ENDPOINTS (Modern Media Framework - October 14, 2025)
134
170
  #
@@ -137,65 +173,68 @@ urlpatterns = [
137
173
  # Bulk: POST validate multiple segments
138
174
  # Status: GET/POST validation status for all segments
139
175
  # ---------------------------------------------------------------------------------------
140
-
141
176
  # Single Segment Validation API
142
177
  # POST /api/media/videos/<int:pk>/segments/<int:segment_id>/validate/
143
178
  # Validates a single video segment
144
179
  # Body: { "is_validated": true, "notes": "..." }
145
- path("media/videos/<int:pk>/segments/<int:segment_id>/validate/", video_segment_validate, name="video-segment-validate"),
146
-
180
+ path(
181
+ "media/videos/<int:pk>/segments/<int:segment_id>/validate/",
182
+ video_segment_validate,
183
+ name="video-segment-validate",
184
+ ),
147
185
  # Bulk Segment Validation API
148
186
  # POST /api/media/videos/<int:pk>/segments/validate-bulk/
149
187
  # Validates multiple segments at once
150
188
  # Body: { "segment_ids": [1,2,3], "is_validated": true, "notes": "..." }
151
- path("media/videos/<int:pk>/segments/validate-bulk/", video_segments_validate_bulk, name="video-segments-validate-bulk"),
152
-
189
+ path(
190
+ "media/videos/<int:pk>/segments/validate-bulk/",
191
+ video_segments_validate_bulk,
192
+ name="video-segments-validate-bulk",
193
+ ),
153
194
  # Segment Validation Status API
154
195
  # GET /api/media/videos/<int:pk>/segments/validation-status/
155
196
  # Returns validation statistics for all segments
156
197
  # POST /api/media/videos/<int:pk>/segments/validation-status/
157
198
  # Marks all segments (or filtered by label) as validated
158
199
  # Body: { "label_name": "polyp", "notes": "..." }
159
- path("media/videos/<int:pk>/segments/validation-status/", video_segments_validation_status, name="video-segments-validation-status"),
160
-
200
+ path(
201
+ "media/videos/<int:pk>/segments/validation-status/",
202
+ video_segments_validation_status,
203
+ name="video-segments-validation-status",
204
+ ),
161
205
  # ---------------------------------------------------------------------------------------
162
206
  # SENSITIVE METADATA ENDPOINTS (Modern Media Framework)
163
207
  # ---------------------------------------------------------------------------------------
164
-
165
208
  # Video Sensitive Metadata (Resource-Scoped)
166
209
  # GET/PATCH /api/media/videos/<pk>/sensitive-metadata/
167
210
  # Get or update sensitive patient data for a video
168
211
  path(
169
212
  "media/videos/<int:pk>/sensitive-metadata/",
170
213
  video_sensitive_metadata,
171
- name="video-sensitive-metadata"
214
+ name="video-sensitive-metadata",
172
215
  ),
173
-
174
216
  # POST /api/media/videos/<pk>/sensitive-metadata/verify/
175
217
  # Update verification state (dob_verified, names_verified)
176
218
  path(
177
219
  "media/videos/<int:pk>/sensitive-metadata/verify/",
178
220
  video_sensitive_metadata_verify,
179
- name="video-sensitive-metadata-verify"
221
+ name="video-sensitive-metadata-verify",
180
222
  ),
181
-
182
223
  # PDF Sensitive Metadata (Resource-Scoped)
183
224
  # GET/PATCH /api/media/pdfs/<pk>/sensitive-metadata/
184
225
  # Get or update sensitive patient data for a PDF
185
226
  path(
186
227
  "media/pdfs/<int:pk>/sensitive-metadata/",
187
228
  pdf_sensitive_metadata,
188
- name="pdf-sensitive-metadata"
229
+ name="pdf-sensitive-metadata",
189
230
  ),
190
-
191
231
  # POST /api/media/pdfs/<pk>/sensitive-metadata/verify/
192
232
  # Update verification state (dob_verified, names_verified)
193
233
  path(
194
234
  "media/pdfs/<int:pk>/sensitive-metadata/verify/",
195
235
  pdf_sensitive_metadata_verify,
196
- name="pdf-sensitive-metadata-verify"
236
+ name="pdf-sensitive-metadata-verify",
197
237
  ),
198
-
199
238
  # List Endpoints (Collection-Level)
200
239
  # GET /api/media/sensitive-metadata/
201
240
  # List all sensitive metadata (combined PDFs and Videos)
@@ -203,25 +242,27 @@ urlpatterns = [
203
242
  path(
204
243
  "media/sensitive-metadata/",
205
244
  sensitive_metadata_list,
206
- name="sensitive-metadata-list"
245
+ name="sensitive-metadata-list",
207
246
  ),
208
-
209
247
  # GET /api/media/pdfs/sensitive-metadata/
210
248
  # List sensitive metadata for PDFs only
211
249
  # Replaces legacy /api/pdf/sensitivemeta/list/
212
250
  path(
213
251
  "media/pdfs/sensitive-metadata/",
214
252
  pdf_sensitive_metadata_list,
215
- name="pdf-sensitive-metadata-list"
253
+ name="pdf-sensitive-metadata-list",
216
254
  ),
217
-
218
255
  # PDF media endpoints
219
256
  path("media/pdfs/", PdfMediaView.as_view(), name="pdf-list"),
220
257
  path("media/pdfs/<int:pk>/", PdfMediaView.as_view(), name="pdf-detail"),
221
- path("media/pdfs/<int:pk>/stream/", PdfStreamView.as_view(), name="pdf-stream"), # Support ?type=raw|anonymized params
258
+ path(
259
+ "media/pdfs/<int:pk>/stream/", PdfStreamView.as_view(), name="pdf-stream"
260
+ ), # Support ?type=raw|anonymized params
222
261
  # PDF Re-import API endpoint (modern media framework)
223
262
  # POST /api/media/pdfs/<int:pk>/reimport/
224
263
  # Re-imports a PDF file to regenerate metadata when OCR failed or data is incomplete
225
- path("media/pdfs/<int:pk>/reimport/", PdfReimportView.as_view(), name="pdf-reimport"),
264
+ path(
265
+ "media/pdfs/<int:pk>/reimport/", PdfReimportView.as_view(), name="pdf-reimport"
266
+ ),
226
267
  ]
227
- # ---------------------------------------------------------------------------------------
268
+ # ---------------------------------------------------------------------------------------
@@ -0,0 +1,29 @@
1
+ # endoreg_db/urls/root_urls.py
2
+ from django.contrib import admin
3
+ from django.urls import path, include
4
+ from django.http import HttpResponse
5
+ from django.conf import settings
6
+ from django.conf.urls.static import static
7
+
8
+ def public_home(_request):
9
+ return HttpResponse("Public home – no login required.")
10
+
11
+ urlpatterns = [
12
+ # Public landing page
13
+ path("", public_home, name="public_home"),
14
+
15
+ # Django admin (optional)
16
+ path("admin/", admin.site.urls),
17
+
18
+ # Mount ALL API routes under /api/
19
+ # This pulls the urlpatterns exported by endoreg_db/urls/__init__.py
20
+ path("api/", include("endoreg_db.urls")),
21
+
22
+ # Keycloak OIDC (mozilla-django-oidc provides /oidc/authenticate/ and /oidc/callback/)
23
+ path("oidc/", include("mozilla_django_oidc.urls")),
24
+ ]
25
+
26
+ # Serve static/media in DEBUG at the root (NOT under /api/)
27
+ if settings.DEBUG:
28
+ urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
29
+ urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
@@ -4,6 +4,7 @@
4
4
 
5
5
  # dataloader
6
6
  from endoreg_db.utils.video.ffmpeg_wrapper import assemble_video_from_frames, get_stream_info, transcode_video, transcode_videofile_if_required
7
+
7
8
  from .dataloader import load_model_data_from_yaml
8
9
 
9
10
  # dates
@@ -40,18 +41,23 @@ from .paths import data_paths
40
41
 
41
42
  # pydantic_models
42
43
  from .pydantic_models import DbConfig
44
+ from .storage import (
45
+ delete_field_file,
46
+ ensure_local_file,
47
+ save_local_file,
48
+ )
49
+ from .storage import (
50
+ file_exists as storage_file_exists,
51
+ )
43
52
 
44
53
  # validate_endo_roi
45
54
  from .validate_endo_roi import validate_endo_roi
55
+ from .video import split_video
46
56
 
47
57
  # ffmpeg_wrapper
48
58
  from .video.ffmpeg_wrapper import (
49
59
  extract_frames,
50
60
  )
51
- from .video import (
52
- split_video
53
- )
54
-
55
61
 
56
62
  # --- Exports ---
57
63
 
@@ -84,5 +90,9 @@ __all__ = [
84
90
  "transcode_video",
85
91
  "transcode_videofile_if_required", # Added
86
92
  "extract_frames", # Added
93
+ "split_video",
94
+ "delete_field_file",
95
+ "ensure_local_file",
96
+ "save_local_file",
97
+ "storage_file_exists",
87
98
  ]
88
-
@@ -1,10 +1,22 @@
1
+ import logging
2
+ from pathlib import Path
3
+
1
4
  import torch
2
5
  from torchvision import models
3
6
  import torch.nn as nn
4
7
  from pytorch_lightning import LightningModule
5
8
  import numpy as np
9
+ from safetensors.torch import load_file
6
10
  from sklearn.metrics import precision_score, recall_score, f1_score
7
11
 
12
+ try: # Torchvision >= 0.13 exposes explicit weight enums
13
+ from torchvision.models import EfficientNet_B4_Weights, RegNet_X_800MF_Weights
14
+ except ImportError: # pragma: no cover - compatibility with older torchvision
15
+ EfficientNet_B4_Weights = None
16
+ RegNet_X_800MF_Weights = None
17
+
18
+ logger = logging.getLogger(__name__)
19
+
8
20
  METRICS_ON_STEP = False
9
21
 
10
22
 
@@ -41,6 +53,29 @@ def calculate_metrics(pred, target, threshold=0.5):
41
53
  }
42
54
 
43
55
 
56
+ def _load_torchvision_backbone(factory, *, weights_enum=None, load_pretrained=False):
57
+ """Instantiate a torchvision model without triggering unwanted downloads."""
58
+ if weights_enum is not None:
59
+ try:
60
+ weights = weights_enum.DEFAULT if load_pretrained else None
61
+ return factory(weights=weights)
62
+ except (TypeError, AttributeError):
63
+ # Fall back to legacy keyword on older torchvision versions
64
+ pass
65
+
66
+ try:
67
+ return factory(pretrained=load_pretrained)
68
+ except TypeError:
69
+ # Newer torchvision versions removed the pretrained kwarg; call without hints
70
+ try:
71
+ return factory()
72
+ except Exception as exc: # pragma: no cover - surfaced to caller for visibility
73
+ raise RuntimeError(
74
+ "Failed to instantiate torchvision backbone with load_pretrained="
75
+ f"{load_pretrained}."
76
+ ) from exc
77
+
78
+
44
79
  class MultiLabelClassificationNet(LightningModule):
45
80
  def __init__(
46
81
  self,
@@ -49,26 +84,40 @@ class MultiLabelClassificationNet(LightningModule):
49
84
  weight_decay=0.001,
50
85
  pos_weight=2,
51
86
  model_type="EfficientNetB4",
87
+ load_imagenet_weights: bool = False,
88
+ track_hparams: bool = True,
52
89
  ):
53
90
  super().__init__()
54
- self.save_hyperparameters()
55
- self.model_type = "RegNetX800MF" # model_type
56
- self.labels = labels
57
- self.n_classes = len(labels)
58
- self.val_preds = []
59
- self.val_targets = []
91
+ if track_hparams:
92
+ self.save_hyperparameters()
93
+ if labels is None:
94
+ raise ValueError("labels must be provided to initialize MultiLabelClassificationNet")
95
+
96
+ self.model_type = model_type
97
+ self.labels = list(labels)
98
+ self.n_classes = len(self.labels)
99
+ self.val_preds: list[np.ndarray] = []
100
+ self.val_targets: list[np.ndarray] = []
60
101
  self.pos_weight = pos_weight
61
102
  self.weight_decay = weight_decay
62
103
  self.lr = lr
63
104
  self.sigm = nn.Sigmoid()
64
105
 
65
106
  if model_type == "EfficientNetB4":
66
- self.model = models.efficientnet_b4(pretrained=True)
107
+ self.model = _load_torchvision_backbone(
108
+ models.efficientnet_b4,
109
+ weights_enum=EfficientNet_B4_Weights,
110
+ load_pretrained=load_imagenet_weights,
111
+ )
67
112
  num_ftrs = self.model.classifier[1].in_features
68
113
  self.model.classifier[1] = nn.Linear(num_ftrs, len(labels))
69
114
 
70
115
  elif model_type == "RegNetX800MF":
71
- self.model = models.regnet_x_800mf(pretrained=True)
116
+ self.model = _load_torchvision_backbone(
117
+ models.regnet_x_800mf,
118
+ weights_enum=RegNet_X_800MF_Weights,
119
+ load_pretrained=load_imagenet_weights,
120
+ )
72
121
  num_ftrs = self.model.fc.in_features
73
122
  self.model.fc = nn.Linear(num_ftrs, len(labels))
74
123
 
@@ -78,10 +127,47 @@ class MultiLabelClassificationNet(LightningModule):
78
127
 
79
128
  @classmethod
80
129
  def load_from_checkpoint(cls, checkpoint_path, *args, **kwargs):
81
- instance = super(MultiLabelClassificationNet, cls).load_from_checkpoint(
130
+ path = Path(checkpoint_path)
131
+ suffix = path.suffix.lower()
132
+
133
+ if suffix == ".safetensors":
134
+ map_location = kwargs.pop("map_location", "cpu")
135
+ strict = kwargs.pop("strict", True)
136
+ labels = kwargs.pop("labels", None)
137
+ if not labels:
138
+ raise ValueError("labels must be provided when loading .safetensors checkpoints")
139
+ model_type = kwargs.pop("model_type", None) or "EfficientNetB4"
140
+ load_imagenet = kwargs.pop("load_imagenet_weights", False)
141
+
142
+ device = torch.device(map_location) if map_location is not None else torch.device("cpu")
143
+ if isinstance(device, torch.device):
144
+ device_hint = f"{device.type}:{device.index}" if device.index is not None else device.type
145
+ else:
146
+ device_hint = device
147
+
148
+ state_dict = load_file(path, device=device_hint)
149
+
150
+ instance = cls(
151
+ labels=labels,
152
+ model_type=model_type,
153
+ load_imagenet_weights=load_imagenet,
154
+ track_hparams=False,
155
+ *args,
156
+ **kwargs,
157
+ )
158
+ missing, unexpected = instance.load_state_dict(state_dict, strict=strict)
159
+
160
+ if missing:
161
+ logger.warning("Missing parameters when loading %s: %s", path, missing)
162
+ if unexpected:
163
+ logger.warning("Unexpected parameters when loading %s: %s", path, unexpected)
164
+
165
+ instance.to(device)
166
+ return instance
167
+
168
+ return super(MultiLabelClassificationNet, cls).load_from_checkpoint(
82
169
  checkpoint_path, *args, **kwargs
83
170
  )
84
- return instance
85
171
 
86
172
  def forward(self, x): # pylint: disable=arguments-differ
87
173
  x = self.model(x)
@@ -113,18 +199,24 @@ class MultiLabelClassificationNet(LightningModule):
113
199
 
114
200
  def validation_epoch_end(self, _outputs):
115
201
  """Called at the end of validation to aggregate outputs"""
116
- self.val_preds = np.concatenate([_ for _ in self.val_preds])
117
- self.val_targets = np.concatenate([_ for _ in self.val_targets])
118
-
119
- metrics = calculate_metrics(self.val_preds, self.val_targets, threshold=0.5)
120
- for key, value in metrics.items():
121
- value = value.tolist()
122
- if isinstance(value, list):
123
- for i, _value in enumerate(value):
202
+ val_preds_np = np.concatenate(self.val_preds)
203
+ val_targets_np = np.concatenate(self.val_targets)
204
+
205
+ metrics = calculate_metrics(val_preds_np, val_targets_np, threshold=0.5)
206
+ for key, metric_value in metrics.items():
207
+ if isinstance(metric_value, np.ndarray):
208
+ processed_value = metric_value.tolist()
209
+ elif isinstance(metric_value, (list, tuple)):
210
+ processed_value = list(metric_value)
211
+ else:
212
+ processed_value = float(metric_value)
213
+
214
+ if isinstance(processed_value, list):
215
+ for i, single_value in enumerate(processed_value):
124
216
  name = "val/" + f"{key}/{self.labels[i]}"
125
217
  self.log(
126
218
  name,
127
- _value,
219
+ float(single_value),
128
220
  on_epoch=True,
129
221
  on_step=METRICS_ON_STEP,
130
222
  prog_bar=False,
@@ -132,7 +224,11 @@ class MultiLabelClassificationNet(LightningModule):
132
224
  else:
133
225
  name = "val/" + f"{key}"
134
226
  self.log(
135
- name, value, on_epoch=True, on_step=METRICS_ON_STEP, prog_bar=True
227
+ name,
228
+ float(processed_value),
229
+ on_epoch=True,
230
+ on_step=METRICS_ON_STEP,
231
+ prog_bar=True,
136
232
  )
137
233
 
138
234
  self.val_preds = []