endoreg-db 0.8.8.9__py3-none-any.whl → 0.8.9.10__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 (453) hide show
  1. endoreg_db/admin.py +10 -5
  2. endoreg_db/apps.py +4 -7
  3. endoreg_db/authz/auth.py +1 -0
  4. endoreg_db/authz/backends.py +1 -1
  5. endoreg_db/authz/management/commands/list_routes.py +2 -0
  6. endoreg_db/authz/middleware.py +8 -7
  7. endoreg_db/authz/permissions.py +21 -10
  8. endoreg_db/authz/policy.py +14 -19
  9. endoreg_db/authz/views_auth.py +14 -10
  10. endoreg_db/codemods/rename_datetime_fields.py +8 -1
  11. endoreg_db/exceptions.py +5 -2
  12. endoreg_db/forms/__init__.py +0 -1
  13. endoreg_db/forms/examination_form.py +4 -3
  14. endoreg_db/forms/patient_finding_intervention_form.py +30 -8
  15. endoreg_db/forms/patient_form.py +9 -13
  16. endoreg_db/forms/questionnaires/__init__.py +1 -1
  17. endoreg_db/forms/settings/__init__.py +4 -1
  18. endoreg_db/forms/unit.py +2 -1
  19. endoreg_db/helpers/count_db.py +17 -14
  20. endoreg_db/helpers/default_objects.py +2 -1
  21. endoreg_db/helpers/download_segmentation_model.py +4 -3
  22. endoreg_db/helpers/interact.py +0 -5
  23. endoreg_db/helpers/test_video_helper.py +33 -25
  24. endoreg_db/import_files/__init__.py +1 -1
  25. endoreg_db/import_files/context/__init__.py +1 -1
  26. endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
  27. endoreg_db/import_files/context/ensure_center.py +4 -4
  28. endoreg_db/import_files/context/file_lock.py +3 -3
  29. endoreg_db/import_files/context/import_context.py +11 -12
  30. endoreg_db/import_files/context/validate_directories.py +1 -0
  31. endoreg_db/import_files/file_storage/create_report_file.py +57 -34
  32. endoreg_db/import_files/file_storage/create_video_file.py +64 -35
  33. endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
  34. endoreg_db/import_files/file_storage/state_management.py +146 -83
  35. endoreg_db/import_files/file_storage/storage.py +5 -1
  36. endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
  37. endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
  38. endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
  39. endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
  40. endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
  41. endoreg_db/import_files/report_import_service.py +36 -30
  42. endoreg_db/import_files/video_import_service.py +27 -23
  43. endoreg_db/logger_conf.py +56 -40
  44. endoreg_db/management/__init__.py +1 -1
  45. endoreg_db/management/commands/__init__.py +1 -1
  46. endoreg_db/management/commands/check_auth.py +45 -38
  47. endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
  48. endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
  49. endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
  50. endoreg_db/management/commands/fix_video_paths.py +75 -54
  51. endoreg_db/management/commands/import_report.py +1 -3
  52. endoreg_db/management/commands/list_routes.py +2 -0
  53. endoreg_db/management/commands/load_ai_model_data.py +8 -2
  54. endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
  55. endoreg_db/management/commands/load_center_data.py +3 -3
  56. endoreg_db/management/commands/load_distribution_data.py +35 -38
  57. endoreg_db/management/commands/load_endoscope_data.py +0 -3
  58. endoreg_db/management/commands/load_examination_data.py +20 -4
  59. endoreg_db/management/commands/load_finding_data.py +18 -3
  60. endoreg_db/management/commands/load_gender_data.py +17 -24
  61. endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
  62. endoreg_db/management/commands/load_information_source.py +0 -3
  63. endoreg_db/management/commands/load_lab_value_data.py +14 -3
  64. endoreg_db/management/commands/load_legacy_data.py +303 -0
  65. endoreg_db/management/commands/load_name_data.py +1 -2
  66. endoreg_db/management/commands/load_pdf_type_data.py +4 -8
  67. endoreg_db/management/commands/load_profession_data.py +0 -1
  68. endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
  69. endoreg_db/management/commands/load_requirement_data.py +6 -2
  70. endoreg_db/management/commands/load_unit_data.py +0 -4
  71. endoreg_db/management/commands/load_user_groups.py +5 -7
  72. endoreg_db/management/commands/model_input.py +169 -0
  73. endoreg_db/management/commands/register_ai_model.py +22 -16
  74. endoreg_db/management/commands/setup_endoreg_db.py +110 -32
  75. endoreg_db/management/commands/storage_management.py +14 -8
  76. endoreg_db/management/commands/summarize_db_content.py +154 -63
  77. endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
  78. endoreg_db/management/commands/validate_video_files.py +82 -50
  79. endoreg_db/management/commands/video_validation.py +4 -6
  80. endoreg_db/migrations/0001_initial.py +112 -63
  81. endoreg_db/migrations/__init__.py +0 -0
  82. endoreg_db/models/__init__.py +8 -0
  83. endoreg_db/models/administration/ai/active_model.py +5 -5
  84. endoreg_db/models/administration/ai/ai_model.py +41 -18
  85. endoreg_db/models/administration/ai/model_type.py +1 -0
  86. endoreg_db/models/administration/case/case.py +22 -22
  87. endoreg_db/models/administration/center/__init__.py +5 -5
  88. endoreg_db/models/administration/center/center.py +6 -2
  89. endoreg_db/models/administration/center/center_resource.py +18 -4
  90. endoreg_db/models/administration/center/center_shift.py +3 -1
  91. endoreg_db/models/administration/center/center_waste.py +6 -2
  92. endoreg_db/models/administration/person/__init__.py +1 -1
  93. endoreg_db/models/administration/person/employee/__init__.py +1 -1
  94. endoreg_db/models/administration/person/employee/employee_type.py +3 -1
  95. endoreg_db/models/administration/person/examiner/__init__.py +1 -1
  96. endoreg_db/models/administration/person/examiner/examiner.py +10 -2
  97. endoreg_db/models/administration/person/names/first_name.py +6 -4
  98. endoreg_db/models/administration/person/names/last_name.py +4 -3
  99. endoreg_db/models/administration/person/patient/__init__.py +1 -1
  100. endoreg_db/models/administration/person/patient/patient.py +0 -1
  101. endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
  102. endoreg_db/models/administration/person/person.py +1 -1
  103. endoreg_db/models/administration/product/__init__.py +7 -6
  104. endoreg_db/models/administration/product/product.py +6 -2
  105. endoreg_db/models/administration/product/product_group.py +9 -7
  106. endoreg_db/models/administration/product/product_material.py +9 -2
  107. endoreg_db/models/administration/product/reference_product.py +64 -15
  108. endoreg_db/models/administration/qualification/qualification.py +3 -1
  109. endoreg_db/models/administration/shift/shift.py +3 -1
  110. endoreg_db/models/administration/shift/shift_type.py +12 -4
  111. endoreg_db/models/aidataset/__init__.py +5 -0
  112. endoreg_db/models/aidataset/aidataset.py +193 -0
  113. endoreg_db/models/label/__init__.py +1 -1
  114. endoreg_db/models/label/label.py +10 -2
  115. endoreg_db/models/label/label_set.py +3 -1
  116. endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
  117. endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
  118. endoreg_db/models/media/__init__.py +12 -5
  119. endoreg_db/models/media/frame/__init__.py +1 -1
  120. endoreg_db/models/media/frame/frame.py +34 -8
  121. endoreg_db/models/media/pdf/__init__.py +2 -1
  122. endoreg_db/models/media/pdf/raw_pdf.py +11 -4
  123. endoreg_db/models/media/pdf/report_file.py +6 -2
  124. endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
  125. endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
  126. endoreg_db/models/media/video/create_from_file.py +20 -41
  127. endoreg_db/models/media/video/pipe_1.py +75 -30
  128. endoreg_db/models/media/video/pipe_2.py +37 -12
  129. endoreg_db/models/media/video/video_file.py +36 -24
  130. endoreg_db/models/media/video/video_file_ai.py +235 -70
  131. endoreg_db/models/media/video/video_file_anonymize.py +240 -65
  132. endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
  133. endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
  134. endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
  135. endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
  136. endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
  137. endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
  138. endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
  139. endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
  140. endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
  141. endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
  142. endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
  143. endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
  144. endoreg_db/models/media/video/video_file_io.py +85 -33
  145. endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
  146. endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
  147. endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
  148. endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
  149. endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
  150. endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
  151. endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
  152. endoreg_db/models/media/video/video_file_segments.py +118 -27
  153. endoreg_db/models/media/video/video_metadata.py +25 -6
  154. endoreg_db/models/media/video/video_processing.py +54 -15
  155. endoreg_db/models/medical/__init__.py +3 -13
  156. endoreg_db/models/medical/contraindication/__init__.py +3 -1
  157. endoreg_db/models/medical/disease.py +18 -6
  158. endoreg_db/models/medical/event.py +6 -2
  159. endoreg_db/models/medical/examination/__init__.py +5 -1
  160. endoreg_db/models/medical/examination/examination.py +22 -6
  161. endoreg_db/models/medical/examination/examination_indication.py +23 -7
  162. endoreg_db/models/medical/examination/examination_time.py +6 -2
  163. endoreg_db/models/medical/finding/__init__.py +3 -1
  164. endoreg_db/models/medical/finding/finding.py +37 -12
  165. endoreg_db/models/medical/finding/finding_classification.py +27 -8
  166. endoreg_db/models/medical/finding/finding_intervention.py +19 -6
  167. endoreg_db/models/medical/finding/finding_type.py +3 -1
  168. endoreg_db/models/medical/hardware/__init__.py +1 -1
  169. endoreg_db/models/medical/hardware/endoscope.py +14 -2
  170. endoreg_db/models/medical/laboratory/__init__.py +1 -1
  171. endoreg_db/models/medical/laboratory/lab_value.py +139 -39
  172. endoreg_db/models/medical/medication/__init__.py +7 -3
  173. endoreg_db/models/medical/medication/medication.py +3 -1
  174. endoreg_db/models/medical/medication/medication_indication.py +3 -1
  175. endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
  176. endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
  177. endoreg_db/models/medical/medication/medication_schedule.py +3 -1
  178. endoreg_db/models/medical/patient/__init__.py +2 -10
  179. endoreg_db/models/medical/patient/medication_examples.py +3 -14
  180. endoreg_db/models/medical/patient/patient_disease.py +17 -5
  181. endoreg_db/models/medical/patient/patient_event.py +12 -4
  182. endoreg_db/models/medical/patient/patient_examination.py +52 -15
  183. endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
  184. endoreg_db/models/medical/patient/patient_finding.py +105 -29
  185. endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
  186. endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
  187. endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
  188. endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
  189. endoreg_db/models/medical/patient/patient_medication.py +25 -7
  190. endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
  191. endoreg_db/models/metadata/model_meta.py +40 -12
  192. endoreg_db/models/metadata/model_meta_logic.py +51 -16
  193. endoreg_db/models/metadata/sensitive_meta.py +65 -28
  194. endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
  195. endoreg_db/models/metadata/video_meta.py +146 -39
  196. endoreg_db/models/metadata/video_prediction_logic.py +70 -21
  197. endoreg_db/models/metadata/video_prediction_meta.py +80 -27
  198. endoreg_db/models/operation_log.py +63 -0
  199. endoreg_db/models/other/__init__.py +10 -10
  200. endoreg_db/models/other/distribution/__init__.py +9 -7
  201. endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
  202. endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
  203. endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
  204. endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
  205. endoreg_db/models/other/emission/__init__.py +1 -1
  206. endoreg_db/models/other/emission/emission_factor.py +9 -3
  207. endoreg_db/models/other/information_source.py +15 -5
  208. endoreg_db/models/other/material.py +3 -1
  209. endoreg_db/models/other/transport_route.py +3 -1
  210. endoreg_db/models/other/unit.py +6 -2
  211. endoreg_db/models/report/report.py +0 -1
  212. endoreg_db/models/requirement/requirement.py +84 -27
  213. endoreg_db/models/requirement/requirement_error.py +5 -6
  214. endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
  215. endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
  216. endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
  217. endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
  218. endoreg_db/models/requirement/requirement_operator.py +28 -8
  219. endoreg_db/models/requirement/requirement_set.py +34 -11
  220. endoreg_db/models/state/__init__.py +1 -0
  221. endoreg_db/models/state/audit_ledger.py +9 -2
  222. endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
  223. endoreg_db/models/state/processing_history/processing_history.py +136 -0
  224. endoreg_db/models/state/raw_pdf.py +0 -1
  225. endoreg_db/models/state/video.py +2 -3
  226. endoreg_db/models/utils.py +4 -2
  227. endoreg_db/queries/__init__.py +2 -6
  228. endoreg_db/queries/annotations/__init__.py +1 -3
  229. endoreg_db/queries/annotations/legacy.py +37 -26
  230. endoreg_db/root_urls.py +3 -4
  231. endoreg_db/schemas/examination_evaluation.py +3 -0
  232. endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
  233. endoreg_db/serializers/__init__.py +2 -8
  234. endoreg_db/serializers/administration/__init__.py +1 -2
  235. endoreg_db/serializers/administration/ai/__init__.py +0 -1
  236. endoreg_db/serializers/administration/ai/active_model.py +3 -1
  237. endoreg_db/serializers/administration/ai/ai_model.py +5 -3
  238. endoreg_db/serializers/administration/ai/model_type.py +3 -1
  239. endoreg_db/serializers/administration/center.py +7 -2
  240. endoreg_db/serializers/administration/gender.py +4 -2
  241. endoreg_db/serializers/anonymization.py +13 -13
  242. endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
  243. endoreg_db/serializers/examination/__init__.py +1 -1
  244. endoreg_db/serializers/examination/base.py +12 -13
  245. endoreg_db/serializers/examination/dropdown.py +6 -7
  246. endoreg_db/serializers/examination_serializer.py +3 -6
  247. endoreg_db/serializers/finding/__init__.py +1 -1
  248. endoreg_db/serializers/finding/finding.py +14 -7
  249. endoreg_db/serializers/finding_classification/__init__.py +3 -3
  250. endoreg_db/serializers/finding_classification/choice.py +3 -3
  251. endoreg_db/serializers/finding_classification/classification.py +2 -4
  252. endoreg_db/serializers/label_video_segment/__init__.py +5 -3
  253. endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
  254. endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
  255. endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
  256. endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
  257. endoreg_db/serializers/meta/__init__.py +1 -2
  258. endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
  259. endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
  260. endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
  261. endoreg_db/serializers/misc/__init__.py +2 -2
  262. endoreg_db/serializers/misc/file_overview.py +11 -7
  263. endoreg_db/serializers/misc/stats.py +10 -8
  264. endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
  265. endoreg_db/serializers/misc/upload_job.py +32 -29
  266. endoreg_db/serializers/patient/__init__.py +2 -1
  267. endoreg_db/serializers/patient/patient.py +32 -15
  268. endoreg_db/serializers/patient/patient_dropdown.py +11 -3
  269. endoreg_db/serializers/patient_examination/__init__.py +1 -1
  270. endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
  271. endoreg_db/serializers/patient_finding/__init__.py +1 -1
  272. endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
  273. endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
  274. endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
  275. endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
  276. endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
  277. endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
  278. endoreg_db/serializers/pdf/__init__.py +1 -3
  279. endoreg_db/serializers/requirements/requirement_schema.py +1 -6
  280. endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
  281. endoreg_db/serializers/video/__init__.py +2 -2
  282. endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
  283. endoreg_db/serializers/video/video_file_brief.py +6 -2
  284. endoreg_db/serializers/video/video_file_detail.py +36 -23
  285. endoreg_db/serializers/video/video_file_list.py +4 -2
  286. endoreg_db/serializers/video/video_processing_history.py +54 -50
  287. endoreg_db/services/__init__.py +1 -1
  288. endoreg_db/services/anonymization.py +2 -2
  289. endoreg_db/services/examination_evaluation.py +40 -17
  290. endoreg_db/services/model_meta_from_hf.py +76 -0
  291. endoreg_db/services/polling_coordinator.py +101 -70
  292. endoreg_db/services/pseudonym_service.py +27 -22
  293. endoreg_db/services/report_import.py +6 -3
  294. endoreg_db/services/segment_sync.py +75 -59
  295. endoreg_db/services/video_import.py +6 -7
  296. endoreg_db/urls/__init__.py +2 -2
  297. endoreg_db/urls/ai.py +7 -25
  298. endoreg_db/urls/anonymization.py +61 -15
  299. endoreg_db/urls/auth.py +4 -4
  300. endoreg_db/urls/classification.py +4 -9
  301. endoreg_db/urls/examination.py +27 -18
  302. endoreg_db/urls/media.py +27 -34
  303. endoreg_db/urls/patient.py +11 -7
  304. endoreg_db/urls/requirements.py +3 -1
  305. endoreg_db/urls/root_urls.py +2 -3
  306. endoreg_db/urls/stats.py +24 -16
  307. endoreg_db/urls/upload.py +3 -11
  308. endoreg_db/utils/__init__.py +14 -15
  309. endoreg_db/utils/ai/__init__.py +1 -1
  310. endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
  311. endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
  312. endoreg_db/utils/ai/get.py +2 -1
  313. endoreg_db/utils/ai/inference_dataset.py +14 -15
  314. endoreg_db/utils/ai/model_training/config.py +117 -0
  315. endoreg_db/utils/ai/model_training/dataset.py +74 -0
  316. endoreg_db/utils/ai/model_training/losses.py +68 -0
  317. endoreg_db/utils/ai/model_training/metrics.py +78 -0
  318. endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
  319. endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
  320. endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
  321. endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
  322. endoreg_db/utils/ai/predict.py +4 -4
  323. endoreg_db/utils/ai/preprocess.py +19 -11
  324. endoreg_db/utils/calc_duration_seconds.py +4 -4
  325. endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
  326. endoreg_db/utils/check_video_files.py +74 -47
  327. endoreg_db/utils/cropping.py +10 -9
  328. endoreg_db/utils/dataloader.py +11 -3
  329. endoreg_db/utils/dates.py +3 -4
  330. endoreg_db/utils/defaults/set_default_center.py +7 -6
  331. endoreg_db/utils/env.py +6 -2
  332. endoreg_db/utils/extract_specific_frames.py +24 -9
  333. endoreg_db/utils/file_operations.py +30 -18
  334. endoreg_db/utils/fix_video_path_direct.py +57 -41
  335. endoreg_db/utils/frame_anonymization_utils.py +157 -157
  336. endoreg_db/utils/hashs.py +3 -18
  337. endoreg_db/utils/links/requirement_link.py +96 -52
  338. endoreg_db/utils/ocr.py +30 -25
  339. endoreg_db/utils/operation_log.py +61 -0
  340. endoreg_db/utils/parse_and_generate_yaml.py +12 -13
  341. endoreg_db/utils/paths.py +6 -6
  342. endoreg_db/utils/permissions.py +40 -24
  343. endoreg_db/utils/pipelines/process_video_dir.py +50 -26
  344. endoreg_db/utils/product/sum_emissions.py +5 -3
  345. endoreg_db/utils/product/sum_weights.py +4 -2
  346. endoreg_db/utils/pydantic_models/__init__.py +3 -4
  347. endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
  348. endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
  349. endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
  350. endoreg_db/utils/setup_config.py +21 -5
  351. endoreg_db/utils/storage.py +3 -1
  352. endoreg_db/utils/translation.py +19 -15
  353. endoreg_db/utils/uuid.py +1 -0
  354. endoreg_db/utils/validate_endo_roi.py +12 -4
  355. endoreg_db/utils/validate_subcategory_dict.py +26 -24
  356. endoreg_db/utils/validate_video_detailed.py +207 -149
  357. endoreg_db/utils/video/__init__.py +7 -3
  358. endoreg_db/utils/video/extract_frames.py +30 -18
  359. endoreg_db/utils/video/ffmpeg_wrapper.py +217 -52
  360. endoreg_db/utils/video/names.py +11 -6
  361. endoreg_db/utils/video/streaming_processor.py +175 -101
  362. endoreg_db/utils/video/video_splitter.py +30 -19
  363. endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
  364. endoreg_db/views/__init__.py +0 -20
  365. endoreg_db/views/anonymization/__init__.py +6 -2
  366. endoreg_db/views/anonymization/media_management.py +2 -6
  367. endoreg_db/views/anonymization/overview.py +34 -1
  368. endoreg_db/views/anonymization/validate.py +79 -18
  369. endoreg_db/views/auth/__init__.py +1 -1
  370. endoreg_db/views/auth/keycloak.py +16 -14
  371. endoreg_db/views/examination/__init__.py +12 -15
  372. endoreg_db/views/examination/examination.py +5 -5
  373. endoreg_db/views/examination/examination_manifest_cache.py +5 -5
  374. endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
  375. endoreg_db/views/examination/get_finding_classifications.py +9 -7
  376. endoreg_db/views/examination/get_findings.py +8 -10
  377. endoreg_db/views/examination/get_instruments.py +3 -2
  378. endoreg_db/views/examination/get_interventions.py +1 -1
  379. endoreg_db/views/finding/__init__.py +2 -2
  380. endoreg_db/views/finding/finding.py +58 -54
  381. endoreg_db/views/finding/get_classifications.py +1 -1
  382. endoreg_db/views/finding/get_interventions.py +1 -1
  383. endoreg_db/views/finding_classification/__init__.py +5 -5
  384. endoreg_db/views/finding_classification/finding_classification.py +5 -6
  385. endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
  386. endoreg_db/views/media/__init__.py +13 -13
  387. endoreg_db/views/media/pdf_media.py +9 -9
  388. endoreg_db/views/media/sensitive_metadata.py +10 -7
  389. endoreg_db/views/media/video_media.py +4 -4
  390. endoreg_db/views/meta/__init__.py +1 -1
  391. endoreg_db/views/meta/sensitive_meta_list.py +20 -22
  392. endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
  393. endoreg_db/views/misc/__init__.py +6 -34
  394. endoreg_db/views/misc/center.py +2 -1
  395. endoreg_db/views/misc/csrf.py +2 -1
  396. endoreg_db/views/misc/gender.py +2 -1
  397. endoreg_db/views/misc/stats.py +141 -106
  398. endoreg_db/views/patient/__init__.py +1 -3
  399. endoreg_db/views/patient/patient.py +141 -99
  400. endoreg_db/views/patient_examination/__init__.py +5 -5
  401. endoreg_db/views/patient_examination/patient_examination.py +43 -42
  402. endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
  403. endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
  404. endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
  405. endoreg_db/views/patient_examination/video.py +114 -80
  406. endoreg_db/views/patient_finding/__init__.py +1 -1
  407. endoreg_db/views/patient_finding/patient_finding.py +17 -10
  408. endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
  409. endoreg_db/views/patient_finding_classification/__init__.py +1 -1
  410. endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
  411. endoreg_db/views/report/reimport.py +1 -1
  412. endoreg_db/views/report/report_stream.py +5 -8
  413. endoreg_db/views/requirement/__init__.py +2 -1
  414. endoreg_db/views/requirement/evaluate.py +7 -9
  415. endoreg_db/views/requirement/lookup.py +2 -3
  416. endoreg_db/views/requirement/lookup_store.py +0 -1
  417. endoreg_db/views/requirement/requirement_utils.py +2 -4
  418. endoreg_db/views/stats/__init__.py +4 -4
  419. endoreg_db/views/stats/stats_views.py +152 -115
  420. endoreg_db/views/video/__init__.py +18 -27
  421. endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
  422. endoreg_db/views/{ai → video/ai}/label.py +20 -16
  423. endoreg_db/views/video/correction.py +5 -6
  424. endoreg_db/views/video/reimport.py +134 -99
  425. endoreg_db/views/video/segments_crud.py +134 -44
  426. endoreg_db/views/video/video_apply_mask.py +13 -12
  427. endoreg_db/views/video/video_correction.py +2 -1
  428. endoreg_db/views/video/video_download_processed.py +15 -15
  429. endoreg_db/views/video/video_meta_stats.py +7 -6
  430. endoreg_db/views/video/video_processing_history.py +3 -2
  431. endoreg_db/views/video/video_remove_frames.py +13 -12
  432. endoreg_db/views/video/video_stream.py +110 -82
  433. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
  434. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +436 -433
  435. endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +0 -119
  436. endoreg_db/management/commands/import_fallback_video.py +0 -203
  437. endoreg_db/management/commands/import_video.py +0 -422
  438. endoreg_db/management/commands/import_video_with_classification.py +0 -367
  439. endoreg_db/models/media/processing_history/processing_history.py +0 -96
  440. endoreg_db/serializers/label/__init__.py +0 -7
  441. endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
  442. endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
  443. endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
  444. endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
  445. endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
  446. endoreg_db/services/__old/pdf_import.py +0 -1487
  447. endoreg_db/services/__old/video_import.py +0 -1306
  448. endoreg_db/tasks/upload_tasks.py +0 -216
  449. endoreg_db/tasks/video_ingest.py +0 -161
  450. endoreg_db/tasks/video_processing_tasks.py +0 -327
  451. endoreg_db/views/misc/translation.py +0 -182
  452. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
  453. {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,303 @@
1
+ # endoreg_db/management/commands/load_legacy_data.py
2
+
3
+ import json
4
+ from pathlib import Path
5
+
6
+ from django.conf import settings
7
+ from django.core.management.base import BaseCommand, CommandError
8
+ from django.db import transaction
9
+
10
+ from endoreg_db.models import (
11
+ AIDataSet,
12
+ Frame,
13
+ ImageClassificationAnnotation,
14
+ Label,
15
+ LabelSet,
16
+ VideoFile,
17
+ )
18
+
19
+ DEFAULT_LABELSET_NAME = (
20
+ "multilabel_classification_colonoscopy_default" # must be present in the DB
21
+ )
22
+ DEFAULT_LABELSET_VERSION = 1
23
+
24
+
25
+ class Command(BaseCommand):
26
+ help = (
27
+ "Import legacy multilabel image data from JSONL + images into the database.\n"
28
+ "- Creates Frames linked to a given VideoFile\n"
29
+ "- Creates ImageClassificationAnnotations (value=True) for each listed label\n"
30
+ "- Reuses/extends an existing LabelSet\n"
31
+ "- Fills an AIDataSet (image dataset) with all annotations via image_annotations"
32
+ )
33
+
34
+ def add_arguments(self, parser):
35
+ parser.add_argument(
36
+ "--jsonl-path",
37
+ type=str,
38
+ default=str(
39
+ Path(settings.BASE_DIR)
40
+ / "data"
41
+ / "legacy_data"
42
+ / "legacy_img_dicts.jsonl"
43
+ ),
44
+ help="Path to legacy_img_dicts.jsonl",
45
+ )
46
+ parser.add_argument(
47
+ "--images-root",
48
+ type=str,
49
+ default=str(Path(settings.BASE_DIR) / "data" / "legacy_data" / "images"),
50
+ help="Root directory containing legacy images.",
51
+ )
52
+ # All imported frames need to belong to some VideoFile.
53
+ parser.add_argument(
54
+ "--video-id",
55
+ type=int,
56
+ required=True,
57
+ help="ID of an existing VideoFile to attach all legacy Frames to.",
58
+ )
59
+ parser.add_argument(
60
+ "--dataset-name",
61
+ type=str,
62
+ default="legacy_multilabel_dataset_v1", # later change this if needed
63
+ help="Name for the created/reused AIDataSet.",
64
+ )
65
+ parser.add_argument(
66
+ "--dataset-description",
67
+ type=str,
68
+ default="Legacy multilabel colonoscopy dataset imported from JSONL.",
69
+ help="Description for the created AIDataSet.",
70
+ )
71
+ parser.add_argument(
72
+ "--labelset-name",
73
+ type=str,
74
+ default=DEFAULT_LABELSET_NAME,
75
+ help="LabelSet name to use (must exist).",
76
+ )
77
+ parser.add_argument(
78
+ "--labelset-version",
79
+ type=int,
80
+ default=DEFAULT_LABELSET_VERSION,
81
+ help="LabelSet version to use (must exist).",
82
+ )
83
+ parser.add_argument(
84
+ "--dry-run",
85
+ action="store_true",
86
+ help="Parse and validate, but do not write anything to the database.",
87
+ )
88
+
89
+ def handle(self, *args, **options):
90
+ jsonl_path = Path(options["jsonl_path"])
91
+ images_root = Path(options["images_root"])
92
+ video_id = options["video_id"]
93
+ dataset_name = options["dataset_name"]
94
+ dataset_description = options["dataset_description"]
95
+ labelset_name = options["labelset_name"]
96
+ labelset_version = options["labelset_version"]
97
+ dry_run = options["dry_run"]
98
+
99
+ # --- Basic checks ---
100
+ if not jsonl_path.exists():
101
+ raise CommandError(f"JSONL file not found: {jsonl_path}")
102
+
103
+ if not images_root.exists():
104
+ raise CommandError(f"Images root directory not found: {images_root}")
105
+
106
+ try:
107
+ video = VideoFile.objects.get(id=video_id)
108
+ except VideoFile.DoesNotExist:
109
+ raise CommandError(f"VideoFile with id={video_id} does not exist.")
110
+
111
+ self.stdout.write(
112
+ self.style.NOTICE(f"Using VideoFile id={video.id} for all Frames.")
113
+ )
114
+
115
+ # Ensure this VideoFile uses the legacy images folder as its frame_dir
116
+ # IMPORTANT: we only set this if frame_dir is empty, so we don't break other videos.
117
+ if not video.frame_dir:
118
+ video.frame_dir = str(images_root) # images_root is Path(...)
119
+ video.save(update_fields=["frame_dir"])
120
+ self.stdout.write(
121
+ self.style.NOTICE(
122
+ f"Set frame_dir for VideoFile id={video.id} to '{video.frame_dir}' "
123
+ "for legacy image frames."
124
+ )
125
+ )
126
+ else:
127
+ self.stdout.write(
128
+ self.style.WARNING(
129
+ f"VideoFile id={video.id} already has frame_dir='{video.frame_dir}'. "
130
+ "Legacy Frames will be resolved relative to this directory."
131
+ )
132
+ )
133
+
134
+ # --- Use existing LabelSet (v1) ---
135
+ labelset = self._get_existing_labelset(
136
+ labelset_name=labelset_name,
137
+ labelset_version=labelset_version,
138
+ )
139
+
140
+ self.stdout.write(
141
+ self.style.SUCCESS(
142
+ f"Using LabelSet '{labelset.name}' (version={labelset.version}, id={labelset.id})."
143
+ )
144
+ )
145
+
146
+ # --- Create or reuse AIDataSet (image dataset) ---
147
+ if dry_run:
148
+ self.stdout.write(
149
+ self.style.WARNING("Dry run: AIDataSet will NOT be created.")
150
+ )
151
+ ai_dataset = None
152
+ else:
153
+ ai_dataset, created = AIDataSet.objects.get_or_create(
154
+ name=dataset_name,
155
+ defaults={
156
+ "description": dataset_description,
157
+ "dataset_type": AIDataSet.DATASET_TYPE_IMAGE,
158
+ "ai_model_type": AIDataSet.AI_MODEL_TYPE_IMAGE_MULTILABEL,
159
+ "is_active": True,
160
+ },
161
+ )
162
+ if created:
163
+ self.stdout.write(
164
+ self.style.SUCCESS(
165
+ f"Created AIDataSet id={ai_dataset.id}, name='{ai_dataset.name}'."
166
+ )
167
+ )
168
+ else:
169
+ # Use the helper method so this works even if we add video/text later
170
+ current_count = ai_dataset.get_annotations_queryset().count()
171
+ self.stdout.write(
172
+ self.style.WARNING(
173
+ f"Re-using existing AIDataSet id={ai_dataset.id}, name='{ai_dataset.name}'. "
174
+ f"(Current annotation_count={current_count})"
175
+ )
176
+ )
177
+
178
+ frame_counter = 0
179
+ annotation_counter = 0
180
+
181
+ # Use transaction unless dry-run
182
+ ctx = transaction.atomic if not dry_run else self._noop_context
183
+
184
+ with ctx():
185
+ with jsonl_path.open("r", encoding="utf-8") as f:
186
+ for line_num, line in enumerate(f, start=1):
187
+ line = line.strip()
188
+ if not line:
189
+ continue
190
+
191
+ try:
192
+ item = json.loads(line)
193
+ except json.JSONDecodeError as exc:
194
+ raise CommandError(
195
+ f"Invalid JSON on line {line_num} of {jsonl_path}: {exc}"
196
+ )
197
+
198
+ labels_list = item.get("labels", [])
199
+ filename = item.get("filename")
200
+ # old_examination_id and old_id are available if you want them later:
201
+ old_id = item.get("old_id")
202
+ old_exam_id = item.get("old_examination_id")
203
+
204
+ if not filename:
205
+ self.stdout.write(
206
+ self.style.WARNING(
207
+ f"Skipping line {line_num}: no 'filename' key."
208
+ )
209
+ )
210
+ continue
211
+
212
+ image_path = images_root / filename
213
+ if not image_path.exists():
214
+ self.stdout.write(
215
+ self.style.WARNING(
216
+ f"Image file does not exist for line {line_num}: {image_path}"
217
+ )
218
+ )
219
+ # Still create Frame so DB + paths are consistent.
220
+
221
+ # --- Create Frame ---
222
+ frame_counter += 1
223
+ frame = Frame(
224
+ video=video,
225
+ frame_number=frame_counter,
226
+ relative_path=filename, # filename is relative under images_root
227
+ timestamp=None,
228
+ old_examination_id=old_exam_id, # keeping old examination id legacy exam id for grouping
229
+ is_extracted=True,
230
+ )
231
+ if not dry_run:
232
+ frame.save()
233
+
234
+ # --- Create annotations for positive labels ---
235
+ for label_name in labels_list:
236
+ label = self._get_or_create_label_and_attach_to_labelset(
237
+ label_name=label_name,
238
+ labelset=labelset,
239
+ )
240
+
241
+ annotation_counter += 1
242
+ annotation = ImageClassificationAnnotation(
243
+ frame=frame,
244
+ label=label,
245
+ value=True,
246
+ annotator="legacy_import",
247
+ )
248
+ if not dry_run:
249
+ annotation.save()
250
+ if ai_dataset is not None:
251
+ # IMPORTANT CHANGE:
252
+ # Use the AIDataSet helper, which for dataset_type='image'
253
+ # returns the image_annotations manager.
254
+ ai_dataset.get_annotations_queryset().add(annotation)
255
+
256
+ # --- Summary ---
257
+ if dry_run:
258
+ self.stdout.write(
259
+ self.style.WARNING(
260
+ f"[DRY RUN] Processed {frame_counter} Frames, {annotation_counter} Annotations. "
261
+ "No database changes were committed."
262
+ )
263
+ )
264
+ else:
265
+ self.stdout.write(
266
+ self.style.SUCCESS(
267
+ f"Imported {frame_counter} Frames, {annotation_counter} "
268
+ f"ImageClassificationAnnotations into AIDataSet id={ai_dataset.id}."
269
+ )
270
+ )
271
+
272
+ # ------------------------------------------------------------------
273
+ # Helper methods
274
+ # ------------------------------------------------------------------
275
+
276
+ def _get_existing_labelset(
277
+ self, labelset_name: str, labelset_version: int
278
+ ) -> LabelSet:
279
+ try:
280
+ return LabelSet.objects.get(name=labelset_name, version=labelset_version)
281
+ except LabelSet.DoesNotExist as exc:
282
+ raise CommandError(
283
+ f"LabelSet name='{labelset_name}', version={labelset_version} does not exist. "
284
+ "Create it first (e.g. via fixtures or admin)."
285
+ ) from exc
286
+
287
+ def _get_or_create_label_and_attach_to_labelset(
288
+ self, label_name: str, labelset: LabelSet
289
+ ) -> Label:
290
+ label, _ = Label.objects.get_or_create(name=label_name)
291
+ # Attach to this labelset if missing
292
+ if label not in labelset.labels.all():
293
+ labelset.labels.add(label)
294
+ return label
295
+
296
+ class _noop_context:
297
+ """Simple no-op context manager used for dry-run."""
298
+
299
+ def __enter__(self):
300
+ return None
301
+
302
+ def __exit__(self, exc_type, exc_val, exc_tb):
303
+ return False
@@ -3,7 +3,7 @@
3
3
  import os
4
4
  from django.core.management.base import BaseCommand
5
5
  from endoreg_db.utils import collect_center_names # Import your function here
6
- from pathlib import Path
6
+
7
7
 
8
8
  class Command(BaseCommand):
9
9
  help = "Generate first_names.yaml and last_names.yaml from center data"
@@ -26,7 +26,6 @@ class Command(BaseCommand):
26
26
  pass
27
27
 
28
28
  def handle(self, *args, **options):
29
-
30
29
  # Run the function with the provided arguments
31
30
  try:
32
31
  collect_center_names()
@@ -1,7 +1,3 @@
1
- import os
2
-
3
- import yaml
4
- from django.conf import settings
5
1
  from django.core.management.base import BaseCommand
6
2
 
7
3
  from ...data import REPORT_TYPE_DATA_DIR
@@ -29,15 +25,15 @@ IMPORT_METADATA = {
29
25
  "endoscope_info_line",
30
26
  "examiner_info_line",
31
27
  "cut_off_below_lines",
32
- "cut_off_above_lines"
33
- ], # e.g. ["intervention_types"]
28
+ "cut_off_above_lines",
29
+ ], # e.g. ["intervention_types"]
34
30
  "foreign_key_models": [
35
31
  ReportReaderFlag,
36
32
  ReportReaderFlag,
37
33
  ReportReaderFlag,
38
34
  ReportReaderFlag,
39
- ReportReaderFlag
40
- ] # e.g. [InterventionType]
35
+ ReportReaderFlag,
36
+ ], # e.g. [InterventionType]
41
37
  }
