endoreg-db 0.8.9.2__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 +89 -122
- 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/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 -4
- 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/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.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +434 -431
- 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.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,21 +1,25 @@
|
|
|
1
|
-
import os
|
|
2
1
|
import subprocess
|
|
3
2
|
import pathlib
|
|
4
3
|
import math
|
|
5
4
|
import logging
|
|
6
|
-
import json
|
|
7
5
|
|
|
8
6
|
# Setup basic logging
|
|
9
|
-
logging.basicConfig(
|
|
7
|
+
logging.basicConfig(
|
|
8
|
+
level=logging.INFO, format="%(asctime)s - %(levelname)s - %(message)s"
|
|
9
|
+
)
|
|
10
|
+
|
|
10
11
|
|
|
11
12
|
def get_video_duration(video_path: pathlib.Path) -> float:
|
|
12
13
|
"""Gets the duration of a video file using ffprobe."""
|
|
13
14
|
cmd = [
|
|
14
15
|
"ffprobe",
|
|
15
|
-
"-v",
|
|
16
|
-
"
|
|
17
|
-
"-
|
|
18
|
-
|
|
16
|
+
"-v",
|
|
17
|
+
"error",
|
|
18
|
+
"-show_entries",
|
|
19
|
+
"format=duration",
|
|
20
|
+
"-of",
|
|
21
|
+
"default=noprint_wrappers=1:nokey=1",
|
|
22
|
+
str(video_path),
|
|
19
23
|
]
|
|
20
24
|
try:
|
|
21
25
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
@@ -28,6 +32,7 @@ def get_video_duration(video_path: pathlib.Path) -> float:
|
|
|
28
32
|
logging.error(f"Could not parse duration from ffprobe output: {result.stdout}")
|
|
29
33
|
raise
|
|
30
34
|
|
|
35
|
+
|
|
31
36
|
def split_video(input_path: str, interval: int):
|
|
32
37
|
"""
|
|
33
38
|
Splits a video into segments of a specified interval using ffmpeg.
|
|
@@ -54,11 +59,13 @@ def split_video(input_path: str, interval: int):
|
|
|
54
59
|
return
|
|
55
60
|
|
|
56
61
|
num_segments = math.ceil(duration / interval)
|
|
57
|
-
logging.info(
|
|
62
|
+
logging.info(
|
|
63
|
+
f"Splitting into {num_segments} segments of approximately {interval} seconds each."
|
|
64
|
+
)
|
|
58
65
|
|
|
59
66
|
for i in range(num_segments):
|
|
60
67
|
start_time = i * interval
|
|
61
|
-
output_filename = output_dir / f"segment_{i+1:03d}{input_file.suffix}"
|
|
68
|
+
output_filename = output_dir / f"segment_{i + 1:03d}{input_file.suffix}"
|
|
62
69
|
|
|
63
70
|
# Use -t for interval duration. For the last segment, ffmpeg with -c copy
|
|
64
71
|
# might automatically stop at the end, or we could calculate exact duration.
|
|
@@ -69,26 +76,30 @@ def split_video(input_path: str, interval: int):
|
|
|
69
76
|
|
|
70
77
|
cmd = [
|
|
71
78
|
"ffmpeg",
|
|
72
|
-
"-i",
|
|
73
|
-
|
|
74
|
-
"-
|
|
75
|
-
|
|
76
|
-
"-
|
|
77
|
-
str(
|
|
79
|
+
"-i",
|
|
80
|
+
str(input_file),
|
|
81
|
+
"-ss",
|
|
82
|
+
str(start_time),
|
|
83
|
+
"-t",
|
|
84
|
+
str(interval),
|
|
85
|
+
"-c",
|
|
86
|
+
"copy", # Fast, lossless splitting
|
|
87
|
+
"-avoid_negative_ts",
|
|
88
|
+
"make_zero", # Avoids issues with negative timestamps
|
|
89
|
+
str(output_filename),
|
|
78
90
|
]
|
|
79
91
|
|
|
80
92
|
logging.info(f"Running command: {' '.join(cmd)}")
|
|
81
93
|
try:
|
|
82
94
|
result = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
83
95
|
logging.info(f"Successfully created segment: {output_filename}")
|
|
84
|
-
if result.stderr:
|
|
85
|
-
|
|
96
|
+
if result.stderr: # ffmpeg often outputs info to stderr
|
|
97
|
+
logging.debug(f"ffmpeg output for segment {i + 1}:\n{result.stderr}")
|
|
86
98
|
except subprocess.CalledProcessError as e:
|
|
87
|
-
logging.error(f"Error creating segment {i+1}: {output_filename}")
|
|
99
|
+
logging.error(f"Error creating segment {i + 1}: {output_filename}")
|
|
88
100
|
logging.error(f"Command failed: {' '.join(cmd)}")
|
|
89
101
|
logging.error(f"ffmpeg stderr:\n{e.stderr}")
|
|
90
102
|
# Decide if you want to stop on error or continue
|
|
91
103
|
# return # Uncomment to stop on first error
|
|
92
104
|
|
|
93
105
|
logging.info("Video splitting completed.")
|
|
94
|
-
|
|
@@ -4,19 +4,17 @@ from rest_framework import status
|
|
|
4
4
|
from endoreg_db.models import VideoFile
|
|
5
5
|
from ..serializers.Frames_NICE_and_PARIS_classifications import (
|
|
6
6
|
ForNiceClassificationSerializer,
|
|
7
|
-
ForParisClassificationSerializer
|
|
7
|
+
ForParisClassificationSerializer,
|
|
8
8
|
)
|
|
9
9
|
|
|
10
|
-
import logging
|
|
11
|
-
|
|
12
10
|
|
|
13
11
|
class ForNiceClassificationView(APIView):
|
|
14
12
|
"""
|
|
15
13
|
NICE Classification API View
|
|
16
|
-
|
|
14
|
+
|
|
17
15
|
GET: Führt NICE-Klassifikation für alle Videos durch
|
|
18
16
|
POST: Führt NICE-Klassifikation für spezifizierte Videos durch
|
|
19
|
-
|
|
17
|
+
|
|
20
18
|
POST Body: {"video_ids": [1, 2, 3]} oder leerer Body für alle Videos
|
|
21
19
|
"""
|
|
22
20
|
|
|
@@ -34,12 +32,14 @@ class ForNiceClassificationView(APIView):
|
|
|
34
32
|
try:
|
|
35
33
|
# Handle POST data for specific video IDs
|
|
36
34
|
video_ids = None
|
|
37
|
-
if request.method ==
|
|
38
|
-
video_ids = request.data.get(
|
|
35
|
+
if request.method == "POST" and hasattr(request, "data"):
|
|
36
|
+
video_ids = request.data.get("video_ids", None)
|
|
39
37
|
|
|
40
38
|
if video_ids:
|
|
41
39
|
videos = VideoFile.objects.filter(id__in=video_ids)
|
|
42
|
-
print(
|
|
40
|
+
print(
|
|
41
|
+
f"[DEBUG] Processing NICE classification for specific videos: {video_ids}"
|
|
42
|
+
)
|
|
43
43
|
else:
|
|
44
44
|
videos = VideoFile.objects.all()
|
|
45
45
|
print("[DEBUG] Processing NICE classification for all videos")
|
|
@@ -51,7 +51,7 @@ class ForNiceClassificationView(APIView):
|
|
|
51
51
|
if not videos.exists():
|
|
52
52
|
return Response(
|
|
53
53
|
{"error": "No videos found in the database."},
|
|
54
|
-
status=status.HTTP_404_NOT_FOUND
|
|
54
|
+
status=status.HTTP_404_NOT_FOUND,
|
|
55
55
|
)
|
|
56
56
|
|
|
57
57
|
serializer = ForNiceClassificationSerializer()
|
|
@@ -60,7 +60,7 @@ class ForNiceClassificationView(APIView):
|
|
|
60
60
|
if not response_data:
|
|
61
61
|
return Response(
|
|
62
62
|
{"error": "No valid segments for NICE classification."},
|
|
63
|
-
status=status.HTTP_404_NOT_FOUND
|
|
63
|
+
status=status.HTTP_404_NOT_FOUND,
|
|
64
64
|
)
|
|
65
65
|
|
|
66
66
|
return Response(response_data, status=status.HTTP_200_OK)
|
|
@@ -68,17 +68,17 @@ class ForNiceClassificationView(APIView):
|
|
|
68
68
|
except Exception as e:
|
|
69
69
|
return Response(
|
|
70
70
|
{"error": f"Internal server error: {str(e)}"},
|
|
71
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
71
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
72
72
|
)
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
class ForParisClassificationView(APIView):
|
|
76
76
|
"""
|
|
77
77
|
PARIS Classification API View
|
|
78
|
-
|
|
78
|
+
|
|
79
79
|
GET: Führt PARIS-Klassifikation für alle Videos durch
|
|
80
80
|
POST: Führt PARIS-Klassifikation für spezifizierte Videos durch
|
|
81
|
-
|
|
81
|
+
|
|
82
82
|
POST Body: {"video_ids": [1, 2, 3]} oder leerer Body für alle Videos
|
|
83
83
|
"""
|
|
84
84
|
|
|
@@ -96,27 +96,32 @@ class ForParisClassificationView(APIView):
|
|
|
96
96
|
try:
|
|
97
97
|
# Handle POST data for specific video IDs
|
|
98
98
|
video_ids = None
|
|
99
|
-
if request.method ==
|
|
100
|
-
video_ids = request.data.get(
|
|
99
|
+
if request.method == "POST" and hasattr(request, "data"):
|
|
100
|
+
video_ids = request.data.get("video_ids", None)
|
|
101
101
|
|
|
102
102
|
if video_ids:
|
|
103
103
|
videos = VideoFile.objects.filter(id__in=video_ids)
|
|
104
|
-
print(
|
|
104
|
+
print(
|
|
105
|
+
f"[DEBUG] Processing PARIS classification for specific videos: {video_ids}"
|
|
106
|
+
)
|
|
105
107
|
else:
|
|
106
108
|
videos = VideoFile.objects.all()
|
|
107
|
-
print(
|
|
109
|
+
print("[DEBUG] Processing PARIS classification for all videos")
|
|
108
110
|
|
|
109
111
|
print(f"[DEBUG] Total videos found: {videos.count()}")
|
|
110
112
|
|
|
111
113
|
filtered_videos = [
|
|
112
|
-
video
|
|
113
|
-
|
|
114
|
+
video
|
|
115
|
+
for video in videos
|
|
116
|
+
if getattr(
|
|
117
|
+
video, "frame_dir", None
|
|
118
|
+
) # no more readable_predictions check
|
|
114
119
|
]
|
|
115
120
|
|
|
116
121
|
if not filtered_videos:
|
|
117
122
|
return Response(
|
|
118
123
|
{"error": "No videos with valid frame_dir found."},
|
|
119
|
-
status=status.HTTP_404_NOT_FOUND
|
|
124
|
+
status=status.HTTP_404_NOT_FOUND,
|
|
120
125
|
)
|
|
121
126
|
|
|
122
127
|
serializer = ForParisClassificationSerializer()
|
|
@@ -125,7 +130,7 @@ class ForParisClassificationView(APIView):
|
|
|
125
130
|
if not response_data:
|
|
126
131
|
return Response(
|
|
127
132
|
{"error": "No valid PARIS segments found."},
|
|
128
|
-
status=status.HTTP_404_NOT_FOUND
|
|
133
|
+
status=status.HTTP_404_NOT_FOUND,
|
|
129
134
|
)
|
|
130
135
|
|
|
131
136
|
return Response(response_data, status=status.HTTP_200_OK)
|
|
@@ -133,16 +138,16 @@ class ForParisClassificationView(APIView):
|
|
|
133
138
|
except Exception as e:
|
|
134
139
|
return Response(
|
|
135
140
|
{"error": f"Internal server error: {str(e)}"},
|
|
136
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
141
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
137
142
|
)
|
|
138
143
|
|
|
139
144
|
|
|
140
145
|
class BatchClassificationView(APIView):
|
|
141
146
|
"""
|
|
142
147
|
Batch Classification API View
|
|
143
|
-
|
|
148
|
+
|
|
144
149
|
POST: Führt beide Klassifikationstypen (NICE und PARIS) für spezifizierte Videos durch
|
|
145
|
-
|
|
150
|
+
|
|
146
151
|
POST Body: {
|
|
147
152
|
"video_ids": [1, 2, 3],
|
|
148
153
|
"types": ["nice", "paris"] # Optional, default beide
|
|
@@ -151,8 +156,8 @@ class BatchClassificationView(APIView):
|
|
|
151
156
|
|
|
152
157
|
def post(self, request):
|
|
153
158
|
try:
|
|
154
|
-
video_ids = request.data.get(
|
|
155
|
-
classification_types = request.data.get(
|
|
159
|
+
video_ids = request.data.get("video_ids", None)
|
|
160
|
+
classification_types = request.data.get("types", ["nice", "paris"])
|
|
156
161
|
|
|
157
162
|
if video_ids:
|
|
158
163
|
videos = VideoFile.objects.filter(id__in=video_ids)
|
|
@@ -161,67 +166,71 @@ class BatchClassificationView(APIView):
|
|
|
161
166
|
|
|
162
167
|
if not videos.exists():
|
|
163
168
|
return Response(
|
|
164
|
-
{"error": "No videos found."},
|
|
165
|
-
status=status.HTTP_404_NOT_FOUND
|
|
169
|
+
{"error": "No videos found."}, status=status.HTTP_404_NOT_FOUND
|
|
166
170
|
)
|
|
167
171
|
|
|
168
172
|
results = {}
|
|
169
173
|
|
|
170
|
-
if
|
|
174
|
+
if "nice" in classification_types:
|
|
171
175
|
nice_serializer = ForNiceClassificationSerializer()
|
|
172
|
-
results[
|
|
176
|
+
results["nice"] = nice_serializer.to_representation(videos)
|
|
173
177
|
|
|
174
|
-
if
|
|
178
|
+
if "paris" in classification_types:
|
|
175
179
|
# Filter videos for PARIS (need frame_dir)
|
|
176
180
|
filtered_videos = [
|
|
177
|
-
video for video in videos
|
|
178
|
-
if getattr(video, "frame_dir", None)
|
|
181
|
+
video for video in videos if getattr(video, "frame_dir", None)
|
|
179
182
|
]
|
|
180
|
-
|
|
183
|
+
|
|
181
184
|
if filtered_videos:
|
|
182
185
|
paris_serializer = ForParisClassificationSerializer()
|
|
183
|
-
results[
|
|
186
|
+
results["paris"] = paris_serializer.to_representation(
|
|
187
|
+
filtered_videos
|
|
188
|
+
)
|
|
184
189
|
else:
|
|
185
|
-
results[
|
|
190
|
+
results["paris"] = {
|
|
191
|
+
"error": "No videos with valid frame_dir found for PARIS classification."
|
|
192
|
+
}
|
|
186
193
|
|
|
187
|
-
return Response(
|
|
188
|
-
"message": "Batch classification completed.",
|
|
189
|
-
|
|
190
|
-
|
|
194
|
+
return Response(
|
|
195
|
+
{"message": "Batch classification completed.", "results": results},
|
|
196
|
+
status=status.HTTP_200_OK,
|
|
197
|
+
)
|
|
191
198
|
|
|
192
199
|
except Exception as e:
|
|
193
200
|
return Response(
|
|
194
201
|
{"error": f"Internal server error: {str(e)}"},
|
|
195
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
202
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
196
203
|
)
|
|
197
204
|
|
|
198
205
|
|
|
199
206
|
class ClassificationStatusView(APIView):
|
|
200
207
|
"""
|
|
201
208
|
Classification Status API View
|
|
202
|
-
|
|
209
|
+
|
|
203
210
|
GET: Gibt den Status der Klassifikationen für ein Video zurück
|
|
204
211
|
"""
|
|
205
212
|
|
|
206
213
|
def get(self, request, video_id):
|
|
207
214
|
try:
|
|
208
215
|
video = VideoFile.objects.get(id=video_id)
|
|
209
|
-
|
|
216
|
+
|
|
210
217
|
# Check if classifications exist for this video
|
|
211
218
|
# This would typically check for saved classification results in the database
|
|
212
219
|
# For now, we'll return basic status information
|
|
213
|
-
|
|
220
|
+
|
|
214
221
|
status_info = {
|
|
215
222
|
"video_id": video_id,
|
|
216
|
-
"video_name": getattr(video,
|
|
223
|
+
"video_name": getattr(video, "original_file_name", "Unknown"),
|
|
217
224
|
"has_frame_dir": bool(getattr(video, "frame_dir", None)),
|
|
218
225
|
"nice_classification_available": True, # Always available for NICE
|
|
219
|
-
"paris_classification_available": bool(
|
|
226
|
+
"paris_classification_available": bool(
|
|
227
|
+
getattr(video, "frame_dir", None)
|
|
228
|
+
),
|
|
220
229
|
"last_processed": None, # Would come from classification results table
|
|
221
230
|
"classification_results": {
|
|
222
231
|
"nice": None, # Would contain saved NICE results
|
|
223
|
-
"paris": None
|
|
224
|
-
}
|
|
232
|
+
"paris": None, # Would contain saved PARIS results
|
|
233
|
+
},
|
|
225
234
|
}
|
|
226
235
|
|
|
227
236
|
return Response(status_info, status=status.HTTP_200_OK)
|
|
@@ -229,10 +238,10 @@ class ClassificationStatusView(APIView):
|
|
|
229
238
|
except VideoFile.DoesNotExist:
|
|
230
239
|
return Response(
|
|
231
240
|
{"error": f"Video with ID {video_id} not found."},
|
|
232
|
-
status=status.HTTP_404_NOT_FOUND
|
|
241
|
+
status=status.HTTP_404_NOT_FOUND,
|
|
233
242
|
)
|
|
234
243
|
except Exception as e:
|
|
235
244
|
return Response(
|
|
236
245
|
{"error": f"Internal server error: {str(e)}"},
|
|
237
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
246
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
238
247
|
)
|
endoreg_db/views/__init__.py
CHANGED
|
@@ -43,20 +43,11 @@ from .media import (
|
|
|
43
43
|
)
|
|
44
44
|
from .meta import SensitiveMetaListView, SensitiveMetaVerificationView
|
|
45
45
|
from .misc import (
|
|
46
|
-
MODELTRANSLATION_SETTINGS,
|
|
47
46
|
CenterViewSet,
|
|
48
47
|
ExaminationStatsView,
|
|
49
|
-
ExaminationTranslationOptions,
|
|
50
|
-
FindingClassificationChoiceTranslationOptions,
|
|
51
|
-
FindingClassificationTranslationOptions,
|
|
52
|
-
FindingTranslationOptions,
|
|
53
48
|
GenderViewSet,
|
|
54
49
|
GeneralStatsView,
|
|
55
|
-
InterventionTranslationOptions,
|
|
56
50
|
SensitiveMetaStatsView,
|
|
57
|
-
TranslatedFieldMixin,
|
|
58
|
-
TranslatedFixtureLoader,
|
|
59
|
-
TranslationMigrationHelper,
|
|
60
51
|
UploadFileView,
|
|
61
52
|
UploadStatusView,
|
|
62
53
|
VideoSegmentStatsView,
|
|
@@ -77,7 +68,6 @@ from .video import ( # Video Correction (Phase 1.1) - Implemented; Existing vie
|
|
|
77
68
|
VideoApplyMaskView,
|
|
78
69
|
VideoCorrectionView,
|
|
79
70
|
VideoExaminationViewSet,
|
|
80
|
-
VideoProcessingHistoryView,
|
|
81
71
|
VideoReimportView,
|
|
82
72
|
VideoRemoveFramesView,
|
|
83
73
|
VideoStreamView,
|
|
@@ -127,16 +117,7 @@ __all__ = [
|
|
|
127
117
|
"VideoSegmentStatsView",
|
|
128
118
|
"SensitiveMetaStatsView",
|
|
129
119
|
"GeneralStatsView",
|
|
130
|
-
"ExaminationTranslationOptions",
|
|
131
|
-
"FindingTranslationOptions",
|
|
132
|
-
"FindingClassificationTranslationOptions",
|
|
133
|
-
"FindingClassificationChoiceTranslationOptions",
|
|
134
|
-
"InterventionTranslationOptions",
|
|
135
|
-
"TranslatedFieldMixin",
|
|
136
|
-
"TranslationMigrationHelper",
|
|
137
|
-
"TranslatedFixtureLoader",
|
|
138
120
|
"build_multilingual_response",
|
|
139
|
-
"MODELTRANSLATION_SETTINGS",
|
|
140
121
|
"UploadFileView",
|
|
141
122
|
"UploadStatusView",
|
|
142
123
|
# Patient Views
|
|
@@ -158,7 +139,6 @@ __all__ = [
|
|
|
158
139
|
"evaluate_requirements",
|
|
159
140
|
"LookupViewSet",
|
|
160
141
|
# Video Views (Phase 1.1 - Implemented)
|
|
161
|
-
"VideoProcessingHistoryView",
|
|
162
142
|
"VideoApplyMaskView",
|
|
163
143
|
"VideoRemoveFramesView",
|
|
164
144
|
"VideoCorrectionView",
|
|
@@ -10,7 +10,11 @@ from .overview import (
|
|
|
10
10
|
|
|
11
11
|
from .validate import AnonymizationValidateView
|
|
12
12
|
|
|
13
|
-
from .media_management import
|
|
13
|
+
from .media_management import (
|
|
14
|
+
MediaManagementView,
|
|
15
|
+
force_remove_media,
|
|
16
|
+
reset_processing_status,
|
|
17
|
+
)
|
|
14
18
|
|
|
15
19
|
__all__ = [
|
|
16
20
|
"AnonymizationOverviewView",
|
|
@@ -24,4 +28,4 @@ __all__ = [
|
|
|
24
28
|
"force_remove_media",
|
|
25
29
|
"reset_processing_status",
|
|
26
30
|
"has_raw_video_file",
|
|
27
|
-
]
|
|
31
|
+
]
|
|
@@ -5,9 +5,7 @@ from datetime import timedelta
|
|
|
5
5
|
from typing import Any, Dict
|
|
6
6
|
|
|
7
7
|
from django.db import transaction
|
|
8
|
-
from django.db.models import Q
|
|
9
8
|
from django.utils import timezone
|
|
10
|
-
from numpy import delete
|
|
11
9
|
from rest_framework import status
|
|
12
10
|
from rest_framework.decorators import api_view, permission_classes
|
|
13
11
|
from rest_framework.response import Response
|
|
@@ -195,12 +193,10 @@ class MediaManagementView(APIView):
|
|
|
195
193
|
pdf_file_obj = None
|
|
196
194
|
|
|
197
195
|
if media_type == "video":
|
|
198
|
-
video_file_obj = (
|
|
199
|
-
VideoFile.get_video_by_pk(pk=file_id) if file_id else None
|
|
200
|
-
)
|
|
196
|
+
video_file_obj = VideoFile.get_video_by_pk(pk=file_id) if file_id else None
|
|
201
197
|
|
|
202
198
|
elif media_type == "pdf":
|
|
203
|
-
pdf_file_obj = RawPdfFile.
|
|
199
|
+
pdf_file_obj = RawPdfFile.get_report_by_pk(pk=file_id) if file_id else None
|
|
204
200
|
|
|
205
201
|
with transaction.atomic():
|
|
206
202
|
if video_file_obj:
|
|
@@ -19,6 +19,8 @@ from endoreg_db.services.polling_coordinator import (
|
|
|
19
19
|
from endoreg_db.utils.permissions import DEBUG_PERMISSIONS
|
|
20
20
|
|
|
21
21
|
from ...serializers import FileOverviewSerializer, VoPPatientDataSerializer
|
|
22
|
+
from endoreg_db.utils.operation_log import record_operation
|
|
23
|
+
|
|
22
24
|
|
|
23
25
|
logger = logging.getLogger(__name__)
|
|
24
26
|
PERMS = DEBUG_PERMISSIONS # shorten
|
|
@@ -127,7 +129,10 @@ def start_anonymization(request, file_id: int):
|
|
|
127
129
|
if not info:
|
|
128
130
|
return Response({"detail": "File not found"}, status=status.HTTP_404_NOT_FOUND)
|
|
129
131
|
|
|
130
|
-
file_type = info
|
|
132
|
+
file_type = info.get("mediaType") or "unknown"
|
|
133
|
+
status_before = (
|
|
134
|
+
info.get("anonymizationStatus") or info.get("status") or "not_started"
|
|
135
|
+
)
|
|
131
136
|
|
|
132
137
|
# Use processing lock context to prevent duplicate processing
|
|
133
138
|
with ProcessingLockContext(file_id, file_type) as lock:
|
|
@@ -151,6 +156,34 @@ def start_anonymization(request, file_id: int):
|
|
|
151
156
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
152
157
|
)
|
|
153
158
|
|
|
159
|
+
# Re-read status AFTER starting
|
|
160
|
+
try:
|
|
161
|
+
info_after = AnonymizationService.get_status(file_id) or {}
|
|
162
|
+
except Exception:
|
|
163
|
+
logger.exception(
|
|
164
|
+
"Failed to refresh anonymization status for file %s", file_id
|
|
165
|
+
)
|
|
166
|
+
info_after = {}
|
|
167
|
+
|
|
168
|
+
status_after = (
|
|
169
|
+
info_after.get("anonymizationStatus")
|
|
170
|
+
or info_after.get("status")
|
|
171
|
+
or status_before
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# 🔐 Write operation log
|
|
175
|
+
record_operation(
|
|
176
|
+
request,
|
|
177
|
+
action="anonymization.start",
|
|
178
|
+
resource_type=kind, # 'video' or 'pdf' as returned by service.start
|
|
179
|
+
resource_id=file_id,
|
|
180
|
+
status_before=str(status_before),
|
|
181
|
+
status_after=str(status_after),
|
|
182
|
+
meta={
|
|
183
|
+
"file_type_from_status": file_type,
|
|
184
|
+
},
|
|
185
|
+
)
|
|
186
|
+
|
|
154
187
|
return Response(
|
|
155
188
|
{
|
|
156
189
|
"detail": f"Anonymization started for {kind} file",
|
|
@@ -9,6 +9,9 @@ from rest_framework.views import APIView
|
|
|
9
9
|
from endoreg_db.models import RawPdfFile, VideoFile
|
|
10
10
|
from endoreg_db.models.metadata import SensitiveMeta
|
|
11
11
|
from endoreg_db.serializers.anonymization import SensitiveMetaValidateSerializer
|
|
12
|
+
from endoreg_db.utils.operation_log import (
|
|
13
|
+
record_operation, # only touched the parts where validation succeeds
|
|
14
|
+
)
|
|
12
15
|
|
|
13
16
|
logger = logging.getLogger(__name__)
|
|
14
17
|
|
|
@@ -17,7 +20,7 @@ class AnonymizationValidateView(APIView):
|
|
|
17
20
|
"""
|
|
18
21
|
POST /api/anonymization/<int:file_id>/validate/
|
|
19
22
|
|
|
20
|
-
Validiert und aktualisiert SensitiveMeta-Felder für Videos oder
|
|
23
|
+
Validiert und aktualisiert SensitiveMeta-Felder für Videos oder reports.
|
|
21
24
|
|
|
22
25
|
DATA HERE IS COMING FROM THE ANONYIZATION VALIDATION COMPONENT
|
|
23
26
|
|
|
@@ -30,7 +33,7 @@ class AnonymizationValidateView(APIView):
|
|
|
30
33
|
"examination_date": "15.02.2024", // DD.MM.YYYY bevorzugt
|
|
31
34
|
|
|
32
35
|
"casenumber": "12345",
|
|
33
|
-
"anonymized_text": "...", // nur für
|
|
36
|
+
"anonymized_text": "...", // nur für reports; Videos ignorieren
|
|
34
37
|
"is_verified": true // optional; default true
|
|
35
38
|
"file_type": "video" // optional; "video" oder "pdf"; wenn nicht angegeben, wird zuerst Video, dann report versucht
|
|
36
39
|
"center_name": editedPatient.value.centerName || '',
|
|
@@ -54,6 +57,7 @@ class AnonymizationValidateView(APIView):
|
|
|
54
57
|
payload["is_verified"] = True
|
|
55
58
|
|
|
56
59
|
file_type = payload.get("file_type")
|
|
60
|
+
status_before = None
|
|
57
61
|
|
|
58
62
|
with transaction.atomic():
|
|
59
63
|
# Try Video first (unless explicitly requesting report)
|
|
@@ -65,6 +69,7 @@ class AnonymizationValidateView(APIView):
|
|
|
65
69
|
.filter(pk=file_id)
|
|
66
70
|
.first()
|
|
67
71
|
)
|
|
72
|
+
# TODO: The state for video will be none when no state is set and the state for pdf will always be none. After status needs to be inferred after calling the sensitive meta state update functions
|
|
68
73
|
if video is not None:
|
|
69
74
|
prepared_payload = self._prepare_payload(payload, video)
|
|
70
75
|
try:
|
|
@@ -103,9 +108,43 @@ class AnonymizationValidateView(APIView):
|
|
|
103
108
|
)
|
|
104
109
|
|
|
105
110
|
if video.state is not None:
|
|
106
|
-
video.state.
|
|
107
|
-
video.state.save(update_fields=["anonymized"])
|
|
111
|
+
video.state.anonymized = True
|
|
108
112
|
video.sensitive_meta.state.save()
|
|
113
|
+
try:
|
|
114
|
+
if video.state is not None:
|
|
115
|
+
st = getattr(video.state, "anonymization_status", None)
|
|
116
|
+
if st is not None:
|
|
117
|
+
status_before = str(getattr(st, "value", st))
|
|
118
|
+
except Exception:
|
|
119
|
+
logger.exception(
|
|
120
|
+
"Failed to read video anonymization_status before validation"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
# --- NEW: status AFTER validation ---
|
|
124
|
+
status_after = status_before
|
|
125
|
+
try:
|
|
126
|
+
if video.state is not None:
|
|
127
|
+
video.state.refresh_from_db()
|
|
128
|
+
st_after = getattr(
|
|
129
|
+
video.state, "anonymization_status", None
|
|
130
|
+
)
|
|
131
|
+
if st_after is not None:
|
|
132
|
+
status_after = str(getattr(st_after, "value", st_after))
|
|
133
|
+
except Exception:
|
|
134
|
+
logger.exception(
|
|
135
|
+
"Failed to read video anonymization_status after validation"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
# --- write operation log ---
|
|
139
|
+
# TODO: update the function call bases on the status , once merged
|
|
140
|
+
record_operation(
|
|
141
|
+
request,
|
|
142
|
+
action="anonymization.validated",
|
|
143
|
+
resource_type="video",
|
|
144
|
+
resource_id=file_id,
|
|
145
|
+
status_before=status_before,
|
|
146
|
+
status_after=status_after,
|
|
147
|
+
)
|
|
109
148
|
|
|
110
149
|
return Response(
|
|
111
150
|
{"message": "Video validated."},
|
|
@@ -161,25 +200,47 @@ class AnonymizationValidateView(APIView):
|
|
|
161
200
|
pdf.save(update_fields=["sensitive_meta"])
|
|
162
201
|
pdf.sensitive_meta.get_or_create_state()
|
|
163
202
|
if (
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
203
|
+
pdf.sensitive_meta
|
|
204
|
+
and pdf.sensitive_meta.state
|
|
205
|
+
):
|
|
206
|
+
pdf.sensitive_meta.state.refresh_from_db()
|
|
207
|
+
pdf.sensitive_meta.state.mark_dob_verified()
|
|
208
|
+
pdf.sensitive_meta.state.mark_names_verified()
|
|
209
|
+
pdf.sensitive_meta.create_anonymized_record()
|
|
210
|
+
|
|
211
|
+
if pdf.state:
|
|
212
|
+
pdf.state.mark_anonymized()
|
|
213
|
+
pdf.state.save(update_fields=["anonymized"])
|
|
214
|
+
|
|
215
|
+
pdf.sensitive_meta.state.save()
|
|
175
216
|
else:
|
|
176
217
|
return Response(
|
|
177
|
-
{
|
|
178
|
-
"message": "report not validated, failed to create State."
|
|
179
|
-
},
|
|
218
|
+
{"message": "report not validated, failed to create State."},
|
|
180
219
|
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
181
220
|
)
|
|
182
221
|
|
|
222
|
+
status_after = status_before
|
|
223
|
+
try:
|
|
224
|
+
if pdf.state is not None:
|
|
225
|
+
pdf.state.refresh_from_db()
|
|
226
|
+
st_after = getattr(pdf.state, "anonymization_status", None)
|
|
227
|
+
if st_after is not None:
|
|
228
|
+
status_after = str(getattr(st_after, "value", st_after))
|
|
229
|
+
except Exception:
|
|
230
|
+
logger.exception(
|
|
231
|
+
"Failed to read pdf anonymization_status after validation"
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
# --- NEW: write operation log ---
|
|
235
|
+
record_operation(
|
|
236
|
+
request,
|
|
237
|
+
action="anonymization.validated",
|
|
238
|
+
resource_type="pdf",
|
|
239
|
+
resource_id=file_id,
|
|
240
|
+
status_before=status_before,
|
|
241
|
+
status_after=status_after,
|
|
242
|
+
)
|
|
243
|
+
|
|
183
244
|
return Response(
|
|
184
245
|
{"message": "report validated."},
|
|
185
246
|
status=status.HTTP_200_OK,
|