endoreg-db 0.8.4.4__py3-none-any.whl → 0.8.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/authz/auth.py +74 -0
- endoreg_db/authz/backends.py +168 -0
- endoreg_db/authz/management/commands/list_routes.py +18 -0
- endoreg_db/authz/middleware.py +83 -0
- endoreg_db/authz/permissions.py +127 -0
- endoreg_db/authz/policy.py +218 -0
- endoreg_db/authz/views_auth.py +66 -0
- endoreg_db/config/env.py +13 -8
- endoreg_db/data/__init__.py +8 -31
- endoreg_db/data/_examples/disease.yaml +55 -0
- endoreg_db/data/_examples/disease_classification.yaml +13 -0
- endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
- endoreg_db/data/_examples/event.yaml +64 -0
- endoreg_db/data/_examples/examination.yaml +72 -0
- endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
- endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
- endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
- endoreg_db/data/_examples/finding/complication.yaml +16 -0
- endoreg_db/data/_examples/finding/data.yaml +105 -0
- endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
- endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
- endoreg_db/data/_examples/finding/outcome.yaml +12 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
- endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
- endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
- endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
- endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
- endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
- endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
- endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
- endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
- endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/_examples/finding_type/data.yaml +43 -0
- endoreg_db/data/_examples/requirement/age.yaml +26 -0
- endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
- endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
- endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
- endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
- endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
- endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
- endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
- endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
- endoreg_db/data/_examples/requirement/gender.yaml +25 -0
- endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
- endoreg_db/data/_examples/requirement/medication.yaml +93 -0
- endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
- endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
- endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
- endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
- endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
- endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
- endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
- endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
- endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
- endoreg_db/data/requirement_set/_old_ +109 -0
- endoreg_db/data/requirement_set_type/data.yaml +21 -0
- endoreg_db/data/setup_config.yaml +4 -4
- endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
- endoreg_db/exceptions.py +5 -2
- endoreg_db/helpers/data_loader.py +1 -1
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
- endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
- endoreg_db/management/commands/import_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +1 -1
- endoreg_db/management/commands/init_default_ai_model.py +1 -1
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_ai_model_data.py +2 -1
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_requirement_data.py +60 -31
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/setup_endoreg_db.py +14 -10
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/migrations/0001_initial.py +1799 -1300
- endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
- endoreg_db/migrations/_old/0001_initial.py +1857 -0
- endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
- endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
- endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
- endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
- endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
- endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
- endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
- endoreg_db/models/__init__.py +78 -123
- endoreg_db/models/administration/__init__.py +21 -42
- endoreg_db/models/administration/ai/active_model.py +2 -2
- endoreg_db/models/administration/ai/ai_model.py +7 -6
- endoreg_db/models/administration/case/__init__.py +1 -15
- endoreg_db/models/administration/case/case.py +3 -3
- endoreg_db/models/administration/case/case_template/__init__.py +2 -14
- endoreg_db/models/administration/case/case_template/case_template.py +2 -124
- endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
- endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
- endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
- endoreg_db/models/administration/center/center.py +33 -19
- endoreg_db/models/administration/center/center_product.py +12 -9
- endoreg_db/models/administration/center/center_resource.py +25 -19
- endoreg_db/models/administration/center/center_shift.py +21 -17
- endoreg_db/models/administration/center/center_waste.py +16 -8
- endoreg_db/models/administration/person/__init__.py +2 -0
- endoreg_db/models/administration/person/employee/employee.py +10 -5
- endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
- endoreg_db/models/administration/person/employee/employee_type.py +12 -6
- endoreg_db/models/administration/person/examiner/examiner.py +13 -11
- endoreg_db/models/administration/person/patient/__init__.py +2 -0
- endoreg_db/models/administration/person/patient/patient.py +103 -100
- endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
- endoreg_db/models/administration/person/person.py +4 -0
- endoreg_db/models/administration/person/profession/__init__.py +8 -4
- endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
- endoreg_db/models/administration/product/product.py +20 -15
- endoreg_db/models/administration/product/product_material.py +17 -18
- endoreg_db/models/administration/product/product_weight.py +12 -8
- endoreg_db/models/administration/product/reference_product.py +23 -55
- endoreg_db/models/administration/qualification/qualification.py +7 -3
- endoreg_db/models/administration/qualification/qualification_type.py +7 -3
- endoreg_db/models/administration/shift/scheduled_days.py +8 -5
- endoreg_db/models/administration/shift/shift.py +16 -12
- endoreg_db/models/administration/shift/shift_type.py +23 -31
- endoreg_db/models/label/__init__.py +7 -8
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
- endoreg_db/models/label/label.py +15 -15
- endoreg_db/models/label/label_set.py +19 -6
- endoreg_db/models/label/label_type.py +1 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
- endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
- endoreg_db/models/label/video_segmentation_label.py +4 -0
- endoreg_db/models/label/video_segmentation_labelset.py +4 -3
- endoreg_db/models/media/frame/frame.py +22 -22
- endoreg_db/models/media/pdf/raw_pdf.py +249 -177
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +48 -56
- endoreg_db/models/media/video/pipe_1.py +30 -33
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +359 -204
- endoreg_db/models/media/video/video_file_ai.py +288 -74
- endoreg_db/models/media/video/video_file_anonymize.py +38 -38
- endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
- endoreg_db/models/media/video/video_file_io.py +109 -62
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
- endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
- endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
- endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
- endoreg_db/models/media/video/video_file_segments.py +24 -17
- endoreg_db/models/media/video/video_metadata.py +19 -35
- endoreg_db/models/media/video/video_processing.py +96 -95
- endoreg_db/models/medical/contraindication/__init__.py +13 -3
- endoreg_db/models/medical/disease.py +22 -16
- endoreg_db/models/medical/event.py +31 -18
- endoreg_db/models/medical/examination/__init__.py +13 -6
- endoreg_db/models/medical/examination/examination.py +17 -18
- endoreg_db/models/medical/examination/examination_indication.py +26 -25
- endoreg_db/models/medical/examination/examination_time.py +16 -6
- endoreg_db/models/medical/examination/examination_time_type.py +9 -6
- endoreg_db/models/medical/examination/examination_type.py +3 -4
- endoreg_db/models/medical/finding/finding.py +38 -39
- endoreg_db/models/medical/finding/finding_classification.py +37 -48
- endoreg_db/models/medical/finding/finding_intervention.py +27 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +20 -26
- endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
- endoreg_db/models/medical/laboratory/lab_value.py +62 -91
- endoreg_db/models/medical/medication/medication.py +22 -10
- endoreg_db/models/medical/medication/medication_indication.py +29 -3
- endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
- endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
- endoreg_db/models/medical/medication/medication_schedule.py +27 -16
- endoreg_db/models/medical/organ/__init__.py +15 -12
- endoreg_db/models/medical/patient/medication_examples.py +1 -5
- endoreg_db/models/medical/patient/patient_disease.py +20 -23
- endoreg_db/models/medical/patient/patient_event.py +19 -22
- endoreg_db/models/medical/patient/patient_examination.py +48 -54
- endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
- endoreg_db/models/medical/patient/patient_finding.py +122 -139
- endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
- endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
- endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
- endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
- endoreg_db/models/medical/patient/patient_medication.py +27 -38
- endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
- endoreg_db/models/medical/risk/risk.py +7 -6
- endoreg_db/models/medical/risk/risk_type.py +8 -5
- endoreg_db/models/metadata/model_meta.py +60 -29
- endoreg_db/models/metadata/model_meta_logic.py +139 -18
- endoreg_db/models/metadata/pdf_meta.py +19 -24
- endoreg_db/models/metadata/sensitive_meta.py +102 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +383 -43
- endoreg_db/models/metadata/video_meta.py +51 -31
- endoreg_db/models/metadata/video_prediction_logic.py +16 -23
- endoreg_db/models/metadata/video_prediction_meta.py +29 -33
- endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
- endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
- endoreg_db/models/other/emission/emission_factor.py +18 -8
- endoreg_db/models/other/gender.py +10 -5
- endoreg_db/models/other/information_source.py +25 -25
- endoreg_db/models/other/material.py +9 -5
- endoreg_db/models/other/resource.py +6 -4
- endoreg_db/models/other/tag.py +10 -5
- endoreg_db/models/other/transport_route.py +13 -8
- endoreg_db/models/other/unit.py +10 -6
- endoreg_db/models/other/waste.py +6 -5
- endoreg_db/models/requirement/requirement.py +580 -272
- endoreg_db/models/requirement/requirement_error.py +85 -0
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
- endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
- endoreg_db/models/requirement/requirement_operator.py +36 -33
- endoreg_db/models/requirement/requirement_set.py +74 -57
- endoreg_db/models/state/__init__.py +4 -4
- endoreg_db/models/state/abstract.py +2 -2
- endoreg_db/models/state/anonymization.py +12 -0
- endoreg_db/models/state/audit_ledger.py +46 -47
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +40 -46
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +58 -53
- endoreg_db/models/upload_job.py +32 -55
- endoreg_db/models/utils.py +1 -2
- endoreg_db/root_urls.py +21 -2
- endoreg_db/serializers/__init__.py +26 -57
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/meta/report_meta.py +1 -1
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/__init__.py +1 -1
- endoreg_db/serializers/misc/file_overview.py +33 -91
- endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/serializers/video_examination.py +198 -0
- endoreg_db/services/anonymization.py +75 -73
- endoreg_db/services/lookup_service.py +256 -73
- endoreg_db/services/lookup_store.py +174 -30
- endoreg_db/services/pdf_import.py +711 -310
- endoreg_db/services/storage_aware_video_processor.py +140 -114
- endoreg_db/services/video_import.py +266 -117
- endoreg_db/urls/__init__.py +27 -27
- endoreg_db/urls/label_video_segments.py +2 -0
- endoreg_db/urls/media.py +108 -66
- endoreg_db/urls/root_urls.py +29 -0
- endoreg_db/utils/__init__.py +15 -5
- endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
- endoreg_db/utils/case_generator/__init__.py +3 -0
- endoreg_db/utils/dataloader.py +88 -16
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
- endoreg_db/utils/setup_config.py +8 -5
- endoreg_db/utils/storage.py +115 -0
- endoreg_db/utils/validate_endo_roi.py +8 -2
- endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
- endoreg_db/views/__init__.py +5 -12
- endoreg_db/views/anonymization/media_management.py +198 -163
- endoreg_db/views/anonymization/overview.py +4 -1
- endoreg_db/views/anonymization/validate.py +174 -40
- endoreg_db/views/media/__init__.py +2 -0
- endoreg_db/views/media/pdf_media.py +131 -150
- endoreg_db/views/media/sensitive_metadata.py +46 -6
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/media/video_segments.py +187 -260
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/pdf/__init__.py +5 -8
- endoreg_db/views/pdf/pdf_stream.py +186 -0
- endoreg_db/views/pdf/pdf_stream_views.py +0 -127
- endoreg_db/views/pdf/reimport.py +86 -91
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +186 -288
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +0 -4
- endoreg_db/views/video/correction.py +2 -2
- endoreg_db/views/video/video_examination_viewset.py +202 -289
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +350 -255
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/media/video/refactor_plan.md +0 -0
- endoreg_db/models/media/video/video_file_frames.py +0 -0
- endoreg_db/models/metadata/frame_ocr_result.py +0 -0
- endoreg_db/models/rule/__init__.py +0 -13
- endoreg_db/models/rule/rule.py +0 -27
- endoreg_db/models/rule/rule_applicator.py +0 -224
- endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
- endoreg_db/models/rule/rule_type.py +0 -20
- endoreg_db/models/rule/ruleset.py +0 -17
- endoreg_db/serializers/video/video_metadata.py +0 -105
- endoreg_db/urls/report.py +0 -48
- endoreg_db/urls/video.py +0 -61
- endoreg_db/utils/case_generator/case_generator.py +0 -159
- endoreg_db/utils/case_generator/utils.py +0 -30
- endoreg_db/views/pdf/pdf_media.py +0 -239
- endoreg_db/views/report/__init__.py +0 -9
- endoreg_db/views/report/report_list.py +0 -112
- endoreg_db/views/report/report_with_secure_url.py +0 -28
- endoreg_db/views/report/start_examination.py +0 -7
- endoreg_db/views/video/video_media.py +0 -158
- endoreg_db/views.py +0 -0
- /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
- /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
- /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -5,7 +5,7 @@ Django management command to create ModelMeta from Hugging Face model.
|
|
|
5
5
|
from pathlib import Path
|
|
6
6
|
|
|
7
7
|
from django.core.files.base import ContentFile
|
|
8
|
-
from django.core.management.base import BaseCommand
|
|
8
|
+
from django.core.management.base import BaseCommand, CommandError
|
|
9
9
|
from huggingface_hub import hf_hub_download
|
|
10
10
|
|
|
11
11
|
from endoreg_db.models import AiModel, LabelSet, ModelMeta
|
|
@@ -39,12 +39,19 @@ class Command(BaseCommand):
|
|
|
39
39
|
default="1",
|
|
40
40
|
help="Version for the model meta",
|
|
41
41
|
)
|
|
42
|
+
parser.add_argument(
|
|
43
|
+
"--labelset_version",
|
|
44
|
+
type=int,
|
|
45
|
+
default=None,
|
|
46
|
+
help="LabelSet version; if omitted, the latest by name is used",
|
|
47
|
+
)
|
|
42
48
|
|
|
43
49
|
def handle(self, *args, **options):
|
|
44
50
|
model_id = options["model_id"]
|
|
45
51
|
model_name = options["model_name"]
|
|
46
52
|
labelset_name = options["labelset_name"]
|
|
47
53
|
version = options["meta_version"]
|
|
54
|
+
labelset_version = options.get("labelset_version")
|
|
48
55
|
|
|
49
56
|
self.stdout.write(f"Downloading model {model_id} from Hugging Face...")
|
|
50
57
|
|
|
@@ -52,7 +59,7 @@ class Command(BaseCommand):
|
|
|
52
59
|
# Download the model weights
|
|
53
60
|
weights_path = hf_hub_download(
|
|
54
61
|
repo_id=model_id,
|
|
55
|
-
filename="colo_segmentation_RegNetX800MF_base.
|
|
62
|
+
filename="colo_segmentation_RegNetX800MF_base.safetensors",
|
|
56
63
|
local_dir="/tmp",
|
|
57
64
|
)
|
|
58
65
|
self.stdout.write(f"Downloaded weights to: {weights_path}")
|
|
@@ -64,14 +71,17 @@ class Command(BaseCommand):
|
|
|
64
71
|
if created:
|
|
65
72
|
self.stdout.write(f"Created AI model: {ai_model.name}")
|
|
66
73
|
|
|
67
|
-
# Get labelset
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
74
|
+
# Get labelset (optionally by version); fail with non-zero exit
|
|
75
|
+
labelset_qs = LabelSet.objects.filter(name=labelset_name)
|
|
76
|
+
if labelset_version is not None:
|
|
77
|
+
labelset_qs = labelset_qs.filter(version=labelset_version)
|
|
78
|
+
labelset = labelset_qs.order_by("-version").first()
|
|
79
|
+
if labelset is None:
|
|
80
|
+
raise CommandError(
|
|
81
|
+
f"LabelSet '{labelset_name}'"
|
|
82
|
+
+ (f" v{labelset_version}" if labelset_version is not None else "")
|
|
83
|
+
+ " not found"
|
|
73
84
|
)
|
|
74
|
-
return
|
|
75
85
|
|
|
76
86
|
# Create ModelMeta
|
|
77
87
|
model_meta, created = ModelMeta.objects.get_or_create(
|
|
@@ -95,7 +105,7 @@ class Command(BaseCommand):
|
|
|
95
105
|
# Save the weights file to the model
|
|
96
106
|
with open(weights_path, "rb") as f:
|
|
97
107
|
model_meta.weights.save(
|
|
98
|
-
f"{model_name}_v{version}
|
|
108
|
+
f"{model_name}_v{version}.safetensors", ContentFile(f.read())
|
|
99
109
|
)
|
|
100
110
|
|
|
101
111
|
# Set as active meta
|
|
@@ -113,3 +123,4 @@ class Command(BaseCommand):
|
|
|
113
123
|
import traceback
|
|
114
124
|
|
|
115
125
|
traceback.print_exc()
|
|
126
|
+
raise CommandError("ModelMeta creation failed") from e
|
|
@@ -1,214 +1,384 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
Management command for creating ModelMeta entries for multilabel classification models.
|
|
3
|
+
|
|
4
|
+
Supports two workflows:
|
|
5
|
+
1. Registering a local `.safetensors` weights file.
|
|
6
|
+
2. Generating metadata from a YAML template, downloading weights from Hugging Face.
|
|
3
7
|
"""
|
|
4
8
|
|
|
9
|
+
import logging
|
|
10
|
+
import tempfile
|
|
5
11
|
from pathlib import Path
|
|
6
|
-
from
|
|
7
|
-
|
|
12
|
+
from typing import Any, Dict, Iterable, List
|
|
13
|
+
|
|
14
|
+
import yaml
|
|
15
|
+
from django.core.management import BaseCommand, CommandError
|
|
16
|
+
from huggingface_hub import hf_hub_download
|
|
8
17
|
|
|
18
|
+
from endoreg_db.data import AI_MODEL_META_DATA_DIR
|
|
19
|
+
from endoreg_db.models import AiModel, LabelSet, ModelMeta
|
|
9
20
|
|
|
10
|
-
|
|
11
|
-
# python manage.py create_multilabel_model_meta --model_path "./data/models/colo_segmentation_RegNetX800MF_6.ckpt"
|
|
12
|
-
# python manage.py create_multilabel_model_meta --model_path "./libs/endoreg-db/tests/assets/colo_segmentation_RegNetX800MF_6.ckpt"
|
|
21
|
+
logger = logging.getLogger(__name__)
|
|
13
22
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
23
|
+
DEFAULT_MODEL_PATH = (
|
|
24
|
+
Path(__file__).resolve().parents[3]
|
|
25
|
+
/ "tests"
|
|
26
|
+
/ "assets"
|
|
27
|
+
/ "colo_segmentation_RegNetX800MF_6.safetensors"
|
|
28
|
+
)
|
|
18
29
|
|
|
19
30
|
|
|
20
31
|
class Command(BaseCommand):
|
|
21
|
-
help =
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
def add_arguments(self, parser):
|
|
29
|
-
"""
|
|
30
|
-
Adds command-line arguments for configuring model meta creation.
|
|
31
|
-
|
|
32
|
-
This method defines all necessary arguments for specifying model details, label set selection, normalization parameters, image dimensions, axes configuration, batch size, worker count, and versioning options for the Django management command.
|
|
33
|
-
"""
|
|
32
|
+
help = (
|
|
33
|
+
"Create or update ModelMeta entries for multilabel classification models using "
|
|
34
|
+
"either a local safetensor file or a YAML template with Hugging Face download support."
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
def add_arguments(self, parser): # noqa: D401 - inherited docstring is sufficient
|
|
34
38
|
parser.add_argument(
|
|
35
39
|
"--model_name",
|
|
36
40
|
type=str,
|
|
37
41
|
default="image_multilabel_classification_colonoscopy_default",
|
|
38
|
-
help="Name of the
|
|
42
|
+
help="Name of the AiModel to attach metadata to.",
|
|
39
43
|
)
|
|
40
|
-
|
|
41
|
-
# model_path
|
|
42
44
|
parser.add_argument(
|
|
43
45
|
"--model_path",
|
|
44
46
|
type=str,
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
47
|
+
default=str(DEFAULT_MODEL_PATH),
|
|
48
|
+
help=(
|
|
49
|
+
"Path to a local .safetensors weights file. If provided (or left as default) "
|
|
50
|
+
"the command registers the local weights."
|
|
51
|
+
),
|
|
52
|
+
)
|
|
53
|
+
parser.add_argument(
|
|
54
|
+
"--template_path",
|
|
55
|
+
type=str,
|
|
56
|
+
default=None,
|
|
57
|
+
help="Absolute or relative path to a model meta YAML template.",
|
|
58
|
+
)
|
|
59
|
+
parser.add_argument(
|
|
60
|
+
"--template_name",
|
|
61
|
+
type=str,
|
|
62
|
+
default=None,
|
|
63
|
+
help=(
|
|
64
|
+
"Name of a built-in template file in endoreg_db/data/ai_model_meta (without extension)."
|
|
65
|
+
),
|
|
66
|
+
)
|
|
67
|
+
parser.add_argument(
|
|
68
|
+
"--template_entry_name",
|
|
69
|
+
type=str,
|
|
70
|
+
default=None,
|
|
71
|
+
help="Optional entry selector when the template file defines multiple models.",
|
|
48
72
|
)
|
|
49
|
-
|
|
50
|
-
# model_meta_version: int = 1
|
|
51
73
|
parser.add_argument(
|
|
52
74
|
"--model_meta_version",
|
|
53
|
-
type=
|
|
54
|
-
default=
|
|
55
|
-
help=
|
|
75
|
+
type=str,
|
|
76
|
+
default=None,
|
|
77
|
+
help=(
|
|
78
|
+
"Version to assign to the metadata. When omitted the command uses the template value "
|
|
79
|
+
"or defaults to '1' for local registrations."
|
|
80
|
+
),
|
|
56
81
|
)
|
|
57
|
-
|
|
58
82
|
parser.add_argument(
|
|
59
83
|
"--image_classification_labelset_name",
|
|
60
84
|
type=str,
|
|
61
85
|
default="multilabel_classification_colonoscopy_default",
|
|
62
|
-
help="Name of the LabelSet
|
|
86
|
+
help="Name of the LabelSet used by the model.",
|
|
63
87
|
)
|
|
64
|
-
|
|
65
88
|
parser.add_argument(
|
|
66
89
|
"--image_classification_labelset_version",
|
|
67
90
|
type=int,
|
|
68
91
|
default=-1,
|
|
69
|
-
help="
|
|
92
|
+
help="Specific LabelSet version. Use -1 to select the latest available version.",
|
|
70
93
|
)
|
|
71
|
-
|
|
72
|
-
# activation: str = "sigmoid"
|
|
73
94
|
parser.add_argument(
|
|
74
95
|
"--activation_function_name",
|
|
75
96
|
type=str,
|
|
76
97
|
default="sigmoid",
|
|
77
|
-
help="Activation function
|
|
98
|
+
help="Activation function applied to model outputs.",
|
|
78
99
|
)
|
|
79
|
-
|
|
80
|
-
# mean: str = "0.45211223,0.27139644,0.19264949"
|
|
81
100
|
parser.add_argument(
|
|
82
101
|
"--mean",
|
|
83
102
|
type=str,
|
|
84
103
|
default="0.45211223,0.27139644,0.19264949",
|
|
85
|
-
help="
|
|
104
|
+
help="Comma-separated mean values for input normalization.",
|
|
86
105
|
)
|
|
87
|
-
|
|
88
106
|
parser.add_argument(
|
|
89
107
|
"--std",
|
|
90
108
|
type=str,
|
|
91
109
|
default="0.31418097,0.21088019,0.16059452",
|
|
92
|
-
help="
|
|
110
|
+
help="Comma-separated std values for input normalization.",
|
|
93
111
|
)
|
|
94
|
-
|
|
95
|
-
# size_x: int = 716
|
|
96
112
|
parser.add_argument(
|
|
97
113
|
"--size_x",
|
|
98
114
|
type=int,
|
|
99
115
|
default=716,
|
|
100
|
-
help="
|
|
116
|
+
help="Input width expected by the model.",
|
|
101
117
|
)
|
|
102
|
-
|
|
103
|
-
# size_y: int = 716
|
|
104
118
|
parser.add_argument(
|
|
105
119
|
"--size_y",
|
|
106
120
|
type=int,
|
|
107
121
|
default=716,
|
|
108
|
-
help="
|
|
122
|
+
help="Input height expected by the model.",
|
|
109
123
|
)
|
|
110
|
-
|
|
111
124
|
parser.add_argument(
|
|
112
125
|
"--axes",
|
|
113
126
|
type=str,
|
|
114
127
|
default="2,0,1",
|
|
115
|
-
help="
|
|
128
|
+
help="Comma-separated axis order expected by the model (e.g. '2,0,1' for CHW).",
|
|
116
129
|
)
|
|
117
|
-
|
|
118
|
-
# batchsize: int = 16
|
|
119
130
|
parser.add_argument(
|
|
120
131
|
"--batchsize",
|
|
121
132
|
type=int,
|
|
122
133
|
default=16,
|
|
123
|
-
help="
|
|
134
|
+
help="Default batch size for inference.",
|
|
124
135
|
)
|
|
125
|
-
|
|
126
|
-
# num_workers: int = 0
|
|
127
136
|
parser.add_argument(
|
|
128
137
|
"--num_workers",
|
|
129
138
|
type=int,
|
|
130
139
|
default=0,
|
|
131
|
-
help="
|
|
140
|
+
help="Default number of data loading workers.",
|
|
141
|
+
)
|
|
142
|
+
parser.add_argument(
|
|
143
|
+
"--description",
|
|
144
|
+
type=str,
|
|
145
|
+
default="",
|
|
146
|
+
help="Optional description to store on the ModelMeta record.",
|
|
132
147
|
)
|
|
133
|
-
|
|
134
|
-
# bump_version: bool = False
|
|
135
148
|
parser.add_argument(
|
|
136
149
|
"--bump_version",
|
|
137
150
|
action="store_true",
|
|
138
|
-
help="
|
|
151
|
+
help="If the requested version exists, bump to the next available version instead of failing.",
|
|
152
|
+
)
|
|
153
|
+
parser.add_argument(
|
|
154
|
+
"--huggingface_token",
|
|
155
|
+
type=str,
|
|
156
|
+
default=None,
|
|
157
|
+
help="Optional Hugging Face token for private repositories.",
|
|
158
|
+
)
|
|
159
|
+
|
|
160
|
+
def handle(self, *args, **options): # noqa: D401 - inherited docstring is sufficient
|
|
161
|
+
use_template = options.get("template_path") or options.get("template_name")
|
|
162
|
+
|
|
163
|
+
try:
|
|
164
|
+
if use_template:
|
|
165
|
+
model_meta = self._create_from_template(options)
|
|
166
|
+
else:
|
|
167
|
+
model_meta = self._create_from_local_file(options)
|
|
168
|
+
except CommandError:
|
|
169
|
+
raise
|
|
170
|
+
except Exception as exc: # pragma: no cover - defensive logging
|
|
171
|
+
logger.exception("Failed to create ModelMeta", exc_info=exc)
|
|
172
|
+
raise CommandError(str(exc)) from exc
|
|
173
|
+
|
|
174
|
+
self.stdout.write(
|
|
175
|
+
self.style.SUCCESS(f"ModelMeta ready: {model_meta.name} (v{model_meta.version}) for {model_meta.model.name}")
|
|
139
176
|
)
|
|
140
177
|
|
|
141
|
-
def
|
|
142
|
-
""
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
Retrieves and validates the specified LabelSet and AiModel, ensures the model file exists, and invokes ModelMeta.create_from_file with the provided configuration. Raises a ValueError if the requested LabelSet does not exist.
|
|
146
|
-
"""
|
|
178
|
+
def _create_from_local_file(self, options: Dict[str, Any]) -> ModelMeta:
|
|
179
|
+
weights_path = Path(options["model_path"]).expanduser().resolve()
|
|
180
|
+
self._validate_safetensors_path(weights_path)
|
|
181
|
+
|
|
147
182
|
model_name = options["model_name"]
|
|
183
|
+
self._ensure_ai_model_exists(model_name)
|
|
184
|
+
|
|
185
|
+
labelset = self._resolve_labelset(
|
|
186
|
+
options["image_classification_labelset_name"],
|
|
187
|
+
options.get("image_classification_labelset_version"),
|
|
188
|
+
)
|
|
148
189
|
|
|
149
|
-
|
|
150
|
-
activation_function_name = options["activation_function_name"]
|
|
190
|
+
requested_version = options.get("model_meta_version") or "1"
|
|
151
191
|
|
|
152
|
-
|
|
153
|
-
|
|
192
|
+
model_meta = ModelMeta.create_from_file(
|
|
193
|
+
meta_name=model_name,
|
|
194
|
+
model_name=model_name,
|
|
195
|
+
labelset_name=labelset.name,
|
|
196
|
+
labelset_version=labelset.version,
|
|
197
|
+
weights_file=weights_path.as_posix(),
|
|
198
|
+
requested_version=str(requested_version),
|
|
199
|
+
bump_if_exists=options.get("bump_version", False),
|
|
200
|
+
**self._collect_local_kwargs(options),
|
|
201
|
+
)
|
|
154
202
|
|
|
155
|
-
|
|
156
|
-
size_y = options["size_y"]
|
|
203
|
+
return model_meta
|
|
157
204
|
|
|
158
|
-
|
|
205
|
+
def _create_from_template(self, options: Dict[str, Any]) -> ModelMeta:
|
|
206
|
+
template_path = self._resolve_template_path(options)
|
|
207
|
+
entries = self._load_template_entries(template_path)
|
|
208
|
+
entry = self._select_template_entry(entries, options)
|
|
159
209
|
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
210
|
+
fields = entry.get("fields", {})
|
|
211
|
+
if not fields:
|
|
212
|
+
raise CommandError("Template entry is missing a 'fields' section.")
|
|
163
213
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
]
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
)
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
+
meta_name = fields.get("name") or options["model_name"]
|
|
215
|
+
model_name = fields.get("model") or options["model_name"]
|
|
216
|
+
labelset_name = fields.get("labelset") or options["image_classification_labelset_name"]
|
|
217
|
+
labelset_version = fields.get("labelset_version", options.get("image_classification_labelset_version"))
|
|
218
|
+
|
|
219
|
+
self._ensure_ai_model_exists(model_name)
|
|
220
|
+
labelset = self._resolve_labelset(labelset_name, labelset_version)
|
|
221
|
+
|
|
222
|
+
requested_version = options.get("model_meta_version") or fields.get("version")
|
|
223
|
+
if not requested_version:
|
|
224
|
+
raise CommandError("Provide --model_meta_version or include a 'version' in the template entry.")
|
|
225
|
+
|
|
226
|
+
hf_config = entry.get("setup_config", {}).get("huggingface_fallback", {})
|
|
227
|
+
repo_id = hf_config.get("repo_id")
|
|
228
|
+
filename = hf_config.get("filename")
|
|
229
|
+
|
|
230
|
+
if not repo_id or not filename:
|
|
231
|
+
raise CommandError(
|
|
232
|
+
"Template entry must define setup_config.huggingface_fallback.repo_id and filename for weight download."
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
if not filename.endswith(".safetensors"):
|
|
236
|
+
raise CommandError("Only .safetensors files are supported when downloading from Hugging Face.")
|
|
237
|
+
|
|
238
|
+
token = options.get("huggingface_token")
|
|
239
|
+
|
|
240
|
+
with tempfile.TemporaryDirectory(prefix="hf-multilabel-") as download_dir:
|
|
241
|
+
download_kwargs = {
|
|
242
|
+
"repo_id": repo_id,
|
|
243
|
+
"filename": filename,
|
|
244
|
+
"local_dir": download_dir,
|
|
245
|
+
"local_dir_use_symlinks": False,
|
|
246
|
+
}
|
|
247
|
+
if token:
|
|
248
|
+
download_kwargs["token"] = token
|
|
249
|
+
|
|
250
|
+
weights_path = Path(hf_hub_download(**download_kwargs)).resolve()
|
|
251
|
+
|
|
252
|
+
self._validate_safetensors_path(weights_path)
|
|
253
|
+
|
|
254
|
+
model_meta = ModelMeta.create_from_file(
|
|
255
|
+
meta_name=meta_name,
|
|
256
|
+
model_name=model_name,
|
|
257
|
+
labelset_name=labelset.name,
|
|
258
|
+
labelset_version=labelset.version,
|
|
259
|
+
weights_file=weights_path.as_posix(),
|
|
260
|
+
requested_version=str(requested_version),
|
|
261
|
+
bump_if_exists=options.get("bump_version", False),
|
|
262
|
+
**self._collect_template_kwargs(fields, options),
|
|
263
|
+
)
|
|
264
|
+
|
|
265
|
+
return model_meta
|
|
266
|
+
|
|
267
|
+
def _resolve_template_path(self, options: Dict[str, Any]) -> Path:
|
|
268
|
+
template_path = options.get("template_path")
|
|
269
|
+
template_name = options.get("template_name")
|
|
270
|
+
|
|
271
|
+
if template_path:
|
|
272
|
+
resolved = Path(template_path).expanduser().resolve()
|
|
273
|
+
elif template_name:
|
|
274
|
+
resolved = (AI_MODEL_META_DATA_DIR / f"{template_name}.yaml").resolve()
|
|
275
|
+
else: # pragma: no cover - guarded by caller
|
|
276
|
+
raise CommandError("Template mode requires --template_path or --template_name.")
|
|
277
|
+
|
|
278
|
+
if not resolved.exists():
|
|
279
|
+
raise CommandError(f"Template file not found: {resolved}")
|
|
280
|
+
|
|
281
|
+
return resolved
|
|
282
|
+
|
|
283
|
+
@staticmethod
|
|
284
|
+
def _load_template_entries(template_path: Path) -> List[Dict[str, Any]]:
|
|
285
|
+
with template_path.open("r", encoding="utf-8") as handle:
|
|
286
|
+
data = yaml.safe_load(handle) or []
|
|
287
|
+
|
|
288
|
+
if isinstance(data, dict):
|
|
289
|
+
return [data]
|
|
290
|
+
if isinstance(data, list):
|
|
291
|
+
return [entry for entry in data if isinstance(entry, dict)]
|
|
292
|
+
|
|
293
|
+
raise CommandError(f"Template {template_path} must define a mapping or list of mappings.")
|
|
294
|
+
|
|
295
|
+
def _select_template_entry(self, entries: Iterable[Dict[str, Any]], options: Dict[str, Any]) -> Dict[str, Any]:
|
|
296
|
+
target = options.get("template_entry_name") or options.get("model_name")
|
|
297
|
+
|
|
298
|
+
for entry in entries:
|
|
299
|
+
fields = entry.get("fields", {})
|
|
300
|
+
if not fields:
|
|
301
|
+
continue
|
|
302
|
+
if target and (fields.get("name") == target or fields.get("model") == target):
|
|
303
|
+
return entry
|
|
304
|
+
|
|
305
|
+
entries = list(entries)
|
|
306
|
+
if len(entries) == 1:
|
|
307
|
+
return entries[0]
|
|
308
|
+
|
|
309
|
+
raise CommandError(
|
|
310
|
+
"Unable to determine which template entry to use. Specify --template_entry_name to disambiguate."
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
def _collect_local_kwargs(self, options: Dict[str, Any]) -> Dict[str, Any]:
|
|
314
|
+
return self._filter_none(
|
|
315
|
+
{
|
|
316
|
+
"activation": options.get("activation_function_name"),
|
|
317
|
+
"mean": options.get("mean"),
|
|
318
|
+
"std": options.get("std"),
|
|
319
|
+
"size_x": options.get("size_x"),
|
|
320
|
+
"size_y": options.get("size_y"),
|
|
321
|
+
"axes": options.get("axes"),
|
|
322
|
+
"batchsize": options.get("batchsize"),
|
|
323
|
+
"num_workers": options.get("num_workers"),
|
|
324
|
+
"description": options.get("description"),
|
|
325
|
+
}
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
def _collect_template_kwargs(self, fields: Dict[str, Any], options: Dict[str, Any]) -> Dict[str, Any]:
|
|
329
|
+
def numeric(value):
|
|
330
|
+
return int(value) if value is not None else value
|
|
331
|
+
|
|
332
|
+
return self._filter_none(
|
|
333
|
+
{
|
|
334
|
+
"activation": fields.get("activation") or options.get("activation_function_name"),
|
|
335
|
+
"mean": self._normalise_sequence(fields.get("mean")) or options.get("mean"),
|
|
336
|
+
"std": self._normalise_sequence(fields.get("std")) or options.get("std"),
|
|
337
|
+
"size_x": numeric(fields.get("size_x")) if fields.get("size_x") is not None else options.get("size_x"),
|
|
338
|
+
"size_y": numeric(fields.get("size_y")) if fields.get("size_y") is not None else options.get("size_y"),
|
|
339
|
+
"axes": fields.get("axes") or options.get("axes"),
|
|
340
|
+
"batchsize": numeric(fields.get("batchsize")) if fields.get("batchsize") is not None else options.get("batchsize"),
|
|
341
|
+
"num_workers": numeric(fields.get("num_workers")) if fields.get("num_workers") is not None else options.get("num_workers"),
|
|
342
|
+
"description": fields.get("description") or options.get("description"),
|
|
343
|
+
}
|
|
214
344
|
)
|
|
345
|
+
|
|
346
|
+
@staticmethod
|
|
347
|
+
def _normalise_sequence(value: Any) -> str | None:
|
|
348
|
+
if value is None:
|
|
349
|
+
return None
|
|
350
|
+
if isinstance(value, str):
|
|
351
|
+
return value
|
|
352
|
+
if isinstance(value, (list, tuple)):
|
|
353
|
+
return ",".join(str(item) for item in value)
|
|
354
|
+
return str(value)
|
|
355
|
+
|
|
356
|
+
@staticmethod
|
|
357
|
+
def _filter_none(payload: Dict[str, Any]) -> Dict[str, Any]:
|
|
358
|
+
return {key: value for key, value in payload.items() if value not in (None, "")}
|
|
359
|
+
|
|
360
|
+
@staticmethod
|
|
361
|
+
def _validate_safetensors_path(path: Path) -> None:
|
|
362
|
+
if path.suffix != ".safetensors":
|
|
363
|
+
raise CommandError(f"Expected a .safetensors file, got: {path}")
|
|
364
|
+
if not path.exists():
|
|
365
|
+
raise CommandError(f"Weights file not found: {path}")
|
|
366
|
+
|
|
367
|
+
@staticmethod
|
|
368
|
+
def _ensure_ai_model_exists(model_name: str) -> None:
|
|
369
|
+
if not AiModel.objects.filter(name=model_name).exists():
|
|
370
|
+
raise CommandError(f"AiModel not found: {model_name}. Load ai model data before running this command.")
|
|
371
|
+
|
|
372
|
+
@staticmethod
|
|
373
|
+
def _resolve_labelset(name: str, version: Any) -> LabelSet:
|
|
374
|
+
queryset = LabelSet.objects.filter(name=name)
|
|
375
|
+
|
|
376
|
+
if version in (None, -1):
|
|
377
|
+
labelset = queryset.order_by("-version").first()
|
|
378
|
+
else:
|
|
379
|
+
labelset = queryset.filter(version=version).first()
|
|
380
|
+
|
|
381
|
+
if not labelset:
|
|
382
|
+
raise CommandError(f"LabelSet not found for name='{name}' and version='{version}'.")
|
|
383
|
+
|
|
384
|
+
return labelset
|
|
@@ -87,7 +87,7 @@ class Command(BaseCommand):
|
|
|
87
87
|
help="Display verbose output",
|
|
88
88
|
)
|
|
89
89
|
parser.add_argument(
|
|
90
|
-
"--
|
|
90
|
+
"--",
|
|
91
91
|
type=str,
|
|
92
92
|
default="university_hospital_wuerzburg",
|
|
93
93
|
help="Name of the center to associate with video",
|
|
@@ -188,7 +188,6 @@ class Command(BaseCommand):
|
|
|
188
188
|
center_name = options["center_name"]
|
|
189
189
|
video_file = options["video_file"]
|
|
190
190
|
frame_dir_root = options["frame_dir_root"]
|
|
191
|
-
video_dir_root = options["video_dir_root"]
|
|
192
191
|
delete_source = options["delete_source"]
|
|
193
192
|
save_video_file = options["save_video_file"]
|
|
194
193
|
model_name = options["model_name"]
|
|
@@ -205,7 +204,7 @@ class Command(BaseCommand):
|
|
|
205
204
|
# Assert Center exists -> Does not exist methods are deprecated
|
|
206
205
|
try:
|
|
207
206
|
center = Center.objects.get(name=center_name)
|
|
208
|
-
self.stdout.write(self.style.SUCCESS(f"Using center: {center.
|
|
207
|
+
self.stdout.write(self.style.SUCCESS(f"Using center: {center.name}"))
|
|
209
208
|
except Center.DoesNotExist:
|
|
210
209
|
self.stdout.write(self.style.ERROR(f"Center not found: {center_name}"))
|
|
211
210
|
return
|
|
@@ -295,7 +294,7 @@ class Command(BaseCommand):
|
|
|
295
294
|
# Updated to handle new return signature (path, metadata)
|
|
296
295
|
cleaned_video_path, extracted_metadata = frame_cleaner.clean_video(
|
|
297
296
|
video_path=Path(video_file_obj.raw_file.path),
|
|
298
|
-
|
|
297
|
+
endoscope_image_roi=video_file_obj.processor.get_roi_endoscope_image() if video_file_obj.processor else None,
|
|
299
298
|
endoscope_data_roi_nested=video_file_obj.processor.get_rois() if video_file_obj.processor else None,
|
|
300
299
|
output_path=video_file_obj.get_processed_file_path(),
|
|
301
300
|
technique="mask_overlay" # Use mask overlay technique as default, if not set this will be inferred.
|
|
@@ -336,11 +335,11 @@ class Command(BaseCommand):
|
|
|
336
335
|
|
|
337
336
|
# Create default SensitiveMeta with placeholder data
|
|
338
337
|
default_data = {
|
|
339
|
-
"patient_first_name":
|
|
340
|
-
"patient_last_name":
|
|
341
|
-
"patient_dob":
|
|
342
|
-
"examination_date":
|
|
343
|
-
"center_name": video_file.center.name if video_file.center else "
|
|
338
|
+
"patient_first_name": None,
|
|
339
|
+
"patient_last_name": None,
|
|
340
|
+
"patient_dob": None,
|
|
341
|
+
"examination_date": None,
|
|
342
|
+
"center_name": video_file.center.name if video_file.center else "unknown",
|
|
344
343
|
}
|
|
345
344
|
|
|
346
345
|
try:
|
|
@@ -420,4 +419,4 @@ class Command(BaseCommand):
|
|
|
420
419
|
self.stderr.write("❌ Please enter a number in the list above.\n")
|
|
421
420
|
continue
|
|
422
421
|
|
|
423
|
-
return processors[index] # ← the chosen object
|
|
422
|
+
return processors[index] # ← the chosen object
|
|
@@ -99,7 +99,7 @@ class Command(BaseCommand):
|
|
|
99
99
|
parser.add_argument(
|
|
100
100
|
"--model_path",
|
|
101
101
|
type=str,
|
|
102
|
-
default="./data/models/colo_segmentation_RegNetX800MF_6.
|
|
102
|
+
default="./data/models/colo_segmentation_RegNetX800MF_6.safetensors",
|
|
103
103
|
help="Path to the model file",
|
|
104
104
|
)
|
|
105
105
|
|