42
38
  }
43
39
 
@@ -1,4 +1,3 @@
1
- from django.conf import settings
2
1
  from django.core.management.base import BaseCommand
3
2
 
4
3
  from ...data import PROFESSION_DATA_DIR
@@ -1,7 +1,3 @@
1
- import os
2
-
3
- import yaml
4
- from django.conf import settings
5
1
  from django.core.management.base import BaseCommand
6
2
 
7
3
  from ...data import REPORT_READER_FLAG_DATA_DIR
@@ -62,10 +62,14 @@ def _validate_requirement_configuration(fields: dict, *, entry: dict, model):
62
62
  return True
63
63
  return any(not item for item in value)
64
64
 
65
- missing = [key for key in ("requirement_types", "operators") if _values_missing(key)]
65
+ missing = [
66
+ key for key in ("requirement_types", "operators") if _values_missing(key)
67
+ ]
66
68
  if missing:
67
69
  missing_display = ", ".join(missing)
68
- raise ValueError(f"{model.__name__} '{name}' is missing required configuration for: {missing_display}.")
70
+ raise ValueError(
71
+ f"{model.__name__} '{name}' is missing required configuration for: {missing_display}."
72
+ )
69
73
 
70
74
 
71
75
  IMPORT_METADATA = {
@@ -1,7 +1,3 @@
1
- import os
2
-
3
- import yaml
4
- from django.conf import settings
5
1
  from django.core.management.base import BaseCommand
6
2
 
7
3
  from ...data import UNIT_DATA_DIR
@@ -1,7 +1,5 @@
1
1
  from django.core.management.base import BaseCommand
2
- from django.contrib.auth.models import Group, Permission
3
- from django.contrib.contenttypes.models import ContentType
4
- from django.apps import apps
2
+ from django.contrib.auth.models import Group
5
3
 
6
4
 
7
5
  class Command(BaseCommand):
@@ -9,13 +7,13 @@ class Command(BaseCommand):
9
7
 
10
8
  def add_arguments(self, parser):
11
9
  parser.add_argument(
12
- '--verbose',
13
- action='store_true',
14
- help='Display verbose output',
10
+ "--verbose",
11
+ action="store_true",
12
+ help="Display verbose output",
15
13
  )
16
14
 
17
15
  def handle(self, *args, **options):
18
- verbose = options['verbose']
16
+ verbose = options["verbose"]
19
17
 
20
18
  # Create groups
21
19
  groups = ["demo", "verified", "agl", "endo_reg_user", "g_play_user", "ukw_user"]
@@ -0,0 +1,169 @@
1
+ # endoreg_db/management/commands/model_input.py
2
+
3
+ from __future__ import annotations
4
+
5
+ from django.core.management.base import BaseCommand, CommandError
6
+
7
+ from endoreg_db.models import AIDataSet
8
+ from endoreg_db.utils.ai.data_loader_for_model_input import (
9
+ build_dataset_for_training,
10
+ )
11
+ from endoreg_db.utils.ai.model_training.config import (
12
+ TrainingConfig,
13
+ )
14
+ from endoreg_db.utils.ai.model_training.trainer_gastronet_multilabel import (
15
+ train_gastronet_multilabel,
16
+ )
17
+
18
+
19
+ class Command(BaseCommand):
20
+ help = (
21
+ "Build the dynamic multi-label dataset from AIDataSet and train a "
22
+ "GastroNet-ResNet50 multi-label model on it.\n"
23
+ "\n"
24
+ "This command:\n"
25
+ "- Uses AIDataSet.id to select annotations.\n"
26
+ "- Infers the LabelSet from used labels.\n"
27
+ "- Builds image_paths, label_vectors, and label_masks from DB.\n"
28
+ "- Prints a short debug dump.\n"
29
+ "- Trains a model using RN50 GastroNet checkpoint (if provided).\n"
30
+ )
31
+
32
+ def add_arguments(self, parser):
33
+ parser.add_argument(
34
+ "--dataset-id",
35
+ type=int,
36
+ required=True,
37
+ help="Primary key of the AIDataSet to use for training.",
38
+ )
39
+ parser.add_argument(
40
+ "--backbone-checkpoint",
41
+ type=str,
42
+ default=None,
43
+ help=(
44
+ "Path to RN50_GastroNet-1M_DINOv1.pth (or similar). "
45
+ "If omitted, ResNet50 is randomly initialized."
46
+ ),
47
+ )
48
+ parser.add_argument(
49
+ "--backbone-name",
50
+ type=str,
51
+ default="gastro_rn50",
52
+ help=(
53
+ "Backbone name, e.g. 'gastro_rn50' (default), "
54
+ "'resnet50_imagenet', 'resnet50_random', 'efficientnet_b0_imagenet', etc."
55
+ ),
56
+ )
57
+ parser.add_argument(
58
+ "--epochs",
59
+ type=int,
60
+ default=10,
61
+ help="Number of training epochs.",
62
+ )
63
+
64
+ def handle(self, *args, **options):
65
+ dataset_id = options["dataset_id"]
66
+ backbone_ckpt = options["backbone_checkpoint"]
67
+ backbone_name = options["backbone_name"]
68
+ num_epochs = options["epochs"]
69
+
70
+ try:
71
+ dataset = AIDataSet.objects.get(id=dataset_id)
72
+ except AIDataSet.DoesNotExist:
73
+ raise CommandError(f"AIDataSet with id={dataset_id} does not exist.")
74
+
75
+ # Basic info
76
+ self.stdout.write(
77
+ self.style.NOTICE(
78
+ f"Using AIDataSet id={dataset.id}, "
79
+ f"name={dataset.name!r}, "
80
+ f"dataset_type={dataset.dataset_type!r}, "
81
+ f"ai_model_type={dataset.ai_model_type!r}"
82
+ )
83
+ )
84
+
85
+ data = build_dataset_for_training(dataset)
86
+
87
+ image_paths = data["image_paths"]
88
+ label_vectors = data["label_vectors"]
89
+ label_masks = data["label_masks"]
90
+ labels = data["labels"]
91
+ labelset = data["labelset"]
92
+
93
+ self.stdout.write(self.style.NOTICE("Inferred LabelSet for this AIDataSet:"))
94
+ self.stdout.write(
95
+ f" LabelSet id={labelset.id}, "
96
+ f"name={labelset.name!r}, "
97
+ f"version={labelset.version}"
98
+ )
99
+ self.stdout.write(" Labels (index, id, name):")
100
+ for idx, lbl in enumerate(labels):
101
+ self.stdout.write(f" [{idx}] id={lbl.id}, name={lbl.name!r}")
102
+
103
+ self.stdout.write(
104
+ self.style.SUCCESS(
105
+ f"\nBuilt training dataset from AIDataSet id={dataset.id}:\n"
106
+ f"- #samples: {len(image_paths)}\n"
107
+ f"- #labels: {len(labels)}"
108
+ )
109
+ )
110
+
111
+ MAX_PRINT = 10
112
+ self.stdout.write(self.style.NOTICE("\nPer-sample debug output (first 10):"))
113
+ for i, (path, vec, mask) in enumerate(
114
+ zip(image_paths, label_vectors, label_masks)
115
+ ):
116
+ if i >= MAX_PRINT:
117
+ self.stdout.write(
118
+ self.style.WARNING(
119
+ f"... ({len(image_paths) - MAX_PRINT} more samples not shown)"
120
+ )
121
+ )
122
+ break
123
+
124
+ self.stdout.write(
125
+ f" Sample {i}:"
126
+ f"\n path = {path!r}"
127
+ f"\n vector (1/0/None) = {vec}"
128
+ f"\n mask (1=use, 0=ignore) = {mask}"
129
+ )
130
+
131
+ self.stdout.write(
132
+ self.style.SUCCESS(
133
+ f"\n Input for model training built successfully from AIDataSet id={dataset.id}."
134
+ )
135
+ )
136
+
137
+ # ------------------------------------------------------------------
138
+ # Ask user if we should really start training
139
+ # ------------------------------------------------------------------
140
+ self.stdout.write("")
141
+ confirm = (
142
+ input(
143
+ "Proceed with model training? "
144
+ "Type 'yes' and press Enter to continue, anything else to abort: "
145
+ )
146
+ .strip()
147
+ .lower()
148
+ )
149
+
150
+ if confirm != "yes":
151
+ self.stdout.write(
152
+ self.style.WARNING("Training aborted by user. No model was trained.")
153
+ )
154
+ return
155
+
156
+ # ---- Training ----
157
+ cfg = TrainingConfig(
158
+ dataset_id=dataset.id,
159
+ backbone_checkpoint=backbone_ckpt,
160
+ backbone_name=backbone_name,
161
+ num_epochs=num_epochs,
162
+ )
163
+ result = train_gastronet_multilabel(cfg)
164
+
165
+ self.stdout.write(
166
+ self.style.SUCCESS(
167
+ f"\nTraining finished. Model saved to: {result['model_path']}"
168
+ )
169
+ )
@@ -7,7 +7,7 @@
7
7
  # "labelset": "multilabel_classification", #labelset name, combination of name and version is unique
8
8
  # "labelset_version": 0,
9
9
  # "weights_path": "weights/multilabel_classification_0.pth", # path to weights file
10
- #}
10
+ # }
11
11
 
12
12
  from django.core.management.base import BaseCommand
13
13
  from django.core.files import File
@@ -15,29 +15,33 @@ from endoreg_db.models import ModelMeta, ModelType, LabelSet
15
15
  import json
16
16
  from pathlib import Path
17
17
 
18
+
18
19
  class Command(BaseCommand):
19
20
  """
20
21
  Registers a new AI model in the database.
21
22
  """
22
- help = 'Registers a new AI model in the database.'
23
+
24
+ help = "Registers a new AI model in the database."
23
25
 
24
26
  def add_arguments(self, parser):
25
- parser.add_argument('model_meta_path', type=str)
27
+ parser.add_argument("model_meta_path", type=str)
26
28
 
27
29
  def handle(self, *args, **options):
28
- model_meta_path = Path(options['model_meta_path'])
30
+ model_meta_path = Path(options["model_meta_path"])
29
31
 
30
- with open(model_meta_path, 'r') as f:
32
+ with open(model_meta_path, "r") as f:
31
33
  model_meta = json.load(f)
32
34
 
33
35
  # get or create model type
34
- model_type = ModelType.objects.get(name=model_meta['model_type'])
36
+ model_type = ModelType.objects.get(name=model_meta["model_type"])
35
37
 
36
38
  # get or create labelset
37
- labelset = LabelSet.objects.get(name=model_meta['labelset'], version=model_meta['labelset_version'])
39
+ labelset = LabelSet.objects.get(
40
+ name=model_meta["labelset"], version=model_meta["labelset_version"]
41
+ )
38
42
 
39
43
  # Handle weights file
40
- weights_path = model_meta['weights_path']
44
+ weights_path = model_meta["weights_path"]
41
45
  # weights path is realative to model_meta_path
42
46
  weights_path = model_meta_path.parent / weights_path
43
47
 
@@ -45,20 +49,22 @@ class Command(BaseCommand):
45
49
 
46
50
  # Make sure the path is correct and the file exists
47
51
  try:
48
- with open(weights_path, 'rb') as file:
52
+ with open(weights_path, "rb") as file:
49
53
  model_name_string = f"{model_meta['name']}_{model_meta['version']}"
50
- weights = File(file, name = model_name_string)
54
+ weights = File(file, name=model_name_string)
51
55
  # Create ModelMeta instance
52
56
  model_meta_instance = ModelMeta.objects.create(
53
- name=model_meta['name'],
54
- version=model_meta['version'],
57
+ name=model_meta["name"],
58
+ version=model_meta["version"],
55
59
  type=model_type,
56
60
  labelset=labelset,
57
61
  weights=weights,
58
- description=model_meta.get('description', '') # Assuming description is optional
62
+ description=model_meta.get(
63
+ "description", ""
64
+ ), # Assuming description is optional
59
65
  )
60
66
  print(f"Successfully registered model {model_meta_instance}")
61
67
  except IOError:
62
- print(f"Failed to open weights file at {weights_path}. Make sure the file exists.")
63
-
64
-
68
+ print(
69
+ f"Failed to open weights file at {weights_path}. Make sure the file exists."
70
+ )