endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.9__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/authz/auth.py +74 -0
- endoreg_db/authz/backends.py +168 -0
- endoreg_db/authz/management/commands/list_routes.py +18 -0
- endoreg_db/authz/middleware.py +83 -0
- endoreg_db/authz/permissions.py +127 -0
- endoreg_db/authz/policy.py +218 -0
- endoreg_db/authz/views_auth.py +66 -0
- endoreg_db/config/env.py +13 -8
- endoreg_db/data/__init__.py +2 -11
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +3 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/examination/examinations/data.yaml +114 -14
- endoreg_db/data/examination/time-type/data.yaml +0 -3
- endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
- endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
- endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
- endoreg_db/data/finding/00_generic.yaml +35 -0
- endoreg_db/data/finding/00_generic_complication.yaml +9 -0
- endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
- endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
- endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
- endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
- endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
- endoreg_db/data/finding_classification/00_generic.yaml +44 -0
- endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
- endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
- endoreg_db/data/finding_classification/02_colonoscopy_baseline.yaml +83 -0
- endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
- endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
- endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
- endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
- endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
- endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
- endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
- endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
- endoreg_db/data/finding_classification_choice/{colonoscopy_not_complete_reason.yaml → 02_colonoscopy_generic.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{colonoscopy_location.yaml → 02_colonoscopy_location.yaml} +23 -4
- endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
- endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
- endoreg_db/data/finding_classification_choice/{colon_lesion_paris.yaml → 02_colonoscopy_polyp_morphology.yaml} +26 -8
- endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
- endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
- endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
- endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
- endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
- endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
- endoreg_db/data/finding_type/data.yaml +8 -12
- endoreg_db/data/requirement/01_patient_data.yaml +93 -0
- endoreg_db/data/requirement/old/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/old/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +29 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/{endoscopy_bleeding_risk.yaml → 02_endoscopy_bleeding_risk.yaml} +0 -6
- endoreg_db/data/requirement_set/90_coloreg.yaml +190 -0
- endoreg_db/data/requirement_set/_old_ +109 -0
- endoreg_db/data/requirement_set_type/data.yaml +21 -0
- endoreg_db/data/setup_config.yaml +4 -4
- endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
- endoreg_db/exceptions.py +4 -2
- endoreg_db/forms/examination_form.py +1 -1
- endoreg_db/helpers/data_loader.py +125 -53
- endoreg_db/helpers/default_objects.py +116 -81
- endoreg_db/import_files/__init__.py +27 -0
- endoreg_db/import_files/context/__init__.py +7 -0
- endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
- endoreg_db/import_files/context/ensure_center.py +17 -0
- endoreg_db/import_files/context/file_lock.py +66 -0
- endoreg_db/import_files/context/import_context.py +43 -0
- endoreg_db/import_files/context/validate_directories.py +56 -0
- endoreg_db/import_files/file_storage/__init__.py +15 -0
- endoreg_db/import_files/file_storage/create_report_file.py +76 -0
- endoreg_db/import_files/file_storage/create_video_file.py +75 -0
- endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
- endoreg_db/import_files/file_storage/state_management.py +400 -0
- endoreg_db/import_files/file_storage/storage.py +36 -0
- endoreg_db/import_files/import_service.md +26 -0
- endoreg_db/import_files/processing/__init__.py +11 -0
- endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
- endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
- endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
- endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
- endoreg_db/import_files/pseudonymization/fake.py +52 -0
- endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
- endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
- endoreg_db/import_files/report_import_service.py +141 -0
- endoreg_db/import_files/video_import_service.py +150 -0
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
- endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
- endoreg_db/management/commands/import_report.py +130 -65
- endoreg_db/management/commands/import_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +2 -2
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_ai_model_data.py +5 -5
- endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
- endoreg_db/management/commands/load_base_db_data.py +5 -134
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_contraindication_data.py +14 -16
- endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
- endoreg_db/management/commands/load_disease_classification_data.py +15 -18
- endoreg_db/management/commands/load_disease_data.py +25 -28
- endoreg_db/management/commands/load_endoscope_data.py +20 -27
- endoreg_db/management/commands/load_event_data.py +14 -16
- endoreg_db/management/commands/load_examination_data.py +31 -44
- endoreg_db/management/commands/load_examination_indication_data.py +20 -21
- endoreg_db/management/commands/load_finding_data.py +52 -80
- endoreg_db/management/commands/load_information_source.py +21 -23
- endoreg_db/management/commands/load_lab_value_data.py +17 -26
- endoreg_db/management/commands/load_medication_data.py +13 -12
- endoreg_db/management/commands/load_organ_data.py +15 -19
- endoreg_db/management/commands/load_pdf_type_data.py +19 -18
- endoreg_db/management/commands/load_profession_data.py +14 -17
- endoreg_db/management/commands/load_qualification_data.py +20 -23
- endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
- endoreg_db/management/commands/load_requirement_data.py +62 -39
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/load_risk_data.py +7 -6
- endoreg_db/management/commands/load_shift_data.py +20 -23
- endoreg_db/management/commands/load_tag_data.py +8 -11
- endoreg_db/management/commands/load_unit_data.py +17 -19
- endoreg_db/management/commands/setup_endoreg_db.py +3 -3
- endoreg_db/management/commands/start_filewatcher.py +46 -37
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/management/commands/validate_video_files.py +1 -5
- endoreg_db/migrations/0001_initial.py +297 -250
- endoreg_db/models/__init__.py +78 -123
- endoreg_db/models/administration/__init__.py +21 -42
- endoreg_db/models/administration/ai/active_model.py +2 -2
- endoreg_db/models/administration/ai/ai_model.py +7 -6
- endoreg_db/models/administration/case/__init__.py +1 -15
- endoreg_db/models/administration/case/case.py +3 -3
- endoreg_db/models/administration/case/case_template/__init__.py +2 -14
- endoreg_db/models/administration/case/case_template/case_template.py +2 -124
- endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
- endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
- endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
- endoreg_db/models/administration/center/center.py +33 -19
- endoreg_db/models/administration/center/center_product.py +12 -9
- endoreg_db/models/administration/center/center_resource.py +25 -19
- endoreg_db/models/administration/center/center_shift.py +21 -17
- endoreg_db/models/administration/center/center_waste.py +16 -8
- endoreg_db/models/administration/person/__init__.py +2 -0
- endoreg_db/models/administration/person/employee/employee.py +10 -5
- endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
- endoreg_db/models/administration/person/employee/employee_type.py +12 -6
- endoreg_db/models/administration/person/examiner/examiner.py +13 -11
- endoreg_db/models/administration/person/patient/__init__.py +2 -0
- endoreg_db/models/administration/person/patient/patient.py +129 -100
- endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
- endoreg_db/models/administration/person/person.py +4 -0
- endoreg_db/models/administration/person/profession/__init__.py +8 -4
- endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
- endoreg_db/models/administration/product/product.py +20 -15
- endoreg_db/models/administration/product/product_material.py +17 -18
- endoreg_db/models/administration/product/product_weight.py +12 -8
- endoreg_db/models/administration/product/reference_product.py +23 -55
- endoreg_db/models/administration/qualification/qualification.py +7 -3
- endoreg_db/models/administration/qualification/qualification_type.py +7 -3
- endoreg_db/models/administration/shift/scheduled_days.py +8 -5
- endoreg_db/models/administration/shift/shift.py +16 -12
- endoreg_db/models/administration/shift/shift_type.py +23 -31
- endoreg_db/models/label/__init__.py +8 -9
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +23 -28
- endoreg_db/models/label/label.py +15 -15
- endoreg_db/models/label/label_set.py +19 -6
- endoreg_db/models/label/label_type.py +1 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
- endoreg_db/models/label/label_video_segment/label_video_segment.py +98 -102
- endoreg_db/models/label/video_segmentation_label.py +4 -0
- endoreg_db/models/label/video_segmentation_labelset.py +4 -3
- endoreg_db/models/media/frame/frame.py +22 -22
- endoreg_db/models/media/pdf/raw_pdf.py +194 -194
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +55 -47
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/processing_history/__init__.py +5 -0
- endoreg_db/models/media/processing_history/processing_history.py +96 -0
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +139 -77
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +174 -112
- endoreg_db/models/media/video/video_file_ai.py +288 -74
- endoreg_db/models/media/video/video_file_anonymize.py +38 -38
- endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
- endoreg_db/models/media/video/video_file_io.py +113 -61
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
- endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
- endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
- endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
- endoreg_db/models/media/video/video_file_segments.py +24 -17
- endoreg_db/models/media/video/video_metadata.py +19 -35
- endoreg_db/models/media/video/video_processing.py +96 -95
- endoreg_db/models/medical/contraindication/README.md +1 -0
- endoreg_db/models/medical/contraindication/__init__.py +13 -3
- endoreg_db/models/medical/disease.py +22 -16
- endoreg_db/models/medical/event.py +31 -18
- endoreg_db/models/medical/examination/__init__.py +13 -6
- endoreg_db/models/medical/examination/examination.py +39 -20
- endoreg_db/models/medical/examination/examination_indication.py +30 -95
- endoreg_db/models/medical/examination/examination_time.py +23 -8
- endoreg_db/models/medical/examination/examination_time_type.py +9 -6
- endoreg_db/models/medical/examination/examination_type.py +3 -4
- endoreg_db/models/medical/finding/finding.py +32 -40
- endoreg_db/models/medical/finding/finding_classification.py +42 -72
- endoreg_db/models/medical/finding/finding_intervention.py +25 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +26 -26
- endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
- endoreg_db/models/medical/laboratory/lab_value.py +62 -91
- endoreg_db/models/medical/medication/medication.py +22 -10
- endoreg_db/models/medical/medication/medication_indication.py +29 -3
- endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
- endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
- endoreg_db/models/medical/medication/medication_schedule.py +27 -16
- endoreg_db/models/medical/organ/__init__.py +15 -12
- endoreg_db/models/medical/patient/medication_examples.py +6 -6
- endoreg_db/models/medical/patient/patient_disease.py +20 -23
- endoreg_db/models/medical/patient/patient_event.py +19 -22
- endoreg_db/models/medical/patient/patient_examination.py +48 -54
- endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
- endoreg_db/models/medical/patient/patient_finding.py +122 -139
- endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
- endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
- endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
- endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
- endoreg_db/models/medical/patient/patient_medication.py +27 -38
- endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
- endoreg_db/models/medical/risk/risk.py +7 -6
- endoreg_db/models/medical/risk/risk_type.py +8 -5
- endoreg_db/models/metadata/model_meta.py +60 -29
- endoreg_db/models/metadata/model_meta_logic.py +125 -18
- endoreg_db/models/metadata/pdf_meta.py +31 -24
- endoreg_db/models/metadata/sensitive_meta.py +105 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +198 -103
- endoreg_db/models/metadata/video_meta.py +51 -31
- endoreg_db/models/metadata/video_prediction_logic.py +16 -23
- endoreg_db/models/metadata/video_prediction_meta.py +29 -33
- endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
- endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
- endoreg_db/models/other/emission/emission_factor.py +18 -8
- endoreg_db/models/other/gender.py +10 -5
- endoreg_db/models/other/information_source.py +50 -29
- endoreg_db/models/other/material.py +9 -5
- endoreg_db/models/other/resource.py +6 -4
- endoreg_db/models/other/tag.py +10 -5
- endoreg_db/models/other/transport_route.py +13 -8
- endoreg_db/models/other/unit.py +10 -6
- endoreg_db/models/other/waste.py +6 -5
- endoreg_db/models/report/report.py +6 -0
- endoreg_db/models/requirement/requirement.py +329 -361
- endoreg_db/models/requirement/requirement_error.py +85 -0
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
- endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
- endoreg_db/models/requirement/requirement_operator.py +103 -112
- endoreg_db/models/requirement/requirement_set.py +74 -57
- endoreg_db/models/state/__init__.py +4 -4
- endoreg_db/models/state/abstract.py +2 -2
- endoreg_db/models/state/anonymization.py +12 -0
- endoreg_db/models/state/audit_ledger.py +49 -51
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +101 -68
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +110 -90
- endoreg_db/models/upload_job.py +35 -34
- endoreg_db/models/utils.py +28 -25
- endoreg_db/queries/__init__.py +3 -1
- endoreg_db/root_urls.py +21 -2
- endoreg_db/schemas/examination_evaluation.py +1 -1
- endoreg_db/serializers/__init__.py +2 -10
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
- endoreg_db/serializers/meta/__init__.py +1 -6
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/file_overview.py +11 -99
- endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
- endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
- endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_file_list.py +65 -34
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/services/__old/pdf_import.py +1487 -0
- endoreg_db/services/__old/video_import.py +1306 -0
- endoreg_db/services/anonymization.py +128 -89
- endoreg_db/services/lookup_service.py +65 -52
- endoreg_db/services/lookup_store.py +2 -2
- endoreg_db/services/pdf_import.py +0 -1382
- endoreg_db/services/report_import.py +10 -0
- endoreg_db/services/video_import.py +6 -1255
- endoreg_db/tasks/upload_tasks.py +79 -70
- endoreg_db/tasks/video_ingest.py +8 -4
- endoreg_db/urls/__init__.py +5 -32
- endoreg_db/urls/ai.py +32 -0
- endoreg_db/urls/media.py +121 -83
- endoreg_db/urls/root_urls.py +29 -0
- endoreg_db/utils/__init__.py +15 -5
- endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
- endoreg_db/utils/case_generator/__init__.py +3 -0
- endoreg_db/utils/dataloader.py +142 -40
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/paths.py +110 -46
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/Readme.md +1 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +655 -0
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
- endoreg_db/utils/setup_config.py +8 -5
- endoreg_db/utils/storage.py +115 -0
- endoreg_db/utils/validate_endo_roi.py +8 -2
- endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
- endoreg_db/views/__init__.py +85 -183
- endoreg_db/views/ai/__init__.py +8 -0
- endoreg_db/views/ai/label.py +155 -0
- endoreg_db/views/anonymization/media_management.py +202 -166
- endoreg_db/views/anonymization/overview.py +99 -67
- endoreg_db/views/anonymization/validate.py +182 -44
- endoreg_db/views/media/__init__.py +7 -20
- endoreg_db/views/media/pdf_media.py +197 -174
- endoreg_db/views/media/sensitive_metadata.py +193 -138
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/meta/__init__.py +0 -8
- endoreg_db/views/misc/__init__.py +1 -7
- endoreg_db/views/misc/upload_views.py +94 -93
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/report/__init__.py +5 -7
- endoreg_db/views/{pdf → report}/reimport.py +22 -22
- endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +46 -39
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +17 -3
- endoreg_db/views/requirement/lookup_store.py +22 -90
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +23 -24
- endoreg_db/views/video/correction.py +201 -172
- endoreg_db/views/video/reimport.py +1 -1
- endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +77 -40
- endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
- endoreg_db/views/video/video_stream.py +7 -8
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +391 -413
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
- endoreg_db/data/finding/anatomy_colon.yaml +0 -128
- endoreg_db/data/finding/colonoscopy.yaml +0 -40
- endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
- endoreg_db/data/finding/complication.yaml +0 -16
- endoreg_db/data/finding/data.yaml +0 -105
- endoreg_db/data/finding/examination_setting.yaml +0 -16
- endoreg_db/data/finding/medication_related.yaml +0 -18
- endoreg_db/data/finding/outcome.yaml +0 -12
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +0 -95
- endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
- endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
- endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -68
- endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -80
- endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
- endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
- endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
- endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
- endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
- endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
- endoreg_db/data/finding_classification/histology_colo.yaml +0 -51
- endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
- endoreg_db/data/finding_classification/medication_related.yaml +0 -23
- endoreg_db/data/finding_classification/visualized.yaml +0 -33
- endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
- endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
- endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
- endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
- endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
- endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
- endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
- endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
- endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
- endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
- endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
- endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
- endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
- endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
- endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
- endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
- endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
- endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
- endoreg_db/data/requirement/age.yaml +0 -26
- endoreg_db/data/requirement/gender.yaml +0 -25
- endoreg_db/management/commands/init_default_ai_model.py +0 -112
- endoreg_db/management/commands/reset_celery_schedule.py +0 -9
- endoreg_db/management/commands/validate_video.py +0 -204
- endoreg_db/migrations/0002_add_video_correction_models.py +0 -52
- endoreg_db/migrations/0003_add_center_display_name.py +0 -30
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/rule/__init__.py +0 -13
- endoreg_db/models/rule/rule.py +0 -27
- endoreg_db/models/rule/rule_applicator.py +0 -224
- endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
- endoreg_db/models/rule/rule_type.py +0 -20
- endoreg_db/models/rule/ruleset.py +0 -17
- endoreg_db/renames.yml +0 -8
- endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
- endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
- endoreg_db/serializers/_old/video.py +0 -71
- endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
- endoreg_db/serializers/meta/report_meta.py +0 -53
- endoreg_db/serializers/report/__init__.py +0 -9
- endoreg_db/serializers/report/mixins.py +0 -45
- endoreg_db/serializers/report/report.py +0 -105
- endoreg_db/serializers/report/report_list.py +0 -22
- endoreg_db/serializers/report/secure_file_url.py +0 -26
- endoreg_db/serializers/video/video_metadata.py +0 -105
- endoreg_db/services/requirements_object.py +0 -147
- endoreg_db/services/storage_aware_video_processor.py +0 -344
- endoreg_db/urls/files.py +0 -6
- endoreg_db/urls/label_video_segment_validate.py +0 -33
- endoreg_db/urls/label_video_segments.py +0 -46
- endoreg_db/urls/report.py +0 -48
- endoreg_db/urls/video.py +0 -61
- endoreg_db/utils/case_generator/case_generator.py +0 -159
- endoreg_db/utils/case_generator/utils.py +0 -30
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +0 -368
- endoreg_db/views/label/__init__.py +0 -5
- endoreg_db/views/label/label.py +0 -15
- endoreg_db/views/label_video_segment/__init__.py +0 -16
- endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
- endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
- endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
- endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
- endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
- endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
- endoreg_db/views/label_video_segment/validate.py +0 -226
- endoreg_db/views/media/segments.py +0 -71
- endoreg_db/views/meta/available_files_list.py +0 -146
- endoreg_db/views/meta/report_meta.py +0 -53
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -148
- endoreg_db/views/misc/secure_file_serving_view.py +0 -80
- endoreg_db/views/misc/secure_file_url_view.py +0 -84
- endoreg_db/views/misc/secure_url_validate.py +0 -79
- endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
- endoreg_db/views/patient_finding_location/__init__.py +0 -5
- endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
- endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
- endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
- endoreg_db/views/pdf/__init__.py +0 -8
- endoreg_db/views/report/report_list.py +0 -112
- endoreg_db/views/report/report_with_secure_url.py +0 -28
- endoreg_db/views/report/start_examination.py +0 -7
- endoreg_db/views/video/segmentation.py +0 -274
- endoreg_db/views/video/task_status.py +0 -49
- endoreg_db/views/video/timeline.py +0 -46
- endoreg_db/views/video/video_analyze.py +0 -52
- endoreg_db/views.py +0 -0
- /endoreg_db/data/requirement/{colonoscopy_baseline_austria.yaml → old/colonoscopy_baseline_austria.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_cardiovascular.yaml → old/disease_cardiovascular.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_classification_choice_cardiovascular.yaml → old/disease_classification_choice_cardiovascular.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_hepatology.yaml → old/disease_hepatology.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_misc.yaml → old/disease_misc.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_renal.yaml → old/disease_renal.yaml} +0 -0
- /endoreg_db/data/requirement/{endoscopy_bleeding_risk.yaml → old/endoscopy_bleeding_risk.yaml} +0 -0
- /endoreg_db/data/requirement/{event_cardiology.yaml → old/event_cardiology.yaml} +0 -0
- /endoreg_db/data/requirement/{event_requirements.yaml → old/event_requirements.yaml} +0 -0
- /endoreg_db/data/requirement/{finding_colon_polyp.yaml → old/finding_colon_polyp.yaml} +0 -0
- /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
- /endoreg_db/data/requirement/{lab_value.yaml → old/lab_value.yaml} +0 -0
- /endoreg_db/data/requirement/{medication.yaml → old/medication.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{age.yaml → _old/age.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{lab_operators.yaml → _old/lab_operators.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{model_operators.yaml → _old/model_operators.yaml} +0 -0
- /endoreg_db/{models/media/video/refactor_plan.md → import_files/pseudonymization/__init__.py} +0 -0
- /endoreg_db/{models/media/video/video_file_frames.py → import_files/pseudonymization/pseudonymize.py} +0 -0
- /endoreg_db/models/{metadata/frame_ocr_result.py → report/__init__.py} +0 -0
- /endoreg_db/{urls/sensitive_meta.py → models/report/images.py} +0 -0
- /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Iterable
|
|
4
|
+
from endoreg_db.utils.paths import (
|
|
5
|
+
ANONYM_REPORT_DIR,
|
|
6
|
+
ANONYM_VIDEO_DIR,
|
|
7
|
+
IMPORT_REPORT_DIR,
|
|
8
|
+
IMPORT_VIDEO_DIR,
|
|
9
|
+
SENSITIVE_REPORT_DIR,
|
|
10
|
+
SENSITIVE_VIDEO_DIR,
|
|
11
|
+
)
|
|
12
|
+
|
|
13
|
+
dirs = [
|
|
14
|
+
ANONYM_REPORT_DIR,
|
|
15
|
+
ANONYM_VIDEO_DIR,
|
|
16
|
+
IMPORT_REPORT_DIR,
|
|
17
|
+
IMPORT_VIDEO_DIR,
|
|
18
|
+
SENSITIVE_REPORT_DIR,
|
|
19
|
+
SENSITIVE_VIDEO_DIR,
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
logger = logging.getLogger(__name__)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def validate_directories(dirs: Iterable[Path] = dirs) -> bool:
|
|
27
|
+
"""
|
|
28
|
+
Ensure all directories in `dirs` exist.
|
|
29
|
+
Missing directories are created automatically.
|
|
30
|
+
|
|
31
|
+
Args:
|
|
32
|
+
dirs: Iterable of Path objects representing directories.
|
|
33
|
+
|
|
34
|
+
Returns:
|
|
35
|
+
bool: True if all directories exist or were created successfully,
|
|
36
|
+
False if any directory could not be created.
|
|
37
|
+
"""
|
|
38
|
+
ok = True
|
|
39
|
+
|
|
40
|
+
for d in dirs:
|
|
41
|
+
try:
|
|
42
|
+
if not d.exists():
|
|
43
|
+
logger.info(f"Directory missing, creating: {d}")
|
|
44
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
if not d.is_dir():
|
|
47
|
+
logger.error(f"Path exists but is not a directory: {d}")
|
|
48
|
+
ok = False
|
|
49
|
+
|
|
50
|
+
except Exception as e:
|
|
51
|
+
logger.error(f"Failed to create or validate directory '{d}': {e}")
|
|
52
|
+
ok = False
|
|
53
|
+
|
|
54
|
+
return ok
|
|
55
|
+
|
|
56
|
+
validate_directories(dirs)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# storage/__init__.py
|
|
2
|
+
|
|
3
|
+
from . import create_report_file
|
|
4
|
+
from . import create_video_file
|
|
5
|
+
from . import sensitive_meta_storage
|
|
6
|
+
from . import state_management
|
|
7
|
+
from . import storage
|
|
8
|
+
|
|
9
|
+
__all__ = [
|
|
10
|
+
"create_report_file",
|
|
11
|
+
"create_video_file",
|
|
12
|
+
"sensitive_meta_storage",
|
|
13
|
+
"state_management",
|
|
14
|
+
"storage",
|
|
15
|
+
]
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
# endoreg_db/import_files/storage/create_report_file.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
|
|
5
|
+
from endoreg_db.import_files.context.import_context import ImportContext
|
|
6
|
+
from endoreg_db.models.media import RawPdfFile
|
|
7
|
+
from endoreg_db.models.media.processing_history.processing_history import ProcessingHistory
|
|
8
|
+
from endoreg_db.import_files.context.ensure_center import ensure_center
|
|
9
|
+
logger = logging.getLogger(__name__)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def create_or_retrieve_report_file(
|
|
13
|
+
ctx: ImportContext,
|
|
14
|
+
) -> Tuple[RawPdfFile, bool]:
|
|
15
|
+
"""
|
|
16
|
+
Create a new or retrieve an existing RawPdfFile for the given context.
|
|
17
|
+
|
|
18
|
+
Returns:
|
|
19
|
+
pdf : RawPdfFile instance
|
|
20
|
+
needs_processing: True if the pipeline should run for this file
|
|
21
|
+
(no successful history yet for this object/file_type key)
|
|
22
|
+
"""
|
|
23
|
+
file_path = ctx.file_path
|
|
24
|
+
center_name = ctx.center_name
|
|
25
|
+
delete_source = ctx.delete_source
|
|
26
|
+
file_type = ctx.file_type # logical key for history; can be None
|
|
27
|
+
|
|
28
|
+
# 1) Determine the RawPdfFile instance to work with
|
|
29
|
+
if ctx.current_report is not None:
|
|
30
|
+
pdf = ctx.current_report
|
|
31
|
+
logger.info("Using existing RawPdfFile from context: pk=%s", pdf.pk)
|
|
32
|
+
else:
|
|
33
|
+
logger.info("Creating new RawPdfFile from %s for center %s", file_path, center_name)
|
|
34
|
+
|
|
35
|
+
pdf = RawPdfFile.create_from_file_initialized(
|
|
36
|
+
file_path=file_path,
|
|
37
|
+
center_name=center_name,
|
|
38
|
+
delete_source=delete_source,
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
center = ensure_center(pdf, ctx.center_name)
|
|
42
|
+
|
|
43
|
+
logger.info(f"Successfully set up report file from {center.name}")
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
# 3) Check if we already have a successful history entry for this object+file_type
|
|
48
|
+
has_success_history = ProcessingHistory.has_history_for_object(
|
|
49
|
+
obj=pdf,
|
|
50
|
+
success=True,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if has_success_history:
|
|
54
|
+
logger.info(
|
|
55
|
+
"RawPdfFile %s already has successful processing history (file_type=%s) - short-circuiting",
|
|
56
|
+
getattr(pdf, str(pdf.file_path)),
|
|
57
|
+
file_type,
|
|
58
|
+
)
|
|
59
|
+
# No need to run the pipeline again
|
|
60
|
+
return pdf, False
|
|
61
|
+
|
|
62
|
+
# 4) No successful history yet → ensure there is a history entry marking it as "in progress"/failed
|
|
63
|
+
ProcessingHistory.get_or_create_for_object(
|
|
64
|
+
obj=pdf,
|
|
65
|
+
# At this point we haven't finished anonymization; treat as not-success yet.
|
|
66
|
+
success=False,
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
logger.info(
|
|
70
|
+
"Report instance ready for processing: pk=%s, file_type=%s (needs_processing=True)",
|
|
71
|
+
pdf.pk,
|
|
72
|
+
file_type,
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
# Signal to the caller that the anonymization pipeline should run
|
|
76
|
+
return pdf, True
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
# endoreg_db/import_files/storage/create_video_file.py
|
|
2
|
+
import logging
|
|
3
|
+
from typing import Tuple
|
|
4
|
+
|
|
5
|
+
from django.db import IntegrityError
|
|
6
|
+
|
|
7
|
+
from endoreg_db.import_files.context.import_context import ImportContext
|
|
8
|
+
from endoreg_db.import_files.context.ensure_center import ensure_center
|
|
9
|
+
from endoreg_db.models.media import VideoFile
|
|
10
|
+
from endoreg_db.models.media.processing_history.processing_history import ProcessingHistory
|
|
11
|
+
from endoreg_db.utils.hashs import get_video_hash
|
|
12
|
+
|
|
13
|
+
logger = logging.getLogger(__name__)
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def create_or_retrieve_video_file(
|
|
17
|
+
ctx: ImportContext,
|
|
18
|
+
) -> Tuple[VideoFile, bool]:
|
|
19
|
+
"""
|
|
20
|
+
Create a new or retrieve an existing VideoFile for the given context.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
video : VideoFile instance
|
|
24
|
+
retry : whether we are re-processing an existing file
|
|
25
|
+
"""
|
|
26
|
+
file_path = ctx.file_path
|
|
27
|
+
center_name = ctx.center_name
|
|
28
|
+
processor_name = ctx.processor_name
|
|
29
|
+
delete_source = ctx.delete_source
|
|
30
|
+
file_type = ctx.file_type
|
|
31
|
+
|
|
32
|
+
# 1) Determine the VideoFile instance to work with
|
|
33
|
+
if ctx.current_video is not None:
|
|
34
|
+
video = ctx.current_video
|
|
35
|
+
logger.info("Using existing VideoFIle from context: pk =%s", video.pk)
|
|
36
|
+
else:
|
|
37
|
+
logger.info("Creating new VideoFIle from %s for center %s", file_path, center_name)
|
|
38
|
+
video = VideoFile.create_from_file_initialized(
|
|
39
|
+
file_path=file_path,
|
|
40
|
+
center_name=center_name,
|
|
41
|
+
processor_name=processor_name,
|
|
42
|
+
delete_source=delete_source,
|
|
43
|
+
)
|
|
44
|
+
center = ensure_center(video, ctx.center_name)
|
|
45
|
+
|
|
46
|
+
logger.info(f"Successfully set up report file from {center.name}")
|
|
47
|
+
# 3) Check if we already have a successful history entry for this object+file_type
|
|
48
|
+
has_success_history = ProcessingHistory.has_history_for_object(
|
|
49
|
+
obj=video,
|
|
50
|
+
success=True,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
if has_success_history:
|
|
54
|
+
logger.info(
|
|
55
|
+
"VideoFile %s already has successful processing history. (file_type:%s) - short-circuiting",
|
|
56
|
+
getattr(video, str(video.active_file_path)),
|
|
57
|
+
file_type,
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
# 4) No successful history yet → ensure there is a history entry marking it as "in progress"/failed
|
|
61
|
+
ProcessingHistory.get_or_create_for_object(
|
|
62
|
+
obj=video,
|
|
63
|
+
success=False
|
|
64
|
+
)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
logger.info(
|
|
68
|
+
"Video instance ready for processing: pk=%s, file_type=%s (needs_processing=True)",
|
|
69
|
+
video.pk,
|
|
70
|
+
file_type,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
# Signal to the caller that the anonymization pipeline should run
|
|
74
|
+
return video, True
|
|
75
|
+
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# endoreg_db/import_files/storage/sensitive_meta_storage.py
|
|
2
|
+
from typing import Union, Dict, Any
|
|
3
|
+
|
|
4
|
+
from endoreg_db.models.media import RawPdfFile, VideoFile
|
|
5
|
+
from endoreg_db.models.metadata import SensitiveMeta
|
|
6
|
+
from endoreg_db.import_files.processing.sensitive_meta_adapter import (
|
|
7
|
+
normalize_lx_sensitive_meta,
|
|
8
|
+
)
|
|
9
|
+
from endoreg_db.import_files.context.default_sensitive_meta import default_sensitive_meta
|
|
10
|
+
from logging import getLogger
|
|
11
|
+
from lx_anonymizer.sensitive_meta_interface import SensitiveMeta as LxSM
|
|
12
|
+
#
|
|
13
|
+
|
|
14
|
+
logger = getLogger(__name__)
|
|
15
|
+
|
|
16
|
+
def sensitive_meta_storage(
|
|
17
|
+
sensitive_meta: LxSM,
|
|
18
|
+
instance: Union[RawPdfFile, VideoFile],
|
|
19
|
+
) -> bool:
|
|
20
|
+
"""
|
|
21
|
+
Merge lx_anonymizer.SensitiveMeta into instance.sensitive_meta in the DB.
|
|
22
|
+
|
|
23
|
+
- Normalizes the dataclass into the dict format expected by the model logic
|
|
24
|
+
- Delegates to SensitiveMeta.update_from_dict() (which already calls logic.update_*)
|
|
25
|
+
"""
|
|
26
|
+
local_meta = instance.sensitive_meta # Django SensitiveMeta model instance
|
|
27
|
+
if not isinstance(local_meta, SensitiveMeta):
|
|
28
|
+
# If sensitice meta doesnt exist yet, ensure it
|
|
29
|
+
local_meta = default_sensitive_meta(instance)
|
|
30
|
+
assert isinstance(local_meta, SensitiveMeta)
|
|
31
|
+
|
|
32
|
+
try:
|
|
33
|
+
payload = normalize_lx_sensitive_meta(sensitive_meta)
|
|
34
|
+
local_meta.update_from_dict(payload) # this calls your big logic.update_*
|
|
35
|
+
except Exception as e:
|
|
36
|
+
logger.error(f"{e}")
|
|
37
|
+
return False
|
|
38
|
+
|
|
39
|
+
return True
|
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
from endoreg_db.models.media.processing_history.processing_history import ProcessingHistory
|
|
2
|
+
from endoreg_db.utils.paths import IMPORT_REPORT_DIR, IMPORT_VIDEO_DIR, ANONYM_REPORT_DIR, ANONYM_VIDEO_DIR
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
import logging
|
|
6
|
+
import shutil
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
from typing import Optional, Union
|
|
9
|
+
|
|
10
|
+
from django.db import transaction
|
|
11
|
+
|
|
12
|
+
from endoreg_db.import_files.context.import_context import ImportContext
|
|
13
|
+
from endoreg_db.models.media import RawPdfFile, VideoFile
|
|
14
|
+
from endoreg_db.models.state import RawPdfState, VideoState
|
|
15
|
+
from endoreg_db.utils import paths as path_utils
|
|
16
|
+
|
|
17
|
+
logger = logging.getLogger(__name__)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _ensure_instance_state(instance: Union[VideoFile, RawPdfFile]) -> Optional[Union[RawPdfState, VideoState]]:
|
|
21
|
+
"""
|
|
22
|
+
Helper: ensure instance.state exists and return it.
|
|
23
|
+
Mirrors PdfImportService._ensure_state.
|
|
24
|
+
"""
|
|
25
|
+
if isinstance(instance, RawPdfFile):
|
|
26
|
+
state = getattr(instance, "state", None)
|
|
27
|
+
else:
|
|
28
|
+
state = getattr(instance, "state", None)
|
|
29
|
+
|
|
30
|
+
if state is not None:
|
|
31
|
+
return state
|
|
32
|
+
|
|
33
|
+
if hasattr(instance, "get_or_create_state"):
|
|
34
|
+
state = instance.get_or_create_state()
|
|
35
|
+
instance.save()
|
|
36
|
+
return state
|
|
37
|
+
|
|
38
|
+
return None
|
|
39
|
+
|
|
40
|
+
def mark_instance_processing_started(
|
|
41
|
+
instance: Union[RawPdfFile, VideoFile],
|
|
42
|
+
ctx: ImportContext,):
|
|
43
|
+
state = _ensure_instance_state(instance)
|
|
44
|
+
|
|
45
|
+
with transaction.atomic():
|
|
46
|
+
if state is not None:
|
|
47
|
+
|
|
48
|
+
# In the old code, processing_started was set earlier; we guard here
|
|
49
|
+
if not getattr(state, "processing_started", False) and hasattr(
|
|
50
|
+
state, "mark_processing_started"
|
|
51
|
+
):
|
|
52
|
+
state.mark_processing_started()
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def finalize_report_success(
|
|
56
|
+
ctx: ImportContext,
|
|
57
|
+
) -> None:
|
|
58
|
+
"""
|
|
59
|
+
Finalize a successful instance import/anonymization.
|
|
60
|
+
|
|
61
|
+
- Move anonymized Report from temp to canonical anonymized dir
|
|
62
|
+
- Update RawPdfFile.processed_file and .anonymized flag
|
|
63
|
+
- Mark RawPdfState as anonymized + sensitive_meta_processed
|
|
64
|
+
- Mark ProcessingHistory.success = True
|
|
65
|
+
"""
|
|
66
|
+
instance = ctx.current_report
|
|
67
|
+
if not isinstance(instance, RawPdfFile):
|
|
68
|
+
logger.warning("finalize_success called with unsaved instance")
|
|
69
|
+
return
|
|
70
|
+
if not instance.pk:
|
|
71
|
+
logger.warning("finalize_success called with unsaved instance")
|
|
72
|
+
return
|
|
73
|
+
|
|
74
|
+
# --- Move anonymized path into final storage (if we have one) ---
|
|
75
|
+
final_path: Optional[Path] = None
|
|
76
|
+
if ctx.anonymized_path is None:
|
|
77
|
+
logger.warning(
|
|
78
|
+
"No anonymized_path for instance %s (hash=%s); skipping file move.",
|
|
79
|
+
instance.pk,
|
|
80
|
+
getattr(instance, "pdf_hash", None),
|
|
81
|
+
)
|
|
82
|
+
final_path = None
|
|
83
|
+
else:
|
|
84
|
+
pdf_hash = getattr(instance, "pdf_hash", None) or instance.pk
|
|
85
|
+
expected_final_path = ANONYM_REPORT_DIR / f"{pdf_hash}.pdf"
|
|
86
|
+
|
|
87
|
+
src = Path(ctx.anonymized_path)
|
|
88
|
+
|
|
89
|
+
logger.debug(
|
|
90
|
+
"finalize_report_success: src=%s (exists=%s, resolved=%s), expected_final=%s",
|
|
91
|
+
src,
|
|
92
|
+
src.exists(),
|
|
93
|
+
src.resolve(),
|
|
94
|
+
expected_final_path,
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# If anonymizer already wrote to the final path, don't move
|
|
98
|
+
if src.resolve() == expected_final_path.resolve():
|
|
99
|
+
logger.info(
|
|
100
|
+
"Anonymizer output already at final path %s; skipping move.",
|
|
101
|
+
expected_final_path,
|
|
102
|
+
)
|
|
103
|
+
final_path = expected_final_path
|
|
104
|
+
else:
|
|
105
|
+
# Only move if the source actually exists
|
|
106
|
+
if not src.exists():
|
|
107
|
+
logger.error(
|
|
108
|
+
"Anonymized file %s does not exist; cannot move to %s",
|
|
109
|
+
src,
|
|
110
|
+
expected_final_path,
|
|
111
|
+
)
|
|
112
|
+
final_path = None
|
|
113
|
+
else:
|
|
114
|
+
ANONYM_REPORT_DIR.mkdir(parents=True, exist_ok=True)
|
|
115
|
+
if expected_final_path.exists():
|
|
116
|
+
expected_final_path.unlink()
|
|
117
|
+
shutil.move(str(src), str(expected_final_path))
|
|
118
|
+
final_path = expected_final_path
|
|
119
|
+
logger.info("Moved anonymized report to %s", final_path)
|
|
120
|
+
|
|
121
|
+
# Update FileField if we have a final path
|
|
122
|
+
if final_path is not None:
|
|
123
|
+
relative_name = path_utils.to_storage_relative(final_path)
|
|
124
|
+
current_name = getattr(instance.processed_file, "name", None)
|
|
125
|
+
if current_name != relative_name:
|
|
126
|
+
instance.processed_file.name = relative_name
|
|
127
|
+
logger.info("Updated processed_file to %s", relative_name)
|
|
128
|
+
try:
|
|
129
|
+
relative_name = str(ctx.anonymized_path)
|
|
130
|
+
except ValueError:
|
|
131
|
+
# Fallback: absolute path if outside STORAGE_DIR
|
|
132
|
+
relative_name = str(final_path)
|
|
133
|
+
|
|
134
|
+
current_name = getattr(instance.processed_file, "name", None)
|
|
135
|
+
if current_name != relative_name:
|
|
136
|
+
instance.processed_file.name = relative_name
|
|
137
|
+
logger.info(
|
|
138
|
+
"Updated processed_file reference to: %s",
|
|
139
|
+
instance.processed_file.name,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# --- Update RawPdfState flags (mirrors _finalize_processing) ---
|
|
144
|
+
state = _ensure_instance_state(instance)
|
|
145
|
+
|
|
146
|
+
with transaction.atomic():
|
|
147
|
+
if state is not None:
|
|
148
|
+
|
|
149
|
+
# In the old code, processing_started was set earlier; we guard here
|
|
150
|
+
if not getattr(state, "processing_started", False) and hasattr(
|
|
151
|
+
state, "mark_processing_started"
|
|
152
|
+
):
|
|
153
|
+
state.mark_processing_started()
|
|
154
|
+
|
|
155
|
+
# We consider text/meta extraction + anonymization done at this point
|
|
156
|
+
if hasattr(state, "mark_anonymized"):
|
|
157
|
+
state.mark_anonymized()
|
|
158
|
+
if hasattr(state, "mark_sensitive_meta_processed"):
|
|
159
|
+
state.mark_sensitive_meta_processed()
|
|
160
|
+
|
|
161
|
+
state.save()
|
|
162
|
+
|
|
163
|
+
instance.save()
|
|
164
|
+
|
|
165
|
+
# --- ProcessingHistory entry ---
|
|
166
|
+
try:
|
|
167
|
+
with transaction.atomic():
|
|
168
|
+
ProcessingHistory.get_or_create_for_object(
|
|
169
|
+
obj=instance,
|
|
170
|
+
success=True,
|
|
171
|
+
)
|
|
172
|
+
except Exception as e:
|
|
173
|
+
logger.debug(
|
|
174
|
+
"Saving not possible; %s"
|
|
175
|
+
f"skipping ProcessingHistory.{e}",
|
|
176
|
+
instance.pk,
|
|
177
|
+
)
|
|
178
|
+
|
|
179
|
+
def finalize_video_success(
|
|
180
|
+
ctx: ImportContext,
|
|
181
|
+
) -> None:
|
|
182
|
+
"""
|
|
183
|
+
Finalize a successful video import/anonymization.
|
|
184
|
+
|
|
185
|
+
- Move anonymized video from temp to canonical anonymized dir
|
|
186
|
+
- Update VideoFile.processed_file
|
|
187
|
+
- Mark VideoState as anonymized + sensitive_meta_processed
|
|
188
|
+
- Mark ProcessingHistory.success = True
|
|
189
|
+
"""
|
|
190
|
+
instance = ctx.current_video
|
|
191
|
+
if not isinstance(instance, VideoFile):
|
|
192
|
+
logger.warning("finalize_video_success called with non-VideoFile instance")
|
|
193
|
+
return
|
|
194
|
+
if not instance.pk:
|
|
195
|
+
logger.warning("finalize_video_success called with unsaved instance")
|
|
196
|
+
return
|
|
197
|
+
|
|
198
|
+
# --- Move anonymized path into final storage (if we have one) ---
|
|
199
|
+
final_path: Optional[Path] = None
|
|
200
|
+
|
|
201
|
+
if ctx.anonymized_path is None:
|
|
202
|
+
logger.warning(
|
|
203
|
+
"No anonymized_path for video instance %s (hash=%s); skipping file move.",
|
|
204
|
+
instance.pk,
|
|
205
|
+
getattr(instance, "video_hash", None),
|
|
206
|
+
)
|
|
207
|
+
else:
|
|
208
|
+
# Use a stable naming convention: <video_hash>.mp4
|
|
209
|
+
video_hash = getattr(instance, "video_hash", None) or instance.pk
|
|
210
|
+
expected_final_path = ANONYM_VIDEO_DIR / f"{video_hash}.mp4"
|
|
211
|
+
|
|
212
|
+
src = Path(ctx.anonymized_path)
|
|
213
|
+
|
|
214
|
+
logger.debug(
|
|
215
|
+
"finalize_video_success: src=%s (exists=%s, resolved=%s), expected_final=%s",
|
|
216
|
+
src,
|
|
217
|
+
src.exists(),
|
|
218
|
+
src.resolve(),
|
|
219
|
+
expected_final_path,
|
|
220
|
+
)
|
|
221
|
+
|
|
222
|
+
# If anonymizer already wrote to the final path, don't move
|
|
223
|
+
try:
|
|
224
|
+
same_target = src.resolve() == expected_final_path.resolve()
|
|
225
|
+
except FileNotFoundError:
|
|
226
|
+
# src might not exist anymore
|
|
227
|
+
same_target = False
|
|
228
|
+
|
|
229
|
+
if same_target:
|
|
230
|
+
logger.info(
|
|
231
|
+
"Anonymizer output already at final video path %s; skipping move.",
|
|
232
|
+
expected_final_path,
|
|
233
|
+
)
|
|
234
|
+
final_path = expected_final_path
|
|
235
|
+
else:
|
|
236
|
+
if not src.exists():
|
|
237
|
+
logger.error(
|
|
238
|
+
"Anonymized video %s does not exist; cannot move to %s",
|
|
239
|
+
src,
|
|
240
|
+
expected_final_path,
|
|
241
|
+
)
|
|
242
|
+
final_path = None
|
|
243
|
+
else:
|
|
244
|
+
ANONYM_VIDEO_DIR.mkdir(parents=True, exist_ok=True)
|
|
245
|
+
if expected_final_path.exists():
|
|
246
|
+
try:
|
|
247
|
+
expected_final_path.unlink()
|
|
248
|
+
except Exception as e:
|
|
249
|
+
logger.warning(
|
|
250
|
+
"Could not remove existing anonymized video %s: %s",
|
|
251
|
+
expected_final_path,
|
|
252
|
+
e,
|
|
253
|
+
)
|
|
254
|
+
shutil.move(str(src), str(expected_final_path))
|
|
255
|
+
final_path = expected_final_path
|
|
256
|
+
logger.info("Moved anonymized video to %s", final_path)
|
|
257
|
+
|
|
258
|
+
# Update FileField if we have a final path
|
|
259
|
+
if final_path is not None:
|
|
260
|
+
relative_name = path_utils.to_storage_relative(final_path)
|
|
261
|
+
current_name = getattr(instance.processed_file, "name", None)
|
|
262
|
+
if current_name != relative_name:
|
|
263
|
+
instance.processed_file.name = relative_name
|
|
264
|
+
logger.info("Updated video processed_file to %s", relative_name)
|
|
265
|
+
|
|
266
|
+
# --- Update VideoState flags (mirrors report) ---
|
|
267
|
+
state = _ensure_instance_state(instance)
|
|
268
|
+
|
|
269
|
+
with transaction.atomic():
|
|
270
|
+
if state is not None:
|
|
271
|
+
if not getattr(state, "processing_started", False) and hasattr(
|
|
272
|
+
state, "mark_processing_started"
|
|
273
|
+
):
|
|
274
|
+
state.mark_processing_started()
|
|
275
|
+
|
|
276
|
+
if hasattr(state, "mark_anonymized"):
|
|
277
|
+
state.mark_anonymized()
|
|
278
|
+
if hasattr(state, "mark_sensitive_meta_processed"):
|
|
279
|
+
state.mark_sensitive_meta_processed()
|
|
280
|
+
|
|
281
|
+
state.save()
|
|
282
|
+
|
|
283
|
+
instance.save()
|
|
284
|
+
|
|
285
|
+
# --- ProcessingHistory entry ---
|
|
286
|
+
try:
|
|
287
|
+
with transaction.atomic():
|
|
288
|
+
ProcessingHistory.get_or_create_for_object(
|
|
289
|
+
obj=instance,
|
|
290
|
+
success=True,
|
|
291
|
+
)
|
|
292
|
+
except Exception as e:
|
|
293
|
+
logger.debug(
|
|
294
|
+
"Saving not possible for video %s; skipping ProcessingHistory. Error: %s",
|
|
295
|
+
instance.pk,
|
|
296
|
+
e,
|
|
297
|
+
)
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
def finalize_failure(
|
|
301
|
+
ctx: ImportContext,
|
|
302
|
+
) -> None:
|
|
303
|
+
"""
|
|
304
|
+
Finalize a failed instance import/anonymization.
|
|
305
|
+
|
|
306
|
+
- Reset RawPdfState flags to "not processed"
|
|
307
|
+
- Mark ProcessingHistory.success = False
|
|
308
|
+
"""
|
|
309
|
+
if ctx.instance is None:
|
|
310
|
+
if isinstance(ctx.current_report, RawPdfFile):
|
|
311
|
+
ctx.instance = ctx.current_report
|
|
312
|
+
elif isinstance(ctx.current_video, VideoFile):
|
|
313
|
+
ctx.instance = ctx.current_video
|
|
314
|
+
else:
|
|
315
|
+
raise Exception
|
|
316
|
+
# Reset state flags similar to _mark_processing_incomplete / _cleanup_on_error
|
|
317
|
+
state = _ensure_instance_state(ctx.instance)
|
|
318
|
+
|
|
319
|
+
if state is not None:
|
|
320
|
+
try:
|
|
321
|
+
state.mark_processing_not_started()
|
|
322
|
+
|
|
323
|
+
state.save()
|
|
324
|
+
logger.info(
|
|
325
|
+
"Reset instance state for failed processing (instance pk=%s)",
|
|
326
|
+
ctx.instance.pk,
|
|
327
|
+
)
|
|
328
|
+
except Exception as e:
|
|
329
|
+
logger.warning(
|
|
330
|
+
"Failed to reset State for instance %s: %s",
|
|
331
|
+
ctx.instance.pk,
|
|
332
|
+
e,
|
|
333
|
+
)
|
|
334
|
+
|
|
335
|
+
try:
|
|
336
|
+
delete_associated_files(ctx)
|
|
337
|
+
except Exception as e:
|
|
338
|
+
logger.warning(f"There might be files remaining. {e}")
|
|
339
|
+
|
|
340
|
+
# History entry with success=False
|
|
341
|
+
if ctx.file_hash:
|
|
342
|
+
ProcessingHistory.get_or_create_for_object(
|
|
343
|
+
obj=ctx.instance,
|
|
344
|
+
success=False,
|
|
345
|
+
)
|
|
346
|
+
else:
|
|
347
|
+
logger.debug(
|
|
348
|
+
"No file_hash in context for instance %s when finalizing failure; "
|
|
349
|
+
"skipping ProcessingHistory.",
|
|
350
|
+
ctx.instance.pk,
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
logger.error(
|
|
354
|
+
"Report processing failed for %s",
|
|
355
|
+
ctx.file_path,
|
|
356
|
+
)
|
|
357
|
+
|
|
358
|
+
def delete_associated_files(ctx:ImportContext):
|
|
359
|
+
try:
|
|
360
|
+
assert isinstance(ctx.original_path, Path)
|
|
361
|
+
except AssertionError as e:
|
|
362
|
+
logger.warning(f"Original file restored from sensitive copy. This is because the original file is gone. {e}")
|
|
363
|
+
if ctx.file_type =="video":
|
|
364
|
+
if isinstance(ctx.sensitive_path, Path):
|
|
365
|
+
try:
|
|
366
|
+
ctx.original_path = Path(shutil.copy2(ctx.sensitive_path, IMPORT_VIDEO_DIR))
|
|
367
|
+
except Exception as e:
|
|
368
|
+
logger.error(f"Error during safety copy: {e} Original file could not be restored!")
|
|
369
|
+
raise
|
|
370
|
+
elif ctx.file_type == "report":
|
|
371
|
+
if isinstance(ctx.sensitive_path, Path):
|
|
372
|
+
try:
|
|
373
|
+
ctx.original_path = Path(shutil.copy2(ctx.sensitive_path, IMPORT_REPORT_DIR))
|
|
374
|
+
except Exception as e:
|
|
375
|
+
logger.error(f"Error during safety copy: {e} Original file could not be restored!")
|
|
376
|
+
raise
|
|
377
|
+
|
|
378
|
+
if isinstance(ctx.anonymized_path, Path):
|
|
379
|
+
try:
|
|
380
|
+
Path.unlink(ctx.anonymized_path)
|
|
381
|
+
assert(ctx.anonymized_path is not Path)
|
|
382
|
+
|
|
383
|
+
except Exception as e:
|
|
384
|
+
logger.error(f"Error when unlinking anonymized path. {e}")
|
|
385
|
+
raise
|
|
386
|
+
|
|
387
|
+
ctx.anonymized_path = None
|
|
388
|
+
|
|
389
|
+
if isinstance(ctx.sensitive_path, Path):
|
|
390
|
+
try:
|
|
391
|
+
Path.unlink(ctx.sensitive_path)
|
|
392
|
+
assert(ctx.sensitive_path is not Path)
|
|
393
|
+
|
|
394
|
+
except Exception as e:
|
|
395
|
+
logger.error(f"Error when unlinking anonymized path. {e}")
|
|
396
|
+
|
|
397
|
+
raise
|
|
398
|
+
|
|
399
|
+
ctx.sensitive_path = None
|
|
400
|
+
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from endoreg_db.models.media.video.create_from_file import atomic_copy_with_fallback, atomic_move_with_fallback
|
|
4
|
+
logger = logging.getLogger(__name__)
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def ensure_dir(path: Path) -> None:
|
|
8
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def create_sensitive_copy(src: Path, sensitive_root: Path) -> Path:
|
|
12
|
+
"""
|
|
13
|
+
Create a sensitive copy of `src` in `sensitive_root`.
|
|
14
|
+
|
|
15
|
+
Returns:
|
|
16
|
+
Path to the sensitive copy.
|
|
17
|
+
"""
|
|
18
|
+
ensure_dir(sensitive_root)
|
|
19
|
+
dest = sensitive_root / src.name
|
|
20
|
+
logger.info("Creating sensitive copy: %s -> %s", src, dest)
|
|
21
|
+
atomic_copy_with_fallback(src, dest)
|
|
22
|
+
return dest
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def move_to_anonymized(temp_path: Path, anonymized_root: Path) -> Path:
|
|
26
|
+
"""
|
|
27
|
+
Move a (temporary) anonymized file into the canonical anonymized root.
|
|
28
|
+
|
|
29
|
+
Returns:
|
|
30
|
+
Final path inside anonymized_root.
|
|
31
|
+
"""
|
|
32
|
+
ensure_dir(anonymized_root)
|
|
33
|
+
dest = anonymized_root / temp_path.name
|
|
34
|
+
logger.info("Moving anonymized file: %s -> %s", temp_path, dest)
|
|
35
|
+
atomic_move_with_fallback(temp_path, dest)
|
|
36
|
+
return dest
|