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
|
@@ -4,35 +4,32 @@
|
|
|
4
4
|
# Class contains classmethod to create object from pdf file
|
|
5
5
|
# objects contains methods to extract text, extract metadata from text and anonymize text from pdf file uzing agl_report_reader.ReportReader class
|
|
6
6
|
# ------------------------------------------------------------------------------
|
|
7
|
-
import
|
|
8
|
-
from typing import TYPE_CHECKING, Optional, Union
|
|
7
|
+
from typing import TYPE_CHECKING, Optional, cast
|
|
9
8
|
|
|
10
9
|
from django.core.exceptions import ValidationError
|
|
11
10
|
from django.core.files import File
|
|
12
11
|
from django.core.validators import FileExtensionValidator
|
|
13
12
|
from django.db import models
|
|
14
|
-
from numpy import isin # Import Django File
|
|
15
13
|
|
|
16
14
|
from endoreg_db.utils.file_operations import get_uuid_filename
|
|
17
15
|
from endoreg_db.utils.hashs import get_pdf_hash
|
|
18
|
-
from endoreg_db.utils.paths import
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
from endoreg_db.utils.paths import (
|
|
17
|
+
ANONYM_REPORT_DIR,
|
|
18
|
+
IMPORT_REPORT_DIR,
|
|
19
|
+
SENSITIVE_REPORT_DIR,
|
|
20
|
+
)
|
|
21
|
+
from endoreg_db.utils.storage import (
|
|
22
|
+
delete_field_file,
|
|
23
|
+
ensure_local_file,
|
|
24
|
+
file_exists,
|
|
25
|
+
save_local_file,
|
|
26
|
+
)
|
|
22
27
|
|
|
23
28
|
if TYPE_CHECKING:
|
|
24
|
-
from
|
|
25
|
-
Examiner,
|
|
26
|
-
Patient,
|
|
27
|
-
)
|
|
29
|
+
from django.db.models.fields.files import FieldFile
|
|
28
30
|
|
|
29
|
-
from
|
|
30
|
-
from ...medical.patient import PatientExamination
|
|
31
|
-
from ...metadata.pdf_meta import PdfType
|
|
32
|
-
from ...state import RawPdfState
|
|
33
|
-
from .report_file import AnonymExaminationReport
|
|
31
|
+
from endoreg_db.models.state import RawPdfState
|
|
34
32
|
|
|
35
|
-
# setup logging to pdf_import.log
|
|
36
33
|
import logging
|
|
37
34
|
from pathlib import Path
|
|
38
35
|
|
|
@@ -42,6 +39,7 @@ logger = logging.getLogger("raw_pdf")
|
|
|
42
39
|
|
|
43
40
|
|
|
44
41
|
class RawPdfFile(models.Model):
|
|
42
|
+
objects = models.Manager()
|
|
45
43
|
# Fields from AbstractPdfFile
|
|
46
44
|
pdf_hash = models.CharField(max_length=255, unique=True)
|
|
47
45
|
pdf_type = models.ForeignKey(
|
|
@@ -55,50 +53,91 @@ class RawPdfFile(models.Model):
|
|
|
55
53
|
on_delete=models.SET_NULL,
|
|
56
54
|
blank=True,
|
|
57
55
|
null=True,
|
|
58
|
-
)
|
|
56
|
+
)
|
|
59
57
|
examination = models.ForeignKey(
|
|
60
58
|
"PatientExamination",
|
|
61
59
|
on_delete=models.SET_NULL,
|
|
62
60
|
blank=True,
|
|
63
61
|
null=True,
|
|
64
62
|
related_name="raw_pdf_files",
|
|
65
|
-
)
|
|
63
|
+
)
|
|
66
64
|
examiner = models.ForeignKey(
|
|
67
65
|
"Examiner",
|
|
68
66
|
on_delete=models.SET_NULL,
|
|
69
67
|
blank=True,
|
|
70
68
|
null=True,
|
|
71
|
-
)
|
|
69
|
+
)
|
|
72
70
|
text = models.TextField(blank=True, null=True)
|
|
73
71
|
date_created = models.DateTimeField(auto_now_add=True)
|
|
74
72
|
date_modified = models.DateTimeField(auto_now=True)
|
|
75
|
-
anonymized = models.BooleanField(
|
|
76
|
-
default=False, help_text="True if the PDF has been anonymized."
|
|
77
|
-
)
|
|
78
73
|
|
|
79
|
-
# Fields specific to RawPdfFile (keeping existing related_names)
|
|
80
74
|
file = models.FileField(
|
|
81
|
-
# Use the relative path from the specific
|
|
82
|
-
upload_to=
|
|
75
|
+
# Use the relative path from the specific REPORT_DIR
|
|
76
|
+
upload_to=SENSITIVE_REPORT_DIR.name,
|
|
83
77
|
validators=[FileExtensionValidator(allowed_extensions=["pdf"])],
|
|
84
|
-
)
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
upload_to=PDF_DIR.name,
|
|
78
|
+
)
|
|
79
|
+
processed_file = models.FileField(
|
|
80
|
+
upload_to=ANONYM_REPORT_DIR.name,
|
|
88
81
|
validators=[FileExtensionValidator(allowed_extensions=["pdf"])],
|
|
89
82
|
null=True,
|
|
90
83
|
blank=True,
|
|
91
|
-
)
|
|
92
|
-
|
|
84
|
+
)
|
|
93
85
|
state = models.OneToOneField(
|
|
94
86
|
"RawPdfState",
|
|
95
87
|
on_delete=models.SET_NULL,
|
|
96
88
|
blank=True,
|
|
97
89
|
null=True,
|
|
98
90
|
related_name="raw_pdf_file",
|
|
99
|
-
)
|
|
91
|
+
)
|
|
92
|
+
patient = models.ForeignKey(
|
|
93
|
+
"Patient",
|
|
94
|
+
on_delete=models.SET_NULL,
|
|
95
|
+
blank=True,
|
|
96
|
+
null=True,
|
|
97
|
+
related_name="raw_pdf_files",
|
|
98
|
+
)
|
|
99
|
+
sensitive_meta = models.ForeignKey(
|
|
100
|
+
"SensitiveMeta",
|
|
101
|
+
on_delete=models.SET_NULL,
|
|
102
|
+
related_name="raw_pdf_files",
|
|
103
|
+
null=True,
|
|
104
|
+
blank=True,
|
|
105
|
+
)
|
|
106
|
+
state_report_processing_required = models.BooleanField(default=True)
|
|
107
|
+
state_report_processed = models.BooleanField(default=False)
|
|
108
|
+
raw_meta = models.JSONField(blank=True, null=True)
|
|
109
|
+
anonym_examination_report = models.OneToOneField(
|
|
110
|
+
"AnonymExaminationReport",
|
|
111
|
+
on_delete=models.SET_NULL,
|
|
112
|
+
blank=True,
|
|
113
|
+
null=True,
|
|
114
|
+
related_name="raw_pdf_file",
|
|
115
|
+
)
|
|
116
|
+
anonymized_text = models.TextField(blank=True, null=True)
|
|
100
117
|
|
|
101
|
-
|
|
118
|
+
# Type hinting is needed, improve and use correct django types
|
|
119
|
+
if TYPE_CHECKING:
|
|
120
|
+
from endoreg_db.models import (
|
|
121
|
+
AnonymExaminationReport,
|
|
122
|
+
Center,
|
|
123
|
+
Examiner,
|
|
124
|
+
Patient,
|
|
125
|
+
PatientExamination,
|
|
126
|
+
RawPdfState,
|
|
127
|
+
SensitiveMeta,
|
|
128
|
+
)
|
|
129
|
+
|
|
130
|
+
center: models.ForeignKey["Center | None"]
|
|
131
|
+
examination: models.ForeignKey["PatientExamination | None"]
|
|
132
|
+
examiner: models.ForeignKey["Examiner | None"]
|
|
133
|
+
state: models.ForeignKey["RawPdfState | None"]
|
|
134
|
+
patient: models.ForeignKey["Patient | None"]
|
|
135
|
+
sensitive_meta: models.ForeignKey["SensitiveMeta | None"]
|
|
136
|
+
anonym_examination_report: models.OneToOneField[
|
|
137
|
+
"AnonymExaminationReport | None"
|
|
138
|
+
]
|
|
139
|
+
file = cast(FieldFile, file)
|
|
140
|
+
processed_file = cast(FieldFile, processed_file)
|
|
102
141
|
|
|
103
142
|
@property
|
|
104
143
|
def uuid(self):
|
|
@@ -113,7 +152,7 @@ class RawPdfFile(models.Model):
|
|
|
113
152
|
@property
|
|
114
153
|
def file_path(self) -> Path | None:
|
|
115
154
|
"""
|
|
116
|
-
Returns the file path of the stored
|
|
155
|
+
Returns the file path of the stored report file if available; otherwise, returns None.
|
|
117
156
|
"""
|
|
118
157
|
from django.db.models.fields.files import FieldFile
|
|
119
158
|
|
|
@@ -128,35 +167,41 @@ class RawPdfFile(models.Model):
|
|
|
128
167
|
|
|
129
168
|
def set_file_path(self, file_path: Path):
|
|
130
169
|
"""
|
|
131
|
-
Sets the file path of the stored
|
|
170
|
+
Sets the file path of the stored report file.
|
|
132
171
|
"""
|
|
133
|
-
|
|
172
|
+
if not file_path.exists():
|
|
173
|
+
raise FileNotFoundError(f"File path does not exist: {file_path}")
|
|
174
|
+
|
|
175
|
+
save_local_file(self.file, file_path, name=file_path.name, save=False)
|
|
134
176
|
self.save(update_fields=["file"])
|
|
135
177
|
|
|
136
178
|
@property
|
|
137
179
|
def anonymized_file_path(self) -> Path | None:
|
|
138
180
|
"""
|
|
139
|
-
Returns the file path of the anonymized
|
|
181
|
+
Returns the file path of the anonymized report file if available; otherwise, returns None.
|
|
140
182
|
"""
|
|
141
|
-
if self.
|
|
183
|
+
if self.processed_file and self.processed_file.name:
|
|
142
184
|
try:
|
|
143
|
-
return Path(self.
|
|
185
|
+
return Path(self.processed_file.path)
|
|
144
186
|
except (ValueError, AttributeError, NotImplementedError):
|
|
145
187
|
return None
|
|
146
188
|
return None
|
|
147
189
|
|
|
148
190
|
def set_anonymized_file_path(self, file_path: Path):
|
|
149
191
|
"""
|
|
150
|
-
Sets the file path of the anonymized
|
|
192
|
+
Sets the file path of the anonymized report file.
|
|
151
193
|
"""
|
|
152
|
-
|
|
153
|
-
|
|
194
|
+
if not file_path.exists():
|
|
195
|
+
raise FileNotFoundError(f"File path does not exist: {file_path}")
|
|
196
|
+
|
|
197
|
+
save_local_file(self.processed_file, file_path, name=file_path.name, save=False)
|
|
198
|
+
self.save(update_fields=["processed_file"])
|
|
154
199
|
|
|
155
200
|
def get_raw_file_path(self) -> Optional[Path]:
|
|
156
201
|
"""
|
|
157
|
-
Get the path to the raw
|
|
202
|
+
Get the path to the raw report file, searching common locations.
|
|
158
203
|
|
|
159
|
-
This method attempts to find the original raw
|
|
204
|
+
This method attempts to find the original raw report file by checking:
|
|
160
205
|
1. Direct hash-based path in raw_pdfs/
|
|
161
206
|
2. Scanning raw_pdfs/ directory for files matching the hash
|
|
162
207
|
3. Checking the file field if it exists
|
|
@@ -171,17 +216,17 @@ class RawPdfFile(models.Model):
|
|
|
171
216
|
try:
|
|
172
217
|
file_path = Path(self.file.path)
|
|
173
218
|
if file_path.exists():
|
|
174
|
-
logger.debug(f"Found raw
|
|
219
|
+
logger.debug(f"Found raw report via file field: {file_path}")
|
|
175
220
|
return file_path
|
|
176
221
|
except (ValueError, AttributeError, NotImplementedError):
|
|
177
222
|
pass
|
|
178
223
|
|
|
179
224
|
# Define potential raw directories
|
|
180
225
|
raw_dirs = [
|
|
181
|
-
|
|
182
|
-
Path(settings.BASE_DIR) / "data" / "
|
|
226
|
+
SENSITIVE_REPORT_DIR, # Files might be in sensitive dir
|
|
227
|
+
Path(settings.BASE_DIR) / "data" / "temporary_reports",
|
|
183
228
|
Path(settings.BASE_DIR) / "data" / "pdfs" / "raw",
|
|
184
|
-
|
|
229
|
+
IMPORT_REPORT_DIR, # General report directory
|
|
185
230
|
]
|
|
186
231
|
|
|
187
232
|
# Check direct hash-based name in each directory
|
|
@@ -191,7 +236,7 @@ class RawPdfFile(models.Model):
|
|
|
191
236
|
|
|
192
237
|
hash_path = raw_dir / f"{self.pdf_hash}.pdf"
|
|
193
238
|
if hash_path.exists():
|
|
194
|
-
logger.debug(f"Found raw
|
|
239
|
+
logger.debug(f"Found raw report at: {hash_path}")
|
|
195
240
|
return hash_path
|
|
196
241
|
|
|
197
242
|
# Scan directories for matching hash
|
|
@@ -203,19 +248,19 @@ class RawPdfFile(models.Model):
|
|
|
203
248
|
try:
|
|
204
249
|
file_hash = get_pdf_hash(file_path)
|
|
205
250
|
if file_hash == self.pdf_hash:
|
|
206
|
-
logger.debug(f"Found matching
|
|
251
|
+
logger.debug(f"Found matching report by hash: {file_path}")
|
|
207
252
|
return file_path
|
|
208
253
|
except Exception as e:
|
|
209
254
|
logger.debug(f"Error checking {file_path}: {e}")
|
|
210
255
|
continue
|
|
211
256
|
|
|
212
|
-
logger.warning(f"No raw file found for
|
|
257
|
+
logger.warning(f"No raw file found for report hash: {self.pdf_hash}")
|
|
213
258
|
return None
|
|
214
259
|
|
|
215
260
|
@property
|
|
216
261
|
def file_url(self):
|
|
217
262
|
"""
|
|
218
|
-
Returns the URL of the stored
|
|
263
|
+
Returns the URL of the stored report file if available; otherwise, returns None.
|
|
219
264
|
"""
|
|
220
265
|
try:
|
|
221
266
|
return self.file.url if self.file and self.file.name else None
|
|
@@ -225,61 +270,20 @@ class RawPdfFile(models.Model):
|
|
|
225
270
|
@property
|
|
226
271
|
def anonymized_file_url(self):
|
|
227
272
|
"""
|
|
228
|
-
Returns the URL of the stored
|
|
273
|
+
Returns the URL of the stored report file if available; otherwise, returns None.
|
|
229
274
|
"""
|
|
230
275
|
try:
|
|
231
276
|
return (
|
|
232
|
-
self.
|
|
233
|
-
if self.
|
|
277
|
+
self.processed_file.url
|
|
278
|
+
if self.processed_file and self.processed_file.name
|
|
234
279
|
else None
|
|
235
280
|
)
|
|
236
281
|
except (ValueError, AttributeError):
|
|
237
282
|
return None
|
|
238
283
|
|
|
239
|
-
patient = models.ForeignKey(
|
|
240
|
-
"Patient",
|
|
241
|
-
on_delete=models.SET_NULL,
|
|
242
|
-
blank=True,
|
|
243
|
-
null=True,
|
|
244
|
-
related_name="raw_pdf_files",
|
|
245
|
-
) # type: ignore
|
|
246
|
-
sensitive_meta = models.ForeignKey(
|
|
247
|
-
"SensitiveMeta",
|
|
248
|
-
on_delete=models.SET_NULL,
|
|
249
|
-
related_name="raw_pdf_files",
|
|
250
|
-
null=True,
|
|
251
|
-
blank=True,
|
|
252
|
-
) # type: ignore
|
|
253
|
-
state_report_processing_required = models.BooleanField(default=True)
|
|
254
|
-
state_report_processed = models.BooleanField(default=False)
|
|
255
|
-
raw_meta = models.JSONField(blank=True, null=True)
|
|
256
|
-
anonym_examination_report = models.OneToOneField(
|
|
257
|
-
"AnonymExaminationReport",
|
|
258
|
-
on_delete=models.SET_NULL,
|
|
259
|
-
blank=True,
|
|
260
|
-
null=True,
|
|
261
|
-
related_name="raw_pdf_file",
|
|
262
|
-
) # type: ignore
|
|
263
|
-
anonymized_text = models.TextField(blank=True, null=True)
|
|
264
|
-
|
|
265
|
-
# Type hinting is needed, improve and use correct django types
|
|
266
|
-
if TYPE_CHECKING:
|
|
267
|
-
file: Optional[Union[models.FieldFile, models.FileField]]
|
|
268
|
-
anonymized_file: Optional[Union[models.FieldFile, models.FileField]]
|
|
269
|
-
pdf_type: Optional[models.ForeignKey]
|
|
270
|
-
examination: Optional[models.ForeignKey["PatientExamination"]]
|
|
271
|
-
examiner: Optional[models.ForeignKey["Examiner"]]
|
|
272
|
-
patient: Optional[models.ForeignKey["Patient"]]
|
|
273
|
-
center: Optional[models.ForeignKey["Center"]]
|
|
274
|
-
anonym_examination_report: Optional[
|
|
275
|
-
models.OneToOneField["AnonymExaminationReport"]
|
|
276
|
-
]
|
|
277
|
-
sensitive_meta: Optional[models.ForeignKey["SensitiveMeta"]]
|
|
278
|
-
state: Optional[models.ForeignKey["RawPdfState"]]
|
|
279
|
-
|
|
280
284
|
def __str__(self):
|
|
281
285
|
"""
|
|
282
|
-
Return a string representation of the RawPdfFile, including its
|
|
286
|
+
Return a string representation of the RawPdfFile, including its report hash, type, and center.
|
|
283
287
|
"""
|
|
284
288
|
str_repr = f"{self.pdf_hash} ({self.pdf_type}, {self.center})"
|
|
285
289
|
return str_repr
|
|
@@ -288,34 +292,54 @@ class RawPdfFile(models.Model):
|
|
|
288
292
|
"""
|
|
289
293
|
Deletes the RawPdfFile instance from the database and removes the associated file from storage if it exists.
|
|
290
294
|
|
|
291
|
-
This method ensures that the physical
|
|
295
|
+
This method ensures that the physical report file is deleted from the file system after the database record is removed. Logs warnings or errors if the file cannot be found or deleted.
|
|
292
296
|
"""
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
try:
|
|
305
|
-
if self.anonymized_file_path:
|
|
306
|
-
os.remove(Path(self.anonymized_file_path))
|
|
307
|
-
logger.info(
|
|
308
|
-
"Anonymized file removed: %s", self.anonymized_file.name
|
|
309
|
-
)
|
|
310
|
-
except OSError as e:
|
|
311
|
-
logger.error(
|
|
312
|
-
"Error removing anonymized file %s: %s",
|
|
313
|
-
self.anonymized_file.name,
|
|
314
|
-
e,
|
|
315
|
-
)
|
|
297
|
+
primary_name = self.file.name if self.file and self.file.name else None
|
|
298
|
+
anonymized_name = (
|
|
299
|
+
self.processed_file.name
|
|
300
|
+
if self.processed_file and self.processed_file.name
|
|
301
|
+
else None
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
if delete_field_file(self.file, missing_ok=True, save=False):
|
|
305
|
+
logger.info("Original file removed from storage: %s", primary_name)
|
|
306
|
+
if delete_field_file(self.processed_file, missing_ok=True, save=False):
|
|
307
|
+
logger.info("Anonymized file removed from storage: %s", anonymized_name)
|
|
316
308
|
|
|
317
309
|
super().delete(*args, **kwargs)
|
|
318
310
|
|
|
311
|
+
# --- Convenience state/meta helpers used in tests and admin workflows ---
|
|
312
|
+
|
|
313
|
+
def mark_sensitive_meta_processed(self, *, save: bool = True) -> "RawPdfFile":
|
|
314
|
+
"""
|
|
315
|
+
Mark this video's processing state as having its sensitive meta fully processed.
|
|
316
|
+
This proxies to the related VideoState and persists by default.
|
|
317
|
+
"""
|
|
318
|
+
sm = self.sensitive_meta
|
|
319
|
+
from endoreg_db.models.metadata.sensitive_meta import SensitiveMeta
|
|
320
|
+
|
|
321
|
+
if not isinstance(sm, SensitiveMeta):
|
|
322
|
+
raise AttributeError()
|
|
323
|
+
state = self.get_or_create_state()
|
|
324
|
+
state.mark_sensitive_meta_processed(save=save)
|
|
325
|
+
return self
|
|
326
|
+
|
|
327
|
+
def mark_sensitive_meta_verified(self) -> "RawPdfFile":
|
|
328
|
+
"""
|
|
329
|
+
Mark the associated SensitiveMeta as verified by setting both DOB and names as verified.
|
|
330
|
+
Ensures the SensitiveMeta and its state exist.
|
|
331
|
+
"""
|
|
332
|
+
sm = self.sensitive_meta
|
|
333
|
+
# Use SensitiveMeta methods to update underlying SensitiveMetaState
|
|
334
|
+
from endoreg_db.models.metadata.sensitive_meta import SensitiveMeta
|
|
335
|
+
|
|
336
|
+
if not isinstance(sm, SensitiveMeta):
|
|
337
|
+
raise AttributeError()
|
|
338
|
+
|
|
339
|
+
sm.mark_dob_verified()
|
|
340
|
+
sm.mark_names_verified()
|
|
341
|
+
return self
|
|
342
|
+
|
|
319
343
|
def validate_metadata_annotation(
|
|
320
344
|
self, extracted_data_dict: Optional[dict] = None
|
|
321
345
|
) -> bool:
|
|
@@ -326,16 +350,17 @@ class RawPdfFile(models.Model):
|
|
|
326
350
|
It also ensures the video file is properly saved after the metadata update.
|
|
327
351
|
"""
|
|
328
352
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
return False
|
|
353
|
+
self.mark_sensitive_meta_processed()
|
|
354
|
+
self.mark_sensitive_meta_verified()
|
|
332
355
|
|
|
333
356
|
if not extracted_data_dict:
|
|
334
357
|
logger.error("No extracted data provided for validation.")
|
|
335
358
|
return False
|
|
336
359
|
|
|
337
|
-
|
|
338
|
-
|
|
360
|
+
if extracted_data_dict:
|
|
361
|
+
self.sensitive_meta.update_from_dict(extracted_data_dict)
|
|
362
|
+
else:
|
|
363
|
+
return False
|
|
339
364
|
|
|
340
365
|
# Save the sensitive meta to ensure changes are persisted
|
|
341
366
|
self.sensitive_meta.save()
|
|
@@ -343,25 +368,22 @@ class RawPdfFile(models.Model):
|
|
|
343
368
|
# Save the RawPdfFile instance to ensure all changes are saved
|
|
344
369
|
self.save()
|
|
345
370
|
|
|
346
|
-
logger.info(
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
try:
|
|
350
|
-
os.unlink(self.file_path) # Delete the original file if it exists
|
|
351
|
-
except OSError as e:
|
|
352
|
-
logger.error(f"Error removing original file {self.file_path}: {e}")
|
|
371
|
+
logger.info(
|
|
372
|
+
f"Metadata for report {self.pk} validated and updated successfully."
|
|
373
|
+
)
|
|
353
374
|
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
f"Error removing anonymized file {self.anonymized_file_path}: {e}"
|
|
360
|
-
)
|
|
375
|
+
deleted_original = delete_field_file(self.file, missing_ok=True, save=False)
|
|
376
|
+
deleted_anonymized = delete_field_file(
|
|
377
|
+
self.processed_file, missing_ok=True, save=False
|
|
378
|
+
)
|
|
379
|
+
self.get_or_create_state().mark_anonymization_validated()
|
|
361
380
|
|
|
362
|
-
|
|
381
|
+
if deleted_original or deleted_anonymized:
|
|
382
|
+
self.save(
|
|
383
|
+
update_fields=["file", "processed_file"]
|
|
384
|
+
) # Persist cleared fields
|
|
363
385
|
|
|
364
|
-
logger.info(f"Files for
|
|
386
|
+
logger.info(f"Files for report {self.pk} deleted successfully.")
|
|
365
387
|
return True
|
|
366
388
|
|
|
367
389
|
@classmethod
|
|
@@ -375,8 +397,8 @@ class RawPdfFile(models.Model):
|
|
|
375
397
|
Creates a RawPdfFile instance from a file and center name, ensuring an associated RawPdfState exists.
|
|
376
398
|
|
|
377
399
|
Parameters:
|
|
378
|
-
file_path (Path): Path to the source
|
|
379
|
-
center_name (str): Name of the center to associate with the
|
|
400
|
+
file_path (Path): Path to the source report file.
|
|
401
|
+
center_name (str): Name of the center to associate with the report.
|
|
380
402
|
delete_source (bool): Whether to delete the source file after processing. Defaults to True.
|
|
381
403
|
|
|
382
404
|
Returns:
|
|
@@ -400,12 +422,12 @@ class RawPdfFile(models.Model):
|
|
|
400
422
|
delete_source=True,
|
|
401
423
|
):
|
|
402
424
|
"""
|
|
403
|
-
Creates or retrieves a RawPdfFile instance from a given
|
|
425
|
+
Creates or retrieves a RawPdfFile instance from a given report file path and center name.
|
|
404
426
|
|
|
405
|
-
If a RawPdfFile with the same
|
|
427
|
+
If a RawPdfFile with the same report hash already exists, verifies the file exists in storage and restores it if missing. Otherwise, creates a new RawPdfFile, assigns the file, and saves it to storage. Optionally deletes the source file after processing.
|
|
406
428
|
|
|
407
429
|
Parameters:
|
|
408
|
-
file_path (Path): Path to the source
|
|
430
|
+
file_path (Path): Path to the source report file.
|
|
409
431
|
center_name (str): Name of the center to associate with the file.
|
|
410
432
|
save (bool, optional): Deprecated; saving occurs internally.
|
|
411
433
|
delete_source (bool, optional): Whether to delete the source file after processing (default True).
|
|
@@ -416,7 +438,7 @@ class RawPdfFile(models.Model):
|
|
|
416
438
|
Raises:
|
|
417
439
|
FileNotFoundError: If the source file does not exist.
|
|
418
440
|
Center.DoesNotExist: If the specified center is not found.
|
|
419
|
-
ValueError: If the
|
|
441
|
+
ValueError: If the report hash cannot be calculated.
|
|
420
442
|
IOError: If the file fails to save to storage.
|
|
421
443
|
"""
|
|
422
444
|
from endoreg_db.models.administration import Center
|
|
@@ -460,7 +482,7 @@ class RawPdfFile(models.Model):
|
|
|
460
482
|
django_file = File(
|
|
461
483
|
f, name=Path(_file.name).name
|
|
462
484
|
) # Use existing name if possible
|
|
463
|
-
existing_pdf_file.file = django_file
|
|
485
|
+
existing_pdf_file.file = django_file
|
|
464
486
|
existing_pdf_file.save(
|
|
465
487
|
update_fields=["file"]
|
|
466
488
|
) # Only update file field
|
|
@@ -554,64 +576,42 @@ class RawPdfFile(models.Model):
|
|
|
554
576
|
# Ensure hash is calculated before the first save if possible and not already set
|
|
555
577
|
# This is primarily a fallback if instance created manually without using create_from_file
|
|
556
578
|
"""
|
|
557
|
-
Saves the RawPdfFile instance, ensuring the
|
|
579
|
+
Saves the RawPdfFile instance, ensuring the report hash is set and related fields are derived from metadata.
|
|
558
580
|
|
|
559
|
-
If the
|
|
581
|
+
If the report hash is missing, attempts to calculate it from the file before saving. Validates that the file has a `.pdf` extension. If related fields such as patient, examination, center, or examiner are unset but available in the associated sensitive metadata, they are populated accordingly before saving.
|
|
560
582
|
"""
|
|
561
583
|
if not self.pk and not self.pdf_hash and self.file:
|
|
562
584
|
try:
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
self.pdf_hash = get_pdf_hash(
|
|
570
|
-
file_path
|
|
571
|
-
) # Assuming get_pdf_hash can handle file obj
|
|
572
|
-
self.file.seek(0) # Reset position
|
|
573
|
-
self.file.close() # Close after reading
|
|
574
|
-
logger.info(f"Calculated hash during pre-save for {self.file.name}")
|
|
575
|
-
except Exception as e:
|
|
585
|
+
with ensure_local_file(self.file) as local_path:
|
|
586
|
+
self.pdf_hash = get_pdf_hash(local_path)
|
|
587
|
+
logger.info(
|
|
588
|
+
"Calculated hash during pre-save for %s", self.file.name
|
|
589
|
+
)
|
|
590
|
+
except Exception as exc:
|
|
576
591
|
logger.warning(
|
|
577
592
|
"Could not calculate hash before initial save for %s: %s",
|
|
578
593
|
self.file.name,
|
|
579
|
-
|
|
594
|
+
exc,
|
|
580
595
|
)
|
|
581
|
-
# Ensure file is closed if opened
|
|
582
|
-
if hasattr(self.file, "closed") and not self.file.closed:
|
|
583
|
-
self.file.close()
|
|
584
596
|
|
|
585
597
|
if self.file and not self.file.name.endswith(".pdf"):
|
|
586
|
-
raise ValidationError("Only
|
|
598
|
+
raise ValidationError("Only report files are allowed")
|
|
587
599
|
|
|
588
600
|
# If hash is still missing after potential creation logic (e.g., direct instantiation)
|
|
589
601
|
# and the file exists in storage, try calculating it from storage path.
|
|
590
602
|
# This is less ideal as it requires the file to be saved first.
|
|
591
|
-
if (
|
|
592
|
-
not self.pdf_hash
|
|
593
|
-
and self.pk
|
|
594
|
-
and self.file
|
|
595
|
-
and self.file.storage.exists(self.file.name)
|
|
596
|
-
):
|
|
603
|
+
if not self.pdf_hash and self.pk and self.file and file_exists(self.file):
|
|
597
604
|
try:
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
with self.file.storage.open(self.file.name, "rb") as f:
|
|
605
|
-
self.pdf_hash = get_pdf_hash(
|
|
606
|
-
file_path
|
|
607
|
-
) # Assuming get_pdf_hash handles file obj
|
|
608
|
-
# No need to save again just for hash unless update_fields is used carefully
|
|
609
|
-
# Let the main super().save() handle saving the hash if it changed
|
|
610
|
-
except Exception as e:
|
|
605
|
+
with ensure_local_file(self.file) as local_path:
|
|
606
|
+
logger.warning(
|
|
607
|
+
"Hash missing for saved file %s. Recalculating.", self.file.name
|
|
608
|
+
)
|
|
609
|
+
self.pdf_hash = get_pdf_hash(local_path)
|
|
610
|
+
except Exception as exc:
|
|
611
611
|
logger.error(
|
|
612
612
|
"Could not calculate hash during save for existing file %s: %s",
|
|
613
613
|
self.file.name,
|
|
614
|
-
|
|
614
|
+
exc,
|
|
615
615
|
)
|
|
616
616
|
|
|
617
617
|
# Derive related fields from sensitive_meta if available
|
|
@@ -651,7 +651,7 @@ class RawPdfFile(models.Model):
|
|
|
651
651
|
# This method might still be useful if called explicitly, but create_from_file now handles restoration
|
|
652
652
|
# Ensure fallback_file is a Path object.
|
|
653
653
|
"""
|
|
654
|
-
Checks if the stored
|
|
654
|
+
Checks if the stored report file exists in storage and attempts to restore it from a fallback file path if missing.
|
|
655
655
|
|
|
656
656
|
Parameters:
|
|
657
657
|
fallback_file: Path or string representing the fallback file location to restore from if the stored file is missing.
|
|
@@ -750,8 +750,8 @@ class RawPdfFile(models.Model):
|
|
|
750
750
|
return settings_dict
|
|
751
751
|
|
|
752
752
|
@staticmethod
|
|
753
|
-
def
|
|
753
|
+
def get_pdf_by_pk(pk: int) -> "RawPdfFile":
|
|
754
754
|
try:
|
|
755
|
-
return RawPdfFile.objects.get(pk=
|
|
755
|
+
return RawPdfFile.objects.get(pk=pk)
|
|
756
756
|
except RawPdfFile.DoesNotExist:
|
|
757
|
-
raise ValueError(f"
|
|
757
|
+
raise ValueError(f"report with ID {pdf_id} does not exist.")
|