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
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Dict, Any, Optional
|
|
2
|
-
from django.core.exceptions import ObjectDoesNotExist
|
|
3
|
-
from rest_framework import serializers
|
|
4
|
-
from endoreg_db.models import (
|
|
5
|
-
Label,
|
|
6
|
-
VideoFile,
|
|
7
|
-
LabelVideoSegment
|
|
8
|
-
)
|
|
9
|
-
|
|
10
|
-
import logging
|
|
11
|
-
logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
13
|
-
if TYPE_CHECKING:
|
|
14
|
-
from endoreg_db.serializers import LabelVideoSegmentSerializer
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
def _validate_fps(video: "VideoFile"):
|
|
18
|
-
"""Raises a ValidationError if the video's FPS is invalid."""
|
|
19
|
-
fps = video.get_fps()
|
|
20
|
-
if not fps or fps <= 0:
|
|
21
|
-
raise serializers.ValidationError("The video must have a valid FPS to convert times to frames.")
|
|
22
|
-
return fps
|
|
23
|
-
|
|
24
|
-
def _convert_time_to_frame(time_value: float, fps: float) -> int:
|
|
25
|
-
"""Converts a time value in seconds to a frame number."""
|
|
26
|
-
return int(time_value * fps)
|
|
27
|
-
|
|
28
|
-
def _update_frames_from_data(instance: "LabelVideoSegment", validated_data: Dict[str, Any], fps: Optional[float]):
|
|
29
|
-
"""Updates start and end frames from either time or frame data."""
|
|
30
|
-
if 'start_time' in validated_data:
|
|
31
|
-
if fps is None:
|
|
32
|
-
fps = _validate_fps(instance.video)
|
|
33
|
-
instance.start_frame_number = _convert_time_to_frame(validated_data['start_time'], fps)
|
|
34
|
-
elif 'start_frame' in validated_data:
|
|
35
|
-
instance.start_frame_number = validated_data['start_frame']
|
|
36
|
-
|
|
37
|
-
if 'end_time' in validated_data:
|
|
38
|
-
if fps is None:
|
|
39
|
-
fps = _validate_fps(instance.video)
|
|
40
|
-
instance.end_frame_number = _convert_time_to_frame(validated_data['end_time'], fps)
|
|
41
|
-
elif 'end_frame' in validated_data:
|
|
42
|
-
instance.end_frame_number = validated_data['end_frame']
|
|
43
|
-
|
|
44
|
-
def _update(instance: "LabelVideoSegment", validated_data: Dict[str, Any]) -> "LabelVideoSegment":
|
|
45
|
-
"""
|
|
46
|
-
Handles the update logic for a LabelVideoSegment instance.
|
|
47
|
-
This function is designed to be used within a serializer's update method.
|
|
48
|
-
"""
|
|
49
|
-
video: Optional["VideoFile"] = validated_data.get('video')
|
|
50
|
-
fps = None
|
|
51
|
-
|
|
52
|
-
if video and video != instance.video:
|
|
53
|
-
instance.video = video
|
|
54
|
-
fps = _validate_fps(video) # Validate FPS for the new video
|
|
55
|
-
|
|
56
|
-
if 'label' in validated_data:
|
|
57
|
-
instance.label = validated_data['label']
|
|
58
|
-
|
|
59
|
-
_update_frames_from_data(instance, validated_data, fps)
|
|
60
|
-
|
|
61
|
-
# Final validation of frame numbers
|
|
62
|
-
if instance.start_frame_number >= instance.end_frame_number:
|
|
63
|
-
raise serializers.ValidationError("start_frame must be less than end_frame.")
|
|
64
|
-
|
|
65
|
-
if instance.video and instance.end_frame_number > instance.video.frame_count:
|
|
66
|
-
raise serializers.ValidationError("end_frame cannot exceed the total frames of the video.")
|
|
67
|
-
|
|
68
|
-
instance.save()
|
|
69
|
-
return instance
|
|
70
|
-
|
|
71
|
-
def _update(self:"LabelVideoSegmentSerializer", instance, validated_data):
|
|
72
|
-
"""
|
|
73
|
-
Update a LabelVideoSegment instance with new validated data.
|
|
74
|
-
|
|
75
|
-
This method updates the associated video file, label (by ID or name), and segment boundaries (start and end times converted to frame numbers based on the video's FPS). If the referenced video or label does not exist, or if any error occurs during the update, a validation error is raised.
|
|
76
|
-
|
|
77
|
-
Returns:
|
|
78
|
-
The updated LabelVideoSegment instance.
|
|
79
|
-
"""
|
|
80
|
-
try:
|
|
81
|
-
# Handle time-based updates
|
|
82
|
-
start_time = validated_data.pop('start_time', None)
|
|
83
|
-
end_time = validated_data.pop('end_time', None)
|
|
84
|
-
video_id = validated_data.pop('video_id', None)
|
|
85
|
-
label_id = validated_data.pop('label_id', None)
|
|
86
|
-
label_name = validated_data.pop('label_name', None)
|
|
87
|
-
|
|
88
|
-
# Update video if provided
|
|
89
|
-
if video_id and (not instance.video_file or video_id != instance.video_file.id):
|
|
90
|
-
try:
|
|
91
|
-
instance.video_file = VideoFile.objects.get(id=video_id)
|
|
92
|
-
except ObjectDoesNotExist as exc:
|
|
93
|
-
raise serializers.ValidationError(f"VideoFile with id {video_id} does not exist") from exc
|
|
94
|
-
|
|
95
|
-
# Update label if provided
|
|
96
|
-
if label_id is not None:
|
|
97
|
-
if label_id:
|
|
98
|
-
try:
|
|
99
|
-
instance.label = Label.objects.get(id=label_id)
|
|
100
|
-
except ObjectDoesNotExist as exc:
|
|
101
|
-
raise serializers.ValidationError(f"Label with id {label_id} does not exist") from exc
|
|
102
|
-
else:
|
|
103
|
-
instance.label = None
|
|
104
|
-
elif label_name is not None:
|
|
105
|
-
if label_name:
|
|
106
|
-
instance.label = self.get_or_create_label_from_name(label_name)
|
|
107
|
-
else:
|
|
108
|
-
instance.label = None
|
|
109
|
-
|
|
110
|
-
def _get_valid_fps(video_file):
|
|
111
|
-
"""
|
|
112
|
-
Extract and validate the frames-per-second (FPS) value from a video file object.
|
|
113
|
-
|
|
114
|
-
Returns:
|
|
115
|
-
float: The FPS value as a positive float. Defaults to 30.0 if missing, invalid, or non-positive.
|
|
116
|
-
"""
|
|
117
|
-
|
|
118
|
-
# Convert time to frame numbers if provided
|
|
119
|
-
if start_time is not None:
|
|
120
|
-
fps = _validate_fps(instance.video_file)
|
|
121
|
-
instance.start_frame_number = _convert_time_to_frame(start_time, fps)
|
|
122
|
-
|
|
123
|
-
if end_time is not None:
|
|
124
|
-
fps = _validate_fps(instance.video_file)
|
|
125
|
-
instance.end_frame_number = _convert_time_to_frame(end_time, fps)
|
|
126
|
-
|
|
127
|
-
# Update other fields
|
|
128
|
-
for attr, value in validated_data.items():
|
|
129
|
-
setattr(instance, attr, value)
|
|
130
|
-
|
|
131
|
-
instance.save()
|
|
132
|
-
logger.info("Updated video segment: %s", instance.pk)
|
|
133
|
-
return instance
|
|
134
|
-
|
|
135
|
-
except Exception as e:
|
|
136
|
-
logger.error("Error updating video segment %s: %s", instance.pk, str(e))
|
|
137
|
-
raise serializers.ValidationError(f"Failed to update segment: {str(e)}")
|
|
138
|
-
|
|
@@ -1,149 +0,0 @@
|
|
|
1
|
-
import logging
|
|
2
|
-
from rest_framework import serializers
|
|
3
|
-
from typing import TYPE_CHECKING
|
|
4
|
-
if TYPE_CHECKING:
|
|
5
|
-
from endoreg_db.serializers import LabelVideoSegmentSerializer
|
|
6
|
-
|
|
7
|
-
logger = logging.getLogger(__name__)
|
|
8
|
-
|
|
9
|
-
def _validate(self:"LabelVideoSegmentSerializer", attrs):
|
|
10
|
-
"""
|
|
11
|
-
Validate video segment data, ensuring the presence and logical consistency of either time-based or frame-based segment information.
|
|
12
|
-
|
|
13
|
-
Raises:
|
|
14
|
-
ValidationError: If neither valid time nor frame data is provided, or if time/frame constraints are violated.
|
|
15
|
-
|
|
16
|
-
Returns:
|
|
17
|
-
dict: The validated attributes for the video segment.
|
|
18
|
-
"""
|
|
19
|
-
logger.debug("Validation started")
|
|
20
|
-
video_id, label_id = self.extract_and_validate_basic_attrs(attrs)
|
|
21
|
-
instance = getattr(self, 'instance', None)
|
|
22
|
-
start_time, end_time = self.process_time_data(attrs, instance)
|
|
23
|
-
start_frame_number, end_frame_number = self.process_frame_data(attrs, instance)
|
|
24
|
-
has_time_data, has_frame_data = self.validate_time_and_frame_presence(start_time, end_time, start_frame_number, end_frame_number)
|
|
25
|
-
label_id = attrs.get('label_id')
|
|
26
|
-
label_name = attrs.get('label_name')
|
|
27
|
-
if not label_id and not label_name:
|
|
28
|
-
logger.info("Creating segment without label")
|
|
29
|
-
if has_time_data:
|
|
30
|
-
self.validate_time_constraints(start_time, end_time)
|
|
31
|
-
if has_frame_data:
|
|
32
|
-
self.validate_frame_constraints(start_frame_number, end_frame_number)
|
|
33
|
-
logger.debug(f"Attributes after validation: {attrs}")
|
|
34
|
-
return attrs
|
|
35
|
-
|
|
36
|
-
def _extract_and_validate_basic_attrs(self:"LabelVideoSegmentSerializer", attrs):
|
|
37
|
-
"""
|
|
38
|
-
Extract and return the video and label identifiers from the provided attributes or initial data.
|
|
39
|
-
|
|
40
|
-
Returns:
|
|
41
|
-
Tuple containing the resolved video ID and label ID.
|
|
42
|
-
"""
|
|
43
|
-
video_id = attrs.get('video_id') or self.initial_data.get('video_id') or self.initial_data.get('video_file')
|
|
44
|
-
label_id = attrs.get('label_id') or self.initial_data.get('label_id') or self.initial_data.get('label')
|
|
45
|
-
logger.debug(f"Validating video segment: video_id={video_id}, label_id={label_id}, attrs={{k: v for k, v in attrs.items() if k not in ['video_file', 'label']}}")
|
|
46
|
-
return video_id, label_id
|
|
47
|
-
|
|
48
|
-
def _process_time_data(self:"LabelVideoSegmentSerializer", attrs, instance):
|
|
49
|
-
"""
|
|
50
|
-
Extract and convert start and end time values from input data, ensuring they are floats or None.
|
|
51
|
-
|
|
52
|
-
Parameters:
|
|
53
|
-
attrs (dict): Input attributes containing potential time values.
|
|
54
|
-
instance: Existing object instance to use as a fallback for time values.
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
tuple: A pair (start_time, end_time), each as a float or None if missing or invalid.
|
|
58
|
-
"""
|
|
59
|
-
start_time = attrs.get('start_time', None)
|
|
60
|
-
end_time = attrs.get('end_time', None)
|
|
61
|
-
if instance is not None:
|
|
62
|
-
if start_time is None:
|
|
63
|
-
start_time = getattr(instance, 'start_time', None)
|
|
64
|
-
if end_time is None:
|
|
65
|
-
end_time = getattr(instance, 'end_time', None)
|
|
66
|
-
# Always convert start_time/end_time to float if present
|
|
67
|
-
if 'start_time' in self.initial_data:
|
|
68
|
-
try:
|
|
69
|
-
attrs['start_time'] = float(self.initial_data['start_time'])
|
|
70
|
-
except (TypeError, ValueError):
|
|
71
|
-
attrs['start_time'] = None
|
|
72
|
-
start_time = attrs['start_time']
|
|
73
|
-
elif start_time is not None:
|
|
74
|
-
try:
|
|
75
|
-
start_time = float(start_time)
|
|
76
|
-
attrs['start_time'] = start_time
|
|
77
|
-
except (TypeError, ValueError):
|
|
78
|
-
start_time = None
|
|
79
|
-
attrs['start_time'] = None
|
|
80
|
-
if 'end_time' in self.initial_data:
|
|
81
|
-
try:
|
|
82
|
-
attrs['end_time'] = float(self.initial_data['end_time'])
|
|
83
|
-
except (TypeError, ValueError):
|
|
84
|
-
attrs['end_time'] = None
|
|
85
|
-
end_time = attrs['end_time']
|
|
86
|
-
elif end_time is not None:
|
|
87
|
-
try:
|
|
88
|
-
end_time = float(end_time)
|
|
89
|
-
attrs['end_time'] = end_time
|
|
90
|
-
except (TypeError, ValueError):
|
|
91
|
-
end_time = None
|
|
92
|
-
attrs['end_time'] = None
|
|
93
|
-
return start_time, end_time
|
|
94
|
-
|
|
95
|
-
def _process_frame_data(self:"LabelVideoSegmentSerializer", attrs, instance):
|
|
96
|
-
"""
|
|
97
|
-
Retrieve start and end frame numbers from input attributes or an existing instance.
|
|
98
|
-
|
|
99
|
-
Returns:
|
|
100
|
-
tuple: A pair containing the start and end frame numbers, or None if not available.
|
|
101
|
-
"""
|
|
102
|
-
start_frame_number = attrs.get('start_frame_number')
|
|
103
|
-
end_frame_number = attrs.get('end_frame_number')
|
|
104
|
-
if instance is not None:
|
|
105
|
-
if start_frame_number is None:
|
|
106
|
-
start_frame_number = getattr(instance, 'start_frame_number', None)
|
|
107
|
-
if end_frame_number is None:
|
|
108
|
-
end_frame_number = getattr(instance, 'end_frame_number', None)
|
|
109
|
-
return start_frame_number, end_frame_number
|
|
110
|
-
|
|
111
|
-
def _validate_time_and_frame_presence(self:"LabelVideoSegmentSerializer", start_time, end_time, start_frame_number, end_frame_number):
|
|
112
|
-
"""
|
|
113
|
-
Check that either both time values or both frame number values are provided for a video segment.
|
|
114
|
-
|
|
115
|
-
Raises a ValidationError if neither a complete time range nor a complete frame range is present.
|
|
116
|
-
|
|
117
|
-
Returns:
|
|
118
|
-
has_time_data (bool): True if both start_time and end_time are provided.
|
|
119
|
-
has_frame_data (bool): True if both start_frame_number and end_frame_number are provided.
|
|
120
|
-
"""
|
|
121
|
-
has_time_data = start_time is not None and end_time is not None
|
|
122
|
-
has_frame_data = start_frame_number is not None and end_frame_number is not None
|
|
123
|
-
if not has_time_data and not has_frame_data:
|
|
124
|
-
raise serializers.ValidationError(
|
|
125
|
-
"Either start_time/end_time or start_frame_number/end_frame_number must be provided"
|
|
126
|
-
)
|
|
127
|
-
return has_time_data, has_frame_data
|
|
128
|
-
|
|
129
|
-
def _validate_time_constraints(self:"LabelVideoSegmentSerializer", start_time, end_time):
|
|
130
|
-
"""
|
|
131
|
-
Validate that the start and end times for a video segment are logically consistent.
|
|
132
|
-
|
|
133
|
-
Raises a ValidationError if start_time is negative or if end_time is not strictly greater than start_time.
|
|
134
|
-
"""
|
|
135
|
-
if start_time < 0:
|
|
136
|
-
raise serializers.ValidationError("start_time must be non-negative")
|
|
137
|
-
if end_time <= start_time:
|
|
138
|
-
raise serializers.ValidationError("end_time must be greater than start_time")
|
|
139
|
-
|
|
140
|
-
def _validate_frame_constraints(self:"LabelVideoSegmentSerializer", start_frame_number, end_frame_number):
|
|
141
|
-
"""
|
|
142
|
-
Validate that frame number constraints are satisfied for a video segment.
|
|
143
|
-
|
|
144
|
-
Raises a ValidationError if `start_frame_number` is negative or if `end_frame_number` is not greater than `start_frame_number`.
|
|
145
|
-
"""
|
|
146
|
-
if start_frame_number < 0:
|
|
147
|
-
raise serializers.ValidationError("start_frame_number must be non-negative")
|
|
148
|
-
if end_frame_number <= start_frame_number:
|
|
149
|
-
raise serializers.ValidationError("end_frame_number must be greater than start_frame_number")
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
from rest_framework import serializers
|
|
2
|
-
import logging
|
|
3
|
-
|
|
4
|
-
logger = logging.getLogger(__name__)
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
class LabelVideoSegmentAnnotationSerializer(serializers.Serializer):
|
|
8
|
-
"""
|
|
9
|
-
Serializer for annotation data.
|
|
10
|
-
Handles segment annotations with metadata including segmentId.
|
|
11
|
-
"""
|
|
12
|
-
id = serializers.IntegerField(read_only=True)
|
|
13
|
-
videoId = serializers.IntegerField(required=True)
|
|
14
|
-
startTime = serializers.FloatField(required=False)
|
|
15
|
-
endTime = serializers.FloatField(required=False)
|
|
16
|
-
type = serializers.CharField(max_length=50, required=True)
|
|
17
|
-
text = serializers.CharField(max_length=1000, required=False, allow_blank=True)
|
|
18
|
-
tags = serializers.ListField(
|
|
19
|
-
child=serializers.CharField(max_length=100),
|
|
20
|
-
required=False,
|
|
21
|
-
default=list
|
|
22
|
-
)
|
|
23
|
-
userId = serializers.CharField(max_length=100, required=False)
|
|
24
|
-
isPublic = serializers.BooleanField(default=False)
|
|
25
|
-
confidence = serializers.FloatField(required=False, min_value=0.0, max_value=1.0)
|
|
26
|
-
metadata = serializers.DictField(required=False, default=dict)
|
|
27
|
-
|
|
28
|
-
def validate(self, data):
|
|
29
|
-
"""
|
|
30
|
-
Validates annotation data, enforcing required fields and value constraints for segment annotations.
|
|
31
|
-
|
|
32
|
-
For annotations of type 'segment', ensures both `startTime` and `endTime` are present and that `startTime` is less than `endTime`. If `metadata.segmentId` is provided, verifies it is a valid integer.
|
|
33
|
-
|
|
34
|
-
Raises:
|
|
35
|
-
serializers.ValidationError: If validation fails.
|
|
36
|
-
|
|
37
|
-
Returns:
|
|
38
|
-
dict: The validated annotation data.
|
|
39
|
-
"""
|
|
40
|
-
annotation_type = data.get('type')
|
|
41
|
-
|
|
42
|
-
if annotation_type == 'segment':
|
|
43
|
-
if 'startTime' not in data or 'endTime' not in data:
|
|
44
|
-
raise serializers.ValidationError(
|
|
45
|
-
"Segment annotations must include startTime and endTime"
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
start_time = data.get('startTime')
|
|
49
|
-
end_time = data.get('endTime')
|
|
50
|
-
|
|
51
|
-
if start_time >= end_time:
|
|
52
|
-
raise serializers.ValidationError(
|
|
53
|
-
"startTime must be less than endTime"
|
|
54
|
-
)
|
|
55
|
-
|
|
56
|
-
# Validate metadata contains segmentId if provided
|
|
57
|
-
metadata = data.get('metadata', {})
|
|
58
|
-
segment_id = metadata.get('segmentId')
|
|
59
|
-
if segment_id is not None:
|
|
60
|
-
try:
|
|
61
|
-
int(segment_id)
|
|
62
|
-
except (ValueError, TypeError):
|
|
63
|
-
raise serializers.ValidationError(
|
|
64
|
-
"metadata.segmentId must be a valid integer"
|
|
65
|
-
)
|
|
66
|
-
|
|
67
|
-
return data
|
|
68
|
-
|
|
69
|
-
#@maxhild we need to verify if this is the right place for these methods, if so we should implement them here
|
|
70
|
-
def create(self, validated_data):
|
|
71
|
-
"""
|
|
72
|
-
Assigns a unique ID to the annotation data and returns the updated data dictionary.
|
|
73
|
-
|
|
74
|
-
In this simulated implementation, the method mimics creating a new annotation by incrementing an internal counter for the ID. No data is persisted to a database or external storage.
|
|
75
|
-
|
|
76
|
-
Returns:
|
|
77
|
-
dict: The annotation data with an assigned unique ID.
|
|
78
|
-
"""
|
|
79
|
-
# Simulate creating an annotation with an ID
|
|
80
|
-
validated_data['id'] = getattr(self, '_next_id', 1)
|
|
81
|
-
self._next_id = validated_data['id'] + 1
|
|
82
|
-
return validated_data
|
|
83
|
-
|
|
84
|
-
def update(self, instance, validated_data):
|
|
85
|
-
"""
|
|
86
|
-
Update annotation data structure.
|
|
87
|
-
"""
|
|
88
|
-
for attr, value in validated_data.items():
|
|
89
|
-
instance[attr] = value
|
|
90
|
-
return instance
|
|
91
|
-
|
|
92
|
-
def save(self, **kwargs):
|
|
93
|
-
"""
|
|
94
|
-
Save the annotation.
|
|
95
|
-
"""
|
|
96
|
-
if self.instance:
|
|
97
|
-
return self.update(self.instance, self.validated_data)
|
|
98
|
-
else:
|
|
99
|
-
return self.create(self.validated_data)
|
|
@@ -1,163 +0,0 @@
|
|
|
1
|
-
from django.db import transaction
|
|
2
|
-
from rest_framework import serializers
|
|
3
|
-
|
|
4
|
-
from endoreg_db.models import LabelVideoSegment, VideoPredictionMeta
|
|
5
|
-
from endoreg_db.serializers.label_video_segment import LabelVideoSegmentSerializer
|
|
6
|
-
|
|
7
|
-
import logging
|
|
8
|
-
|
|
9
|
-
logger = logging.getLogger(__name__)
|
|
10
|
-
class LabelSegmentUpdateSerializer(serializers.Serializer):
|
|
11
|
-
"""
|
|
12
|
-
Serializer for updating label segments.
|
|
13
|
-
|
|
14
|
-
- Ensures that the segments stored in the database match exactly with what is sent from the frontend.
|
|
15
|
-
- Updates existing segments if their `start_frame_number` matches but `end_frame_number` has changed.
|
|
16
|
-
- Inserts new segments if they are not already present in the database.
|
|
17
|
-
- Deletes extra segments from the database if they are no longer in the frontend data.
|
|
18
|
-
"""
|
|
19
|
-
|
|
20
|
-
video_id = serializers.IntegerField()
|
|
21
|
-
label_id = serializers.IntegerField()
|
|
22
|
-
segments = serializers.ListField(
|
|
23
|
-
child=serializers.DictField(
|
|
24
|
-
child=serializers.FloatField() # Ensure we handle float values
|
|
25
|
-
)
|
|
26
|
-
)
|
|
27
|
-
|
|
28
|
-
def validate(self, data):
|
|
29
|
-
"""
|
|
30
|
-
Validate that the input data contains a non-empty list of segments with valid frame numbers.
|
|
31
|
-
|
|
32
|
-
Raises a validation error if any segment is missing required fields or if a segment's start frame exceeds its end frame.
|
|
33
|
-
"""
|
|
34
|
-
if not data.get("segments"):
|
|
35
|
-
raise serializers.ValidationError("No segments provided.")
|
|
36
|
-
|
|
37
|
-
for segment in data["segments"]:
|
|
38
|
-
if "start_frame_number" not in segment or "end_frame_number" not in segment:
|
|
39
|
-
raise serializers.ValidationError(
|
|
40
|
-
"Each segment must have `start_frame_number` and `end_frame_number`."
|
|
41
|
-
)
|
|
42
|
-
|
|
43
|
-
if segment["start_frame_number"] > segment["end_frame_number"]:
|
|
44
|
-
raise serializers.ValidationError(
|
|
45
|
-
"Start frame must be less than or equal to end frame."
|
|
46
|
-
)
|
|
47
|
-
|
|
48
|
-
return data
|
|
49
|
-
|
|
50
|
-
def save(self):
|
|
51
|
-
"""
|
|
52
|
-
Synchronizes label segments in the database to match the provided frontend data for a specific video and label.
|
|
53
|
-
|
|
54
|
-
Compares incoming segments to existing database entries, updating segments with changed end frames, creating new segments as needed, and deleting segments that are no longer present. All changes are performed within a transaction to ensure consistency. Raises a validation error if no prediction metadata exists for the specified video.
|
|
55
|
-
|
|
56
|
-
Returns:
|
|
57
|
-
dict: A dictionary containing serialized updated segments, newly created segments, and the count of deleted segments.
|
|
58
|
-
"""
|
|
59
|
-
|
|
60
|
-
video_id = self.validated_data["video_id"]
|
|
61
|
-
label_id = self.validated_data["label_id"]
|
|
62
|
-
new_segments = self.validated_data["segments"] # Remove new_keys assignment
|
|
63
|
-
|
|
64
|
-
# Fetch the correct `prediction_meta_id` based on `video_id`
|
|
65
|
-
prediction_meta_entry = VideoPredictionMeta.objects.filter(
|
|
66
|
-
video_file_id=video_id # Changed from video_id to video_file_id
|
|
67
|
-
).first()
|
|
68
|
-
if not prediction_meta_entry:
|
|
69
|
-
raise serializers.ValidationError(
|
|
70
|
-
{"error": "No prediction metadata found for this video."}
|
|
71
|
-
)
|
|
72
|
-
|
|
73
|
-
prediction_meta_id = (
|
|
74
|
-
prediction_meta_entry.id
|
|
75
|
-
) # Get the correct prediction_meta_id
|
|
76
|
-
|
|
77
|
-
existing_segments = LabelVideoSegment.objects.filter(
|
|
78
|
-
video_file_id=video_id, label_id=label_id # FIXED: video_file_id instead of video_id
|
|
79
|
-
)
|
|
80
|
-
|
|
81
|
-
# Convert existing segments into a dictionary for quick lookup
|
|
82
|
-
# Key format: (start_frame_number, end_frame_number)
|
|
83
|
-
existing_segments_dict = {
|
|
84
|
-
(float(seg.start_frame_number), float(seg.end_frame_number)): seg
|
|
85
|
-
for seg in existing_segments
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
# Prepare lists for batch processing
|
|
89
|
-
# Initialize sets to track updates and new entries
|
|
90
|
-
updated_segments = set()
|
|
91
|
-
new_entries = []
|
|
92
|
-
existing_keys = set()
|
|
93
|
-
new_keys = set()
|
|
94
|
-
|
|
95
|
-
# Iterate through the validated data to update or create label video segments
|
|
96
|
-
print(f" Before Update: Found {existing_segments.count()} existing segments.")
|
|
97
|
-
logger.debug(f"Before Update: Found %d existing segments.", existing_segments.count())
|
|
98
|
-
logger.debug(f"New Segments Received: %d", len(new_segments))
|
|
99
|
-
logger.debug(f"Using prediction_meta_id: %d", prediction_meta_id)
|
|
100
|
-
with transaction.atomic():
|
|
101
|
-
for segment in new_segments:
|
|
102
|
-
start_frame = float(segment["start_frame_number"])
|
|
103
|
-
end_frame = float(segment["end_frame_number"])
|
|
104
|
-
|
|
105
|
-
if (start_frame, end_frame) in existing_keys:
|
|
106
|
-
# If segment with exact start_frame and end_frame already exists, no change is needed
|
|
107
|
-
continue
|
|
108
|
-
else:
|
|
109
|
-
# Check if a segment exists with the same start_frame but different end_frame
|
|
110
|
-
existing_segment = LabelVideoSegment.objects.filter(
|
|
111
|
-
video_file_id=video_id, # Changed from video_id to video_file_id
|
|
112
|
-
label_id=label_id,
|
|
113
|
-
start_frame_number=start_frame,
|
|
114
|
-
).first()
|
|
115
|
-
|
|
116
|
-
if existing_segment:
|
|
117
|
-
# If a segment with the same_start_frame exists but the end_frame is different, update it
|
|
118
|
-
if float(existing_segment.end_frame_number) != end_frame:
|
|
119
|
-
existing_segment.end_frame_number = end_frame
|
|
120
|
-
existing_segment.save()
|
|
121
|
-
updated_segments.append(existing_segment)
|
|
122
|
-
else: # Added else block to create new segment if not existing
|
|
123
|
-
new_entries.append(
|
|
124
|
-
LabelVideoSegment(
|
|
125
|
-
video_file_id=video_id, # Changed from video_id to video_file_id
|
|
126
|
-
label_id=label_id,
|
|
127
|
-
start_frame_number=start_frame,
|
|
128
|
-
end_frame_number=end_frame,
|
|
129
|
-
prediction_meta_id=prediction_meta_id,
|
|
130
|
-
)
|
|
131
|
-
)
|
|
132
|
-
print(
|
|
133
|
-
f" Adding new segment: Start {start_frame} → End {end_frame}"
|
|
134
|
-
)
|
|
135
|
-
|
|
136
|
-
# Delete segments that are no longer present in the frontend data
|
|
137
|
-
# Segments to delete are those in existing_keys but not in new_keys
|
|
138
|
-
keys_to_delete = existing_keys - set((float(s['start_frame_number']), float(s['end_frame_number'])) for s in new_segments)
|
|
139
|
-
segments_to_delete_ids = [existing_segments_dict[key].id for key in keys_to_delete]
|
|
140
|
-
|
|
141
|
-
if segments_to_delete_ids:
|
|
142
|
-
LabelVideoSegment.objects.filter(id__in=segments_to_delete_ids).delete()
|
|
143
|
-
deleted_count = len(segments_to_delete_ids)
|
|
144
|
-
else:
|
|
145
|
-
deleted_count = 0
|
|
146
|
-
|
|
147
|
-
# Insert new segments in bulk for efficiency
|
|
148
|
-
if new_entries:
|
|
149
|
-
LabelVideoSegment.objects.bulk_create(new_entries)
|
|
150
|
-
|
|
151
|
-
logger.debug(
|
|
152
|
-
"After Update: Updated %d segments, Added %d, Deleted %d",
|
|
153
|
-
len(updated_segments), len(new_entries), deleted_count
|
|
154
|
-
)
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
return {
|
|
158
|
-
"updated_segments": LabelVideoSegmentSerializer(
|
|
159
|
-
updated_segments, many=True
|
|
160
|
-
).data,
|
|
161
|
-
"new_segments": LabelVideoSegmentSerializer(new_entries, many=True).data,
|
|
162
|
-
"deleted_segments": deleted_count,
|
|
163
|
-
}
|