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.
- endoreg_db/admin.py +10 -5
- endoreg_db/apps.py +4 -7
- endoreg_db/authz/auth.py +1 -0
- endoreg_db/authz/backends.py +1 -1
- endoreg_db/authz/management/commands/list_routes.py +2 -0
- endoreg_db/authz/middleware.py +8 -7
- endoreg_db/authz/permissions.py +21 -10
- endoreg_db/authz/policy.py +14 -19
- endoreg_db/authz/views_auth.py +14 -10
- endoreg_db/codemods/rename_datetime_fields.py +8 -1
- endoreg_db/exceptions.py +5 -2
- endoreg_db/forms/__init__.py +0 -1
- endoreg_db/forms/examination_form.py +4 -3
- endoreg_db/forms/patient_finding_intervention_form.py +30 -8
- endoreg_db/forms/patient_form.py +9 -13
- endoreg_db/forms/questionnaires/__init__.py +1 -1
- endoreg_db/forms/settings/__init__.py +4 -1
- endoreg_db/forms/unit.py +2 -1
- endoreg_db/helpers/count_db.py +17 -14
- endoreg_db/helpers/default_objects.py +2 -1
- endoreg_db/helpers/download_segmentation_model.py +4 -3
- endoreg_db/helpers/interact.py +0 -5
- endoreg_db/helpers/test_video_helper.py +33 -25
- endoreg_db/import_files/__init__.py +1 -1
- endoreg_db/import_files/context/__init__.py +1 -1
- endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
- endoreg_db/import_files/context/ensure_center.py +4 -4
- endoreg_db/import_files/context/file_lock.py +3 -3
- endoreg_db/import_files/context/import_context.py +11 -12
- endoreg_db/import_files/context/validate_directories.py +1 -0
- endoreg_db/import_files/file_storage/create_report_file.py +57 -34
- endoreg_db/import_files/file_storage/create_video_file.py +64 -35
- endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
- endoreg_db/import_files/file_storage/state_management.py +146 -83
- endoreg_db/import_files/file_storage/storage.py +5 -1
- endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
- endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
- endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
- endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
- endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
- endoreg_db/import_files/report_import_service.py +36 -30
- endoreg_db/import_files/video_import_service.py +27 -23
- endoreg_db/logger_conf.py +56 -40
- endoreg_db/management/__init__.py +1 -1
- endoreg_db/management/commands/__init__.py +1 -1
- endoreg_db/management/commands/check_auth.py +45 -38
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
- endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
- endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
- endoreg_db/management/commands/fix_video_paths.py +75 -54
- endoreg_db/management/commands/import_report.py +1 -3
- endoreg_db/management/commands/list_routes.py +2 -0
- endoreg_db/management/commands/load_ai_model_data.py +8 -2
- endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
- endoreg_db/management/commands/load_center_data.py +3 -3
- endoreg_db/management/commands/load_distribution_data.py +35 -38
- endoreg_db/management/commands/load_endoscope_data.py +0 -3
- endoreg_db/management/commands/load_examination_data.py +20 -4
- endoreg_db/management/commands/load_finding_data.py +18 -3
- endoreg_db/management/commands/load_gender_data.py +17 -24
- endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
- endoreg_db/management/commands/load_information_source.py +0 -3
- endoreg_db/management/commands/load_lab_value_data.py +14 -3
- endoreg_db/management/commands/load_legacy_data.py +303 -0
- endoreg_db/management/commands/load_name_data.py +1 -2
- endoreg_db/management/commands/load_pdf_type_data.py +4 -8
- endoreg_db/management/commands/load_profession_data.py +0 -1
- endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
- endoreg_db/management/commands/load_requirement_data.py +6 -2
- endoreg_db/management/commands/load_unit_data.py +0 -4
- endoreg_db/management/commands/load_user_groups.py +5 -7
- endoreg_db/management/commands/model_input.py +169 -0
- endoreg_db/management/commands/register_ai_model.py +22 -16
- endoreg_db/management/commands/setup_endoreg_db.py +110 -32
- endoreg_db/management/commands/storage_management.py +14 -8
- endoreg_db/management/commands/summarize_db_content.py +154 -63
- endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
- endoreg_db/management/commands/validate_video_files.py +82 -50
- endoreg_db/management/commands/video_validation.py +4 -6
- endoreg_db/migrations/0001_initial.py +112 -63
- endoreg_db/migrations/__init__.py +0 -0
- endoreg_db/models/__init__.py +8 -0
- endoreg_db/models/administration/ai/active_model.py +5 -5
- endoreg_db/models/administration/ai/ai_model.py +41 -18
- endoreg_db/models/administration/ai/model_type.py +1 -0
- endoreg_db/models/administration/case/case.py +22 -22
- endoreg_db/models/administration/center/__init__.py +5 -5
- endoreg_db/models/administration/center/center.py +6 -2
- endoreg_db/models/administration/center/center_resource.py +18 -4
- endoreg_db/models/administration/center/center_shift.py +3 -1
- endoreg_db/models/administration/center/center_waste.py +6 -2
- endoreg_db/models/administration/person/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/employee_type.py +3 -1
- endoreg_db/models/administration/person/examiner/__init__.py +1 -1
- endoreg_db/models/administration/person/examiner/examiner.py +10 -2
- endoreg_db/models/administration/person/names/first_name.py +6 -4
- endoreg_db/models/administration/person/names/last_name.py +4 -3
- endoreg_db/models/administration/person/patient/__init__.py +1 -1
- endoreg_db/models/administration/person/patient/patient.py +0 -1
- endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
- endoreg_db/models/administration/person/person.py +1 -1
- endoreg_db/models/administration/product/__init__.py +7 -6
- endoreg_db/models/administration/product/product.py +6 -2
- endoreg_db/models/administration/product/product_group.py +9 -7
- endoreg_db/models/administration/product/product_material.py +9 -2
- endoreg_db/models/administration/product/reference_product.py +64 -15
- endoreg_db/models/administration/qualification/qualification.py +3 -1
- endoreg_db/models/administration/shift/shift.py +3 -1
- endoreg_db/models/administration/shift/shift_type.py +12 -4
- endoreg_db/models/aidataset/__init__.py +5 -0
- endoreg_db/models/aidataset/aidataset.py +193 -0
- endoreg_db/models/label/__init__.py +1 -1
- endoreg_db/models/label/label.py +10 -2
- endoreg_db/models/label/label_set.py +3 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
- endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
- endoreg_db/models/media/__init__.py +12 -5
- endoreg_db/models/media/frame/__init__.py +1 -1
- endoreg_db/models/media/frame/frame.py +34 -8
- endoreg_db/models/media/pdf/__init__.py +2 -1
- endoreg_db/models/media/pdf/raw_pdf.py +11 -4
- endoreg_db/models/media/pdf/report_file.py +6 -2
- endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
- endoreg_db/models/media/video/create_from_file.py +20 -41
- endoreg_db/models/media/video/pipe_1.py +75 -30
- endoreg_db/models/media/video/pipe_2.py +37 -12
- endoreg_db/models/media/video/video_file.py +36 -24
- endoreg_db/models/media/video/video_file_ai.py +235 -70
- endoreg_db/models/media/video/video_file_anonymize.py +240 -65
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
- endoreg_db/models/media/video/video_file_io.py +85 -33
- endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
- endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
- endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
- endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
- endoreg_db/models/media/video/video_file_segments.py +118 -27
- endoreg_db/models/media/video/video_metadata.py +25 -6
- endoreg_db/models/media/video/video_processing.py +54 -15
- endoreg_db/models/medical/__init__.py +3 -13
- endoreg_db/models/medical/contraindication/__init__.py +3 -1
- endoreg_db/models/medical/disease.py +18 -6
- endoreg_db/models/medical/event.py +6 -2
- endoreg_db/models/medical/examination/__init__.py +5 -1
- endoreg_db/models/medical/examination/examination.py +22 -6
- endoreg_db/models/medical/examination/examination_indication.py +23 -7
- endoreg_db/models/medical/examination/examination_time.py +6 -2
- endoreg_db/models/medical/finding/__init__.py +3 -1
- endoreg_db/models/medical/finding/finding.py +37 -12
- endoreg_db/models/medical/finding/finding_classification.py +27 -8
- endoreg_db/models/medical/finding/finding_intervention.py +19 -6
- endoreg_db/models/medical/finding/finding_type.py +3 -1
- endoreg_db/models/medical/hardware/__init__.py +1 -1
- endoreg_db/models/medical/hardware/endoscope.py +14 -2
- endoreg_db/models/medical/laboratory/__init__.py +1 -1
- endoreg_db/models/medical/laboratory/lab_value.py +139 -39
- endoreg_db/models/medical/medication/__init__.py +7 -3
- endoreg_db/models/medical/medication/medication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
- endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
- endoreg_db/models/medical/medication/medication_schedule.py +3 -1
- endoreg_db/models/medical/patient/__init__.py +2 -10
- endoreg_db/models/medical/patient/medication_examples.py +3 -14
- endoreg_db/models/medical/patient/patient_disease.py +17 -5
- endoreg_db/models/medical/patient/patient_event.py +12 -4
- endoreg_db/models/medical/patient/patient_examination.py +52 -15
- endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
- endoreg_db/models/medical/patient/patient_finding.py +105 -29
- endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
- endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
- endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
- endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
- endoreg_db/models/medical/patient/patient_medication.py +25 -7
- endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
- endoreg_db/models/metadata/model_meta.py +40 -12
- endoreg_db/models/metadata/model_meta_logic.py +51 -16
- endoreg_db/models/metadata/sensitive_meta.py +65 -28
- endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
- endoreg_db/models/metadata/video_meta.py +146 -39
- endoreg_db/models/metadata/video_prediction_logic.py +70 -21
- endoreg_db/models/metadata/video_prediction_meta.py +80 -27
- endoreg_db/models/operation_log.py +63 -0
- endoreg_db/models/other/__init__.py +10 -10
- endoreg_db/models/other/distribution/__init__.py +9 -7
- endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
- endoreg_db/models/other/emission/__init__.py +1 -1
- endoreg_db/models/other/emission/emission_factor.py +9 -3
- endoreg_db/models/other/information_source.py +15 -5
- endoreg_db/models/other/material.py +3 -1
- endoreg_db/models/other/transport_route.py +3 -1
- endoreg_db/models/other/unit.py +6 -2
- endoreg_db/models/report/report.py +0 -1
- endoreg_db/models/requirement/requirement.py +84 -27
- endoreg_db/models/requirement/requirement_error.py +5 -6
- endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
- endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
- endoreg_db/models/requirement/requirement_operator.py +28 -8
- endoreg_db/models/requirement/requirement_set.py +34 -11
- endoreg_db/models/state/__init__.py +1 -0
- endoreg_db/models/state/audit_ledger.py +9 -2
- endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
- endoreg_db/models/state/processing_history/processing_history.py +136 -0
- endoreg_db/models/state/raw_pdf.py +0 -1
- endoreg_db/models/state/video.py +2 -3
- endoreg_db/models/utils.py +4 -2
- endoreg_db/queries/__init__.py +2 -6
- endoreg_db/queries/annotations/__init__.py +1 -3
- endoreg_db/queries/annotations/legacy.py +37 -26
- endoreg_db/root_urls.py +3 -4
- endoreg_db/schemas/examination_evaluation.py +3 -0
- endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
- endoreg_db/serializers/__init__.py +2 -8
- endoreg_db/serializers/administration/__init__.py +1 -2
- endoreg_db/serializers/administration/ai/__init__.py +0 -1
- endoreg_db/serializers/administration/ai/active_model.py +3 -1
- endoreg_db/serializers/administration/ai/ai_model.py +5 -3
- endoreg_db/serializers/administration/ai/model_type.py +3 -1
- endoreg_db/serializers/administration/center.py +7 -2
- endoreg_db/serializers/administration/gender.py +4 -2
- endoreg_db/serializers/anonymization.py +13 -13
- endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
- endoreg_db/serializers/examination/__init__.py +1 -1
- endoreg_db/serializers/examination/base.py +12 -13
- endoreg_db/serializers/examination/dropdown.py +6 -7
- endoreg_db/serializers/examination_serializer.py +3 -6
- endoreg_db/serializers/finding/__init__.py +1 -1
- endoreg_db/serializers/finding/finding.py +14 -7
- endoreg_db/serializers/finding_classification/__init__.py +3 -3
- endoreg_db/serializers/finding_classification/choice.py +3 -3
- endoreg_db/serializers/finding_classification/classification.py +2 -4
- endoreg_db/serializers/label_video_segment/__init__.py +5 -3
- endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
- endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
- endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
- endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
- endoreg_db/serializers/meta/__init__.py +1 -2
- endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
- endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
- endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
- endoreg_db/serializers/misc/__init__.py +2 -2
- endoreg_db/serializers/misc/file_overview.py +11 -7
- endoreg_db/serializers/misc/stats.py +10 -8
- endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
- endoreg_db/serializers/misc/upload_job.py +32 -29
- endoreg_db/serializers/patient/__init__.py +2 -1
- endoreg_db/serializers/patient/patient.py +32 -15
- endoreg_db/serializers/patient/patient_dropdown.py +11 -3
- endoreg_db/serializers/patient_examination/__init__.py +1 -1
- endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
- endoreg_db/serializers/patient_finding/__init__.py +1 -1
- endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
- endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
- endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
- endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
- endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
- endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
- endoreg_db/serializers/pdf/__init__.py +1 -3
- endoreg_db/serializers/requirements/requirement_schema.py +1 -6
- endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
- endoreg_db/serializers/video/__init__.py +2 -2
- endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
- endoreg_db/serializers/video/video_file_brief.py +6 -2
- endoreg_db/serializers/video/video_file_detail.py +36 -23
- endoreg_db/serializers/video/video_file_list.py +4 -2
- endoreg_db/serializers/video/video_processing_history.py +54 -50
- endoreg_db/services/__init__.py +1 -1
- endoreg_db/services/anonymization.py +2 -2
- endoreg_db/services/examination_evaluation.py +40 -17
- endoreg_db/services/model_meta_from_hf.py +76 -0
- endoreg_db/services/polling_coordinator.py +101 -70
- endoreg_db/services/pseudonym_service.py +27 -22
- endoreg_db/services/report_import.py +6 -3
- endoreg_db/services/segment_sync.py +75 -59
- endoreg_db/services/video_import.py +6 -7
- endoreg_db/urls/__init__.py +2 -2
- endoreg_db/urls/ai.py +7 -25
- endoreg_db/urls/anonymization.py +61 -15
- endoreg_db/urls/auth.py +4 -4
- endoreg_db/urls/classification.py +4 -9
- endoreg_db/urls/examination.py +27 -18
- endoreg_db/urls/media.py +27 -34
- endoreg_db/urls/patient.py +11 -7
- endoreg_db/urls/requirements.py +3 -1
- endoreg_db/urls/root_urls.py +2 -3
- endoreg_db/urls/stats.py +24 -16
- endoreg_db/urls/upload.py +3 -11
- endoreg_db/utils/__init__.py +14 -15
- endoreg_db/utils/ai/__init__.py +1 -1
- endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
- endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
- endoreg_db/utils/ai/get.py +2 -1
- endoreg_db/utils/ai/inference_dataset.py +14 -15
- endoreg_db/utils/ai/model_training/config.py +117 -0
- endoreg_db/utils/ai/model_training/dataset.py +74 -0
- endoreg_db/utils/ai/model_training/losses.py +68 -0
- endoreg_db/utils/ai/model_training/metrics.py +78 -0
- endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
- endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
- endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
- endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
- endoreg_db/utils/ai/predict.py +4 -4
- endoreg_db/utils/ai/preprocess.py +19 -11
- endoreg_db/utils/calc_duration_seconds.py +4 -4
- endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
- endoreg_db/utils/check_video_files.py +74 -47
- endoreg_db/utils/cropping.py +10 -9
- endoreg_db/utils/dataloader.py +11 -3
- endoreg_db/utils/dates.py +3 -4
- endoreg_db/utils/defaults/set_default_center.py +7 -6
- endoreg_db/utils/env.py +6 -2
- endoreg_db/utils/extract_specific_frames.py +24 -9
- endoreg_db/utils/file_operations.py +30 -18
- endoreg_db/utils/fix_video_path_direct.py +57 -41
- endoreg_db/utils/frame_anonymization_utils.py +157 -157
- endoreg_db/utils/hashs.py +3 -18
- endoreg_db/utils/links/requirement_link.py +96 -52
- endoreg_db/utils/ocr.py +30 -25
- endoreg_db/utils/operation_log.py +61 -0
- endoreg_db/utils/parse_and_generate_yaml.py +12 -13
- endoreg_db/utils/paths.py +6 -6
- endoreg_db/utils/permissions.py +40 -24
- endoreg_db/utils/pipelines/process_video_dir.py +50 -26
- endoreg_db/utils/product/sum_emissions.py +5 -3
- endoreg_db/utils/product/sum_weights.py +4 -2
- endoreg_db/utils/pydantic_models/__init__.py +3 -4
- endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
- endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
- endoreg_db/utils/setup_config.py +21 -5
- endoreg_db/utils/storage.py +3 -1
- endoreg_db/utils/translation.py +19 -15
- endoreg_db/utils/uuid.py +1 -0
- endoreg_db/utils/validate_endo_roi.py +12 -4
- endoreg_db/utils/validate_subcategory_dict.py +26 -24
- endoreg_db/utils/validate_video_detailed.py +207 -149
- endoreg_db/utils/video/__init__.py +7 -3
- endoreg_db/utils/video/extract_frames.py +30 -18
- endoreg_db/utils/video/ffmpeg_wrapper.py +217 -52
- endoreg_db/utils/video/names.py +11 -6
- endoreg_db/utils/video/streaming_processor.py +175 -101
- endoreg_db/utils/video/video_splitter.py +30 -19
- endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
- endoreg_db/views/__init__.py +0 -20
- endoreg_db/views/anonymization/__init__.py +6 -2
- endoreg_db/views/anonymization/media_management.py +2 -6
- endoreg_db/views/anonymization/overview.py +34 -1
- endoreg_db/views/anonymization/validate.py +79 -18
- endoreg_db/views/auth/__init__.py +1 -1
- endoreg_db/views/auth/keycloak.py +16 -14
- endoreg_db/views/examination/__init__.py +12 -15
- endoreg_db/views/examination/examination.py +5 -5
- endoreg_db/views/examination/examination_manifest_cache.py +5 -5
- endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
- endoreg_db/views/examination/get_finding_classifications.py +9 -7
- endoreg_db/views/examination/get_findings.py +8 -10
- endoreg_db/views/examination/get_instruments.py +3 -2
- endoreg_db/views/examination/get_interventions.py +1 -1
- endoreg_db/views/finding/__init__.py +2 -2
- endoreg_db/views/finding/finding.py +58 -54
- endoreg_db/views/finding/get_classifications.py +1 -1
- endoreg_db/views/finding/get_interventions.py +1 -1
- endoreg_db/views/finding_classification/__init__.py +5 -5
- endoreg_db/views/finding_classification/finding_classification.py +5 -6
- endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
- endoreg_db/views/media/__init__.py +13 -13
- endoreg_db/views/media/pdf_media.py +9 -9
- endoreg_db/views/media/sensitive_metadata.py +10 -7
- endoreg_db/views/media/video_media.py +4 -4
- endoreg_db/views/meta/__init__.py +1 -1
- endoreg_db/views/meta/sensitive_meta_list.py +20 -22
- endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
- endoreg_db/views/misc/__init__.py +6 -34
- endoreg_db/views/misc/center.py +2 -1
- endoreg_db/views/misc/csrf.py +2 -1
- endoreg_db/views/misc/gender.py +2 -1
- endoreg_db/views/misc/stats.py +141 -106
- endoreg_db/views/patient/__init__.py +1 -3
- endoreg_db/views/patient/patient.py +141 -99
- endoreg_db/views/patient_examination/__init__.py +5 -5
- endoreg_db/views/patient_examination/patient_examination.py +43 -42
- endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
- endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
- endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
- endoreg_db/views/patient_examination/video.py +114 -80
- endoreg_db/views/patient_finding/__init__.py +1 -1
- endoreg_db/views/patient_finding/patient_finding.py +17 -10
- endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
- endoreg_db/views/patient_finding_classification/__init__.py +1 -1
- endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
- endoreg_db/views/report/reimport.py +1 -1
- endoreg_db/views/report/report_stream.py +5 -8
- endoreg_db/views/requirement/__init__.py +2 -1
- endoreg_db/views/requirement/evaluate.py +7 -9
- endoreg_db/views/requirement/lookup.py +2 -3
- endoreg_db/views/requirement/lookup_store.py +0 -1
- endoreg_db/views/requirement/requirement_utils.py +2 -4
- endoreg_db/views/stats/__init__.py +4 -4
- endoreg_db/views/stats/stats_views.py +152 -115
- endoreg_db/views/video/__init__.py +18 -27
- endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
- endoreg_db/views/{ai → video/ai}/label.py +20 -16
- endoreg_db/views/video/correction.py +5 -6
- endoreg_db/views/video/reimport.py +134 -99
- endoreg_db/views/video/segments_crud.py +134 -44
- endoreg_db/views/video/video_apply_mask.py +13 -12
- endoreg_db/views/video/video_correction.py +2 -1
- endoreg_db/views/video/video_download_processed.py +15 -15
- endoreg_db/views/video/video_meta_stats.py +7 -6
- endoreg_db/views/video/video_processing_history.py +3 -2
- endoreg_db/views/video/video_remove_frames.py +13 -12
- endoreg_db/views/video/video_stream.py +110 -82
- {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
- {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +436 -433
- endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +0 -119
- endoreg_db/management/commands/import_fallback_video.py +0 -203
- endoreg_db/management/commands/import_video.py +0 -422
- endoreg_db/management/commands/import_video_with_classification.py +0 -367
- endoreg_db/models/media/processing_history/processing_history.py +0 -96
- endoreg_db/serializers/label/__init__.py +0 -7
- endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
- endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
- endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
- endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
- endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
- endoreg_db/services/__old/pdf_import.py +0 -1487
- endoreg_db/services/__old/video_import.py +0 -1306
- endoreg_db/tasks/upload_tasks.py +0 -216
- endoreg_db/tasks/video_ingest.py +0 -161
- endoreg_db/tasks/video_processing_tasks.py +0 -327
- endoreg_db/views/misc/translation.py +0 -182
- {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
- {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
|
-
|
|
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
|
-
],
|
|
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
|
-
]
|
|
35
|
+
ReportReaderFlag,
|
|
36
|
+
], # e.g. [InterventionType]
|
|
41
37
|
}
|
|
42
38
|
}
|
|
43
39
|
|
|
@@ -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 = [
|
|
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(
|
|
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,5 @@
|
|
|
1
1
|
from django.core.management.base import BaseCommand
|
|
2
|
-
from django.contrib.auth.models import Group
|
|
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
|
-
|
|
13
|
-
action=
|
|
14
|
-
help=
|
|
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[
|
|
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
|
-
|
|
23
|
+
|
|
24
|
+
help = "Registers a new AI model in the database."
|
|
23
25
|
|
|
24
26
|
def add_arguments(self, parser):
|
|
25
|
-
parser.add_argument(
|
|
27
|
+
parser.add_argument("model_meta_path", type=str)
|
|
26
28
|
|
|
27
29
|
def handle(self, *args, **options):
|
|
28
|
-
model_meta_path = Path(options[
|
|
30
|
+
model_meta_path = Path(options["model_meta_path"])
|
|
29
31
|
|
|
30
|
-
with open(model_meta_path,
|
|
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[
|
|
36
|
+
model_type = ModelType.objects.get(name=model_meta["model_type"])
|
|
35
37
|
|
|
36
38
|
# get or create labelset
|
|
37
|
-
labelset = LabelSet.objects.get(
|
|
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[
|
|
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,
|
|
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
|
|
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[
|
|
54
|
-
version=model_meta[
|
|
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(
|
|
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(
|
|
63
|
-
|
|
64
|
-
|
|
68
|
+
print(
|
|
69
|
+
f"Failed to open weights file at {weights_path}. Make sure the file exists."
|
|
70
|
+
)
|