endoreg-db 0.8.8.0__py3-none-any.whl → 0.8.9.2__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/data/__init__.py +22 -8
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +0 -1
- 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/{colonoscopy_bowel_preparation.yaml → 02_colonoscopy_baseline.yaml} +35 -20
- 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/{_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml → finding_classification_choice/02_colonoscopy_generic.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
- endoreg_db/data/{_examples/finding_classification_choice/colonoscopy_location.yaml → finding_classification_choice/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/{_examples/finding_classification_choice/colon_lesion_paris.yaml → finding_classification_choice/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_operator/new_operators.yaml +36 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +0 -2
- endoreg_db/data/requirement_set/90_coloreg.yaml +20 -8
- endoreg_db/exceptions.py +0 -1
- endoreg_db/forms/examination_form.py +1 -1
- endoreg_db/helpers/data_loader.py +124 -52
- 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 +496 -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/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/pseudonymization/pseudonymize.py +0 -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/import_report.py +130 -65
- endoreg_db/management/commands/import_video_with_classification.py +1 -1
- 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_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 +14 -20
- 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/start_filewatcher.py +46 -37
- endoreg_db/management/commands/validate_video_files.py +1 -5
- endoreg_db/migrations/0001_initial.py +1360 -1812
- endoreg_db/models/administration/person/patient/patient.py +72 -46
- endoreg_db/models/label/__init__.py +2 -2
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +18 -26
- endoreg_db/models/label/label_video_segment/label_video_segment.py +23 -1
- endoreg_db/models/media/pdf/raw_pdf.py +136 -64
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +34 -10
- 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/create_from_file.py +101 -31
- endoreg_db/models/media/video/video_file.py +125 -105
- endoreg_db/models/media/video/video_file_io.py +31 -26
- endoreg_db/models/medical/contraindication/README.md +1 -0
- endoreg_db/models/medical/examination/examination.py +28 -8
- endoreg_db/models/medical/examination/examination_indication.py +13 -79
- endoreg_db/models/medical/examination/examination_time.py +8 -3
- endoreg_db/models/medical/finding/finding.py +5 -12
- endoreg_db/models/medical/finding/finding_classification.py +18 -37
- endoreg_db/models/medical/finding/finding_intervention.py +7 -9
- endoreg_db/models/medical/hardware/endoscope.py +6 -0
- endoreg_db/models/medical/patient/medication_examples.py +5 -1
- endoreg_db/models/medical/patient/patient_finding.py +1 -1
- endoreg_db/models/metadata/pdf_meta.py +22 -10
- endoreg_db/models/metadata/sensitive_meta.py +3 -0
- endoreg_db/models/metadata/sensitive_meta_logic.py +200 -124
- endoreg_db/models/other/information_source.py +27 -6
- endoreg_db/models/report/__init__.py +0 -0
- endoreg_db/models/report/images.py +0 -0
- endoreg_db/models/report/report.py +6 -0
- endoreg_db/models/requirement/requirement.py +59 -399
- endoreg_db/models/requirement/requirement_operator.py +86 -98
- endoreg_db/models/state/audit_ledger.py +4 -5
- endoreg_db/models/state/raw_pdf.py +69 -30
- endoreg_db/models/state/video.py +65 -49
- endoreg_db/models/upload_job.py +33 -9
- endoreg_db/models/utils.py +27 -23
- endoreg_db/queries/__init__.py +3 -1
- endoreg_db/schemas/examination_evaluation.py +1 -1
- endoreg_db/serializers/__init__.py +2 -8
- endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
- endoreg_db/serializers/meta/__init__.py +1 -6
- 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/video/video_file_list.py +65 -34
- endoreg_db/services/__old/pdf_import.py +1487 -0
- endoreg_db/services/__old/video_import.py +1306 -0
- endoreg_db/services/anonymization.py +63 -26
- endoreg_db/services/lookup_service.py +28 -28
- endoreg_db/services/lookup_store.py +2 -2
- endoreg_db/services/pdf_import.py +0 -1480
- endoreg_db/services/report_import.py +10 -0
- endoreg_db/services/video_import.py +6 -1165
- endoreg_db/tasks/upload_tasks.py +79 -70
- endoreg_db/tasks/video_ingest.py +8 -4
- endoreg_db/urls/__init__.py +0 -14
- endoreg_db/urls/ai.py +32 -0
- endoreg_db/urls/media.py +21 -24
- endoreg_db/utils/dataloader.py +87 -57
- endoreg_db/utils/paths.py +110 -46
- endoreg_db/utils/pipelines/Readme.md +1 -1
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
- endoreg_db/utils/video/ffmpeg_wrapper.py +217 -52
- endoreg_db/views/__init__.py +85 -173
- endoreg_db/views/ai/__init__.py +8 -0
- endoreg_db/views/ai/label.py +155 -0
- endoreg_db/views/anonymization/media_management.py +8 -7
- endoreg_db/views/anonymization/overview.py +97 -68
- endoreg_db/views/anonymization/validate.py +25 -21
- endoreg_db/views/media/__init__.py +5 -20
- endoreg_db/views/media/pdf_media.py +109 -65
- endoreg_db/views/media/sensitive_metadata.py +163 -148
- 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/report/__init__.py +7 -0
- endoreg_db/views/{pdf → report}/reimport.py +45 -24
- endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +40 -32
- endoreg_db/views/requirement/lookup_store.py +22 -90
- endoreg_db/views/video/__init__.py +23 -22
- 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} +75 -37
- 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.8.0.dist-info → endoreg_db-0.8.9.2.dist-info}/METADATA +2 -2
- {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.9.2.dist-info}/RECORD +217 -335
- {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.9.2.dist-info}/WHEEL +1 -1
- endoreg_db/data/_examples/disease.yaml +0 -55
- endoreg_db/data/_examples/disease_classification.yaml +0 -13
- endoreg_db/data/_examples/disease_classification_choice.yaml +0 -62
- endoreg_db/data/_examples/event.yaml +0 -64
- endoreg_db/data/_examples/examination.yaml +0 -72
- endoreg_db/data/_examples/finding/anatomy_colon.yaml +0 -128
- endoreg_db/data/_examples/finding/colonoscopy.yaml +0 -40
- endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +0 -56
- endoreg_db/data/_examples/finding/complication.yaml +0 -16
- endoreg_db/data/_examples/finding/data.yaml +0 -105
- endoreg_db/data/_examples/finding/examination_setting.yaml +0 -16
- endoreg_db/data/_examples/finding/medication_related.yaml +0 -18
- endoreg_db/data/_examples/finding/outcome.yaml +0 -12
- endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +0 -68
- endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +0 -22
- endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +0 -25
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +0 -68
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
- endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +0 -80
- endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +0 -21
- endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +0 -20
- endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +0 -26
- endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +0 -22
- endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +0 -53
- endoreg_db/data/_examples/finding_classification/complication_generic.yaml +0 -25
- endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +0 -40
- endoreg_db/data/_examples/finding_classification/histology_colo.yaml +0 -51
- endoreg_db/data/_examples/finding_classification/intervention_required.yaml +0 -26
- endoreg_db/data/_examples/finding_classification/medication_related.yaml +0 -23
- endoreg_db/data/_examples/finding_classification/visualized.yaml +0 -33
- endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +0 -78
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +0 -17
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +0 -14
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +0 -82
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/histology.yaml +0 -24
- endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +0 -20
- endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +0 -19
- endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +0 -11
- endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +0 -48
- endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +0 -43
- endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
- endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +0 -128
- endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +0 -32
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +0 -9
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +0 -36
- endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +0 -15
- endoreg_db/data/_examples/finding_type/data.yaml +0 -43
- endoreg_db/data/_examples/requirement/age.yaml +0 -26
- endoreg_db/data/_examples/requirement/gender.yaml +0 -25
- endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +0 -48
- endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +0 -57
- endoreg_db/data/_examples/requirement_set/endoscopy_bleeding_risk.yaml +0 -52
- endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
- 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_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 -38
- endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -49
- 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 -43
- 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_paris.yaml +0 -57
- 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_location.yaml +0 -229
- endoreg_db/data/finding_classification_choice/colonoscopy_not_complete_reason.yaml +0 -19
- 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/colonoscopy_baseline_austria.yaml +0 -45
- endoreg_db/data/requirement/disease_cardiovascular.yaml +0 -79
- endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +0 -41
- endoreg_db/data/requirement/disease_hepatology.yaml +0 -12
- endoreg_db/data/requirement/disease_misc.yaml +0 -12
- endoreg_db/data/requirement/disease_renal.yaml +0 -96
- endoreg_db/data/requirement/endoscopy_bleeding_risk.yaml +0 -59
- endoreg_db/data/requirement/event_cardiology.yaml +0 -251
- endoreg_db/data/requirement/event_requirements.yaml +0 -145
- endoreg_db/data/requirement/finding_colon_polyp.yaml +0 -50
- endoreg_db/data/requirement/gender.yaml +0 -25
- endoreg_db/data/requirement/lab_value.yaml +0 -441
- endoreg_db/data/requirement/medication.yaml +0 -93
- endoreg_db/data/requirement_operator/age.yaml +0 -13
- endoreg_db/data/requirement_operator/lab_operators.yaml +0 -129
- endoreg_db/data/requirement_operator/model_operators.yaml +0 -96
- 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_requirementset_depends_on.py +0 -18
- endoreg_db/migrations/_old/0001_initial.py +0 -1857
- endoreg_db/migrations/_old/0002_add_video_correction_models.py +0 -52
- endoreg_db/migrations/_old/0003_add_center_display_name.py +0 -30
- endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +0 -68
- endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +0 -77
- endoreg_db/migrations/_old/0005_merge_20251111_1003.py +0 -14
- endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +0 -68
- endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +0 -89
- endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +0 -27
- endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +0 -21
- 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/services/requirements_object.py +0 -147
- endoreg_db/services/storage_aware_video_processor.py +0 -370
- 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/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 -85
- 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/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/data/requirement/{colon_polyp_intervention.yaml → old/colon_polyp_intervention.yaml} +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/colonoscopy_baseline_austria.yaml +0 -0
- /endoreg_db/data/requirement/{coloreg_colon_polyp.yaml → old/coloreg_colon_polyp.yaml} +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_cardiovascular.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_classification_choice_cardiovascular.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_hepatology.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_misc.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_renal.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/endoscopy_bleeding_risk.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/event_cardiology.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/event_requirements.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/finding_colon_polyp.yaml +0 -0
- /endoreg_db/{urls/sensitive_meta.py → data/requirement/old/gender.yaml} +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/lab_value.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/medication.yaml +0 -0
- /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/age.yaml +0 -0
- /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/lab_operators.yaml +0 -0
- /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/model_operators.yaml +0 -0
- /endoreg_db/{views/pdf/pdf_stream_views.py → import_files/pseudonymization/__init__.py} +0 -0
- /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
- /endoreg_db/utils/requirement_operator_logic/{model_evaluators.py → _old/model_evaluators.py} +0 -0
- {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.9.2.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,28 +1,27 @@
|
|
|
1
1
|
import random
|
|
2
|
+
import shutil
|
|
3
|
+
from datetime import date
|
|
4
|
+
from logging import getLogger
|
|
5
|
+
from pathlib import Path
|
|
2
6
|
from typing import Optional
|
|
7
|
+
|
|
8
|
+
from django.conf import settings # Import settings
|
|
9
|
+
from django.core.files.storage import default_storage # Import default storage
|
|
10
|
+
from django.db.models.fields.files import FieldFile
|
|
11
|
+
|
|
3
12
|
from endoreg_db.models import (
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
13
|
+
AiModel,
|
|
14
|
+
Center,
|
|
15
|
+
EndoscopyProcessor,
|
|
7
16
|
Examination,
|
|
8
17
|
ExaminationIndication,
|
|
9
|
-
|
|
10
|
-
EndoscopyProcessor,
|
|
11
|
-
ModelMeta,
|
|
18
|
+
Gender,
|
|
12
19
|
InformationSource,
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
from datetime import date
|
|
17
|
-
import shutil
|
|
18
|
-
from pathlib import Path
|
|
19
|
-
from django.conf import settings # Import settings
|
|
20
|
-
from django.core.files.storage import default_storage # Import default storage
|
|
21
|
-
from django.db.models.fields.files import FieldFile
|
|
22
|
-
|
|
23
|
-
from endoreg_db.utils import (
|
|
24
|
-
create_mock_patient_name,
|
|
20
|
+
ModelMeta,
|
|
21
|
+
Patient,
|
|
22
|
+
RawPdfFile,
|
|
25
23
|
)
|
|
24
|
+
from endoreg_db.utils import create_mock_patient_name
|
|
26
25
|
|
|
27
26
|
logger = getLogger("default_objects")
|
|
28
27
|
|
|
@@ -32,7 +31,7 @@ DEFAULT_ENDOSCOPE_NAME = "test_endoscope"
|
|
|
32
31
|
DEFAULT_ENDOSCOPY_PROCESSOR_NAME = "olympus_cv_1500"
|
|
33
32
|
|
|
34
33
|
DEFAULT_EGD_PATH = Path("tests/assets/lux-gastro-report.pdf")
|
|
35
|
-
DEFAULT_GENDERS = ["male","female","unknown"]
|
|
34
|
+
DEFAULT_GENDERS = ["male", "female", "unknown"]
|
|
36
35
|
DEFAULT_EXAMINATIONS = ["colonoscopy"]
|
|
37
36
|
DEFAULT_INDICATIONS = [
|
|
38
37
|
"colonoscopy",
|
|
@@ -51,60 +50,70 @@ DEFAULT_PATIENT_LAST_NAME = "TestLast"
|
|
|
51
50
|
DEFAULT_PATIENT_GENDER_NAME = "female"
|
|
52
51
|
DEFAULT_PATIENT_BIRTH_DATE = date(1970, 1, 1)
|
|
53
52
|
|
|
53
|
+
|
|
54
54
|
def get_information_source_prediction():
|
|
55
55
|
"""
|
|
56
56
|
Retrieves the InformationSource object with the name "prediction".
|
|
57
|
-
|
|
57
|
+
|
|
58
58
|
Loads information source data if needed and returns the corresponding InformationSource instance. Raises a ValueError if the object is not found or is not an InformationSource.
|
|
59
59
|
"""
|
|
60
60
|
from .data_loader import load_information_source
|
|
61
|
+
|
|
61
62
|
load_information_source()
|
|
62
63
|
source = InformationSource.objects.get(name="prediction")
|
|
63
64
|
if not isinstance(source, InformationSource):
|
|
64
65
|
raise ValueError("No InformationSource found in the database.")
|
|
65
66
|
return source
|
|
66
67
|
|
|
67
|
-
def get_latest_segmentation_model(model_name:str=DEFAULT_SEGMENTATION_MODEL_NAME) -> ModelMeta:
|
|
68
68
|
|
|
69
|
+
def get_latest_segmentation_model(
|
|
70
|
+
model_name: str = DEFAULT_SEGMENTATION_MODEL_NAME,
|
|
71
|
+
) -> ModelMeta:
|
|
69
72
|
"""
|
|
70
73
|
Retrieves the latest metadata for a segmentation model by name.
|
|
71
|
-
|
|
74
|
+
|
|
72
75
|
Loads necessary data and returns the most recent ModelMeta instance for the specified AI model. If no metadata exists, attempts to initialize it automatically; if initialization fails, raises a ValueError with instructions for manual setup.
|
|
73
|
-
|
|
76
|
+
|
|
74
77
|
Args:
|
|
75
78
|
model_name: The name of the segmentation model to retrieve.
|
|
76
|
-
|
|
79
|
+
|
|
77
80
|
Returns:
|
|
78
81
|
The latest ModelMeta instance for the specified model.
|
|
79
|
-
|
|
82
|
+
|
|
80
83
|
Raises:
|
|
81
84
|
ValueError: If the AI model does not exist, or if model metadata cannot be found or initialized.
|
|
82
85
|
"""
|
|
83
86
|
from .data_loader import (
|
|
84
|
-
load_center_data,
|
|
85
|
-
load_ai_model_label_data,
|
|
86
87
|
load_ai_model_data,
|
|
88
|
+
load_ai_model_label_data,
|
|
89
|
+
load_center_data,
|
|
87
90
|
)
|
|
91
|
+
|
|
88
92
|
load_center_data()
|
|
89
93
|
load_ai_model_label_data()
|
|
90
94
|
load_ai_model_data()
|
|
91
|
-
|
|
95
|
+
|
|
92
96
|
try:
|
|
93
97
|
ai_model = AiModel.objects.get(name=model_name)
|
|
94
98
|
except AiModel.DoesNotExist:
|
|
95
|
-
raise ValueError(
|
|
96
|
-
|
|
99
|
+
raise ValueError(
|
|
100
|
+
f"AI model '{model_name}' not found. Run 'python manage.py load_ai_model_data' first."
|
|
101
|
+
)
|
|
102
|
+
|
|
97
103
|
try:
|
|
98
104
|
latest_meta = ai_model.get_latest_version()
|
|
99
105
|
return latest_meta
|
|
100
106
|
except ValueError as e:
|
|
101
107
|
if "No model metadata found" in str(e):
|
|
102
|
-
logger.warning(
|
|
103
|
-
|
|
108
|
+
logger.warning(
|
|
109
|
+
f"No ModelMeta found for {model_name}. Attempting to initialize default model metadata..."
|
|
110
|
+
)
|
|
111
|
+
|
|
104
112
|
# Try to initialize the default model metadata
|
|
105
113
|
try:
|
|
106
114
|
from django.core.management import call_command
|
|
107
|
-
|
|
115
|
+
|
|
116
|
+
call_command("init_default_ai_model")
|
|
108
117
|
# Try again after initialization
|
|
109
118
|
latest_meta = ai_model.get_latest_version()
|
|
110
119
|
return latest_meta
|
|
@@ -116,17 +125,18 @@ def get_latest_segmentation_model(model_name:str=DEFAULT_SEGMENTATION_MODEL_NAME
|
|
|
116
125
|
) from e
|
|
117
126
|
else:
|
|
118
127
|
raise
|
|
119
|
-
|
|
128
|
+
|
|
120
129
|
|
|
121
130
|
def get_default_gender() -> Gender:
|
|
122
131
|
"""
|
|
123
132
|
Retrieves the Gender object representing the default "unknown" gender.
|
|
124
|
-
|
|
133
|
+
|
|
125
134
|
Returns:
|
|
126
135
|
The Gender instance with the name "unknown".
|
|
127
136
|
"""
|
|
128
137
|
return Gender.objects.get(name=DEFAULT_GENDER)
|
|
129
138
|
|
|
139
|
+
|
|
130
140
|
def get_gender_m_or_f() -> Gender:
|
|
131
141
|
"""
|
|
132
142
|
Returns a randomly selected Gender object representing either male or female.
|
|
@@ -134,36 +144,40 @@ def get_gender_m_or_f() -> Gender:
|
|
|
134
144
|
gender_name = random.choice(["male", "female"])
|
|
135
145
|
return Gender.objects.get(name=gender_name)
|
|
136
146
|
|
|
147
|
+
|
|
137
148
|
def get_random_gender() -> Gender:
|
|
138
149
|
"""
|
|
139
150
|
Returns a randomly selected Gender object from the available default genders.
|
|
140
151
|
"""
|
|
141
152
|
gender_name = random.choice(DEFAULT_GENDERS)
|
|
142
|
-
return Gender.objects.get(name=gender_name)
|
|
153
|
+
return Gender.objects.get(name=gender_name) # Fetch and return the Gender object
|
|
154
|
+
|
|
143
155
|
|
|
144
156
|
def get_default_processor() -> EndoscopyProcessor:
|
|
145
157
|
"""
|
|
146
158
|
Retrieves the default EndoscopyProcessor object by its predefined name.
|
|
147
|
-
|
|
159
|
+
|
|
148
160
|
Raises:
|
|
149
161
|
ValueError: If no EndoscopyProcessor with the default name exists.
|
|
150
|
-
|
|
162
|
+
|
|
151
163
|
Returns:
|
|
152
164
|
The EndoscopyProcessor instance with the default name.
|
|
153
165
|
"""
|
|
154
166
|
processor = EndoscopyProcessor.objects.get(name=DEFAULT_ENDOSCOPY_PROCESSOR_NAME)
|
|
155
167
|
if not isinstance(processor, EndoscopyProcessor):
|
|
156
|
-
raise ValueError(
|
|
168
|
+
raise ValueError(
|
|
169
|
+
f"No EndoscopyProcessor found with name {DEFAULT_ENDOSCOPY_PROCESSOR_NAME}"
|
|
170
|
+
)
|
|
157
171
|
return processor
|
|
158
172
|
|
|
159
173
|
|
|
160
174
|
def get_default_center() -> Center:
|
|
161
175
|
"""
|
|
162
176
|
Retrieves the default Center object with the predefined name.
|
|
163
|
-
|
|
177
|
+
|
|
164
178
|
Raises:
|
|
165
179
|
ValueError: If no Center with the default name exists.
|
|
166
|
-
|
|
180
|
+
|
|
167
181
|
Returns:
|
|
168
182
|
The Center instance with the default name.
|
|
169
183
|
"""
|
|
@@ -172,9 +186,10 @@ def get_default_center() -> Center:
|
|
|
172
186
|
)
|
|
173
187
|
if not isinstance(center, Center):
|
|
174
188
|
raise ValueError(f"No Center found with name {DEFAULT_CENTER_NAME}")
|
|
175
|
-
|
|
189
|
+
|
|
176
190
|
return center
|
|
177
191
|
|
|
192
|
+
|
|
178
193
|
def generate_patient(**kwargs) -> Patient:
|
|
179
194
|
"""Create a Patient with deterministic defaults unless ``randomize=True`` is supplied."""
|
|
180
195
|
|
|
@@ -193,9 +208,14 @@ def generate_patient(**kwargs) -> Patient:
|
|
|
193
208
|
last_name = kwargs.get("last_name")
|
|
194
209
|
if first_name is None or last_name is None:
|
|
195
210
|
if randomize:
|
|
196
|
-
generated_first, generated_last = create_mock_patient_name(
|
|
211
|
+
generated_first, generated_last = create_mock_patient_name(
|
|
212
|
+
gender=gender.name
|
|
213
|
+
)
|
|
197
214
|
else:
|
|
198
|
-
generated_first, generated_last =
|
|
215
|
+
generated_first, generated_last = (
|
|
216
|
+
DEFAULT_PATIENT_FIRST_NAME,
|
|
217
|
+
DEFAULT_PATIENT_LAST_NAME,
|
|
218
|
+
)
|
|
199
219
|
first_name = first_name or generated_first
|
|
200
220
|
last_name = last_name or generated_last
|
|
201
221
|
|
|
@@ -217,16 +237,17 @@ def generate_patient(**kwargs) -> Patient:
|
|
|
217
237
|
first_name=first_name,
|
|
218
238
|
last_name=last_name,
|
|
219
239
|
dob=dob,
|
|
220
|
-
center
|
|
221
|
-
gender
|
|
240
|
+
center=center,
|
|
241
|
+
gender=gender,
|
|
222
242
|
)
|
|
223
243
|
|
|
224
244
|
return patient
|
|
225
|
-
|
|
245
|
+
|
|
246
|
+
|
|
226
247
|
def get_random_default_examination():
|
|
227
248
|
"""
|
|
228
249
|
Retrieves a random Examination object from the default examination names.
|
|
229
|
-
|
|
250
|
+
|
|
230
251
|
Returns:
|
|
231
252
|
Examination: A randomly selected Examination instance from the defaults.
|
|
232
253
|
"""
|
|
@@ -235,29 +256,33 @@ def get_random_default_examination():
|
|
|
235
256
|
examination = Examination.objects.get(name=examination_name)
|
|
236
257
|
return examination
|
|
237
258
|
|
|
259
|
+
|
|
238
260
|
def get_random_default_examination_indication():
|
|
239
261
|
"""
|
|
240
262
|
Returns a random ExaminationIndication object from the default indications list.
|
|
241
|
-
|
|
263
|
+
|
|
242
264
|
Selects a random indication name from the predefined defaults and retrieves the corresponding ExaminationIndication instance from the database.
|
|
243
265
|
"""
|
|
244
266
|
examination_indication = random.choice(DEFAULT_INDICATIONS)
|
|
245
267
|
all_examination_indications = ExaminationIndication.objects.all()
|
|
246
268
|
try:
|
|
247
|
-
examination_indication = ExaminationIndication.objects.get(
|
|
248
|
-
|
|
269
|
+
examination_indication = ExaminationIndication.objects.get(
|
|
270
|
+
name=examination_indication
|
|
271
|
+
)
|
|
272
|
+
|
|
249
273
|
except Exception as e:
|
|
250
274
|
logger.info(f"examination_indication: {examination_indication}")
|
|
251
275
|
logger.info(f"all_examination_indications: {all_examination_indications}")
|
|
252
276
|
raise e
|
|
253
277
|
return examination_indication
|
|
254
278
|
|
|
279
|
+
|
|
255
280
|
def get_default_egd_pdf():
|
|
256
281
|
"""
|
|
257
|
-
Creates and processes a default EGD
|
|
258
|
-
|
|
259
|
-
This function copies a default EGD
|
|
260
|
-
|
|
282
|
+
Creates and processes a default EGD report file for testing purposes.
|
|
283
|
+
|
|
284
|
+
This function copies a default EGD report to a temporary location, creates a RawPdfFile instance from it, processes the file to generate associated metadata, and ensures cleanup of the temporary file. The resulting RawPdfFile instance is returned for use in tests.
|
|
285
|
+
|
|
261
286
|
Returns:
|
|
262
287
|
RawPdfFile: The created and processed RawPdfFile instance.
|
|
263
288
|
"""
|
|
@@ -277,44 +302,48 @@ def get_default_egd_pdf():
|
|
|
277
302
|
pdf_file = None
|
|
278
303
|
file_field: Optional[FieldFile] = None
|
|
279
304
|
try:
|
|
280
|
-
# Create the
|
|
305
|
+
# Create the report record using the temporary file.
|
|
281
306
|
# delete_source=True will ensure temp_file_path is deleted by create_from_file
|
|
282
307
|
pdf_file = RawPdfFile.create_from_file(
|
|
283
308
|
file_path=temp_file_path,
|
|
284
309
|
center_name=center_name,
|
|
285
|
-
save=True,
|
|
310
|
+
save=True, # save=True is default and handled internally now
|
|
286
311
|
delete_source=True,
|
|
287
312
|
)
|
|
288
313
|
|
|
289
314
|
if pdf_file is None:
|
|
290
|
-
raise RuntimeError("Failed to create
|
|
291
|
-
|
|
315
|
+
raise RuntimeError("Failed to create report file object")
|
|
316
|
+
|
|
292
317
|
# Use storage API to check existence
|
|
293
318
|
file_field = pdf_file.file
|
|
294
319
|
if not isinstance(file_field, FieldFile):
|
|
295
320
|
raise RuntimeError("RawPdfFile.file did not return a FieldFile instance")
|
|
296
321
|
if not default_storage.exists(file_field.path):
|
|
297
|
-
raise RuntimeError(
|
|
298
|
-
|
|
322
|
+
raise RuntimeError(
|
|
323
|
+
f"report file does not exist in storage at {file_field.path}"
|
|
324
|
+
)
|
|
325
|
+
|
|
299
326
|
# Check that the source temp file was deleted
|
|
300
327
|
if temp_file_path.exists():
|
|
301
|
-
raise RuntimeError(
|
|
328
|
+
raise RuntimeError(
|
|
329
|
+
f"Temporary source file {temp_file_path} still exists after creation"
|
|
330
|
+
)
|
|
302
331
|
|
|
303
332
|
# Prepare a minimal report_meta for SensitiveMeta creation
|
|
304
333
|
default_report_meta = {
|
|
305
334
|
"patient_first_name": "DefaultFirstName",
|
|
306
335
|
"patient_last_name": "DefaultLastName",
|
|
307
|
-
"patient_dob": date(1980, 1, 1),
|
|
308
|
-
"examination_date": date(2024, 1, 1),
|
|
336
|
+
"patient_dob": date(1980, 1, 1), # Pass date object directly
|
|
337
|
+
"examination_date": date(2024, 1, 1), # Pass date object directly
|
|
309
338
|
# center_name will be added by process_file using pdf_file.center.name
|
|
310
339
|
}
|
|
311
340
|
|
|
312
341
|
# Call process_file to create SensitiveMeta and extract other info
|
|
313
342
|
pdf_file.process_file(
|
|
314
|
-
text="Default
|
|
315
|
-
anonymized_text="Default anonymized
|
|
343
|
+
text="Default report text content.",
|
|
344
|
+
anonymized_text="Default anonymized report text content.",
|
|
316
345
|
report_meta=default_report_meta,
|
|
317
|
-
verbose=False
|
|
346
|
+
verbose=False,
|
|
318
347
|
)
|
|
319
348
|
# process_file calls sensitive_meta.save() and self.save() (for RawPdfFile)
|
|
320
349
|
|
|
@@ -322,40 +351,46 @@ def get_default_egd_pdf():
|
|
|
322
351
|
# Clean up temp file in case of error before deletion could occur
|
|
323
352
|
if temp_file_path.exists():
|
|
324
353
|
temp_file_path.unlink()
|
|
325
|
-
raise e
|
|
354
|
+
raise e # Re-raise the exception
|
|
326
355
|
|
|
327
356
|
# pdf_file.file.path might fail if storage doesn't support direct paths (like S3)
|
|
328
357
|
# Prefer using storage API for checks. Logging path if available.
|
|
329
358
|
if file_field is not None:
|
|
330
359
|
try:
|
|
331
|
-
logger.info(
|
|
360
|
+
logger.info(
|
|
361
|
+
f"report file created: {file_field.name}, Path: {file_field.path}"
|
|
362
|
+
)
|
|
332
363
|
except NotImplementedError:
|
|
333
|
-
logger.info(
|
|
334
|
-
|
|
364
|
+
logger.info(
|
|
365
|
+
f"report file created: {file_field.name}, Path: (Not available from storage)"
|
|
366
|
+
)
|
|
335
367
|
|
|
336
368
|
return pdf_file
|
|
337
369
|
|
|
370
|
+
|
|
338
371
|
def get_default_video_file():
|
|
339
372
|
"""
|
|
340
373
|
Creates and returns a VideoFile instance using a randomly selected EGD examination video.
|
|
341
|
-
|
|
374
|
+
|
|
342
375
|
Loads all necessary data dependencies, selects a random video file for the 'egd' examination, and initializes a VideoFile object with the default center and processor names. The original video file is retained after creation.
|
|
343
|
-
|
|
376
|
+
|
|
344
377
|
Returns:
|
|
345
378
|
VideoFile: The created and initialized VideoFile instance.
|
|
346
379
|
"""
|
|
347
|
-
from .test_video_helper import get_random_video_path_by_examination_alias
|
|
348
380
|
from endoreg_db.models import VideoFile
|
|
381
|
+
|
|
349
382
|
from .data_loader import (
|
|
383
|
+
load_ai_model_data,
|
|
384
|
+
load_ai_model_label_data,
|
|
385
|
+
load_center_data,
|
|
350
386
|
load_disease_data,
|
|
387
|
+
load_endoscope_data,
|
|
351
388
|
load_event_data,
|
|
352
|
-
load_information_source,
|
|
353
389
|
load_examination_data,
|
|
354
|
-
|
|
355
|
-
load_endoscope_data,
|
|
356
|
-
load_ai_model_label_data,
|
|
357
|
-
load_ai_model_data,
|
|
390
|
+
load_information_source,
|
|
358
391
|
)
|
|
392
|
+
from .test_video_helper import get_random_video_path_by_examination_alias
|
|
393
|
+
|
|
359
394
|
load_disease_data()
|
|
360
395
|
load_event_data()
|
|
361
396
|
load_information_source()
|
|
@@ -365,14 +400,14 @@ def get_default_video_file():
|
|
|
365
400
|
load_ai_model_label_data()
|
|
366
401
|
load_ai_model_data()
|
|
367
402
|
video_path = get_random_video_path_by_examination_alias(
|
|
368
|
-
examination_alias=
|
|
403
|
+
examination_alias="egd", is_anonymous=False
|
|
369
404
|
)
|
|
370
405
|
|
|
371
406
|
video_file = VideoFile.create_from_file_initialized(
|
|
372
407
|
file_path=video_path,
|
|
373
408
|
center_name=DEFAULT_CENTER_NAME, # Pass center name as expected by _create_from_file
|
|
374
409
|
delete_source=False, # Keep the original asset for other tests
|
|
375
|
-
processor_name
|
|
410
|
+
processor_name=DEFAULT_ENDOSCOPY_PROCESSOR_NAME,
|
|
376
411
|
)
|
|
377
412
|
|
|
378
413
|
return video_file
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Re-export context helpers
|
|
2
|
+
from .context.file_lock import file_lock
|
|
3
|
+
from .context.default_sensitive_meta import default_sensitive_meta # if needed
|
|
4
|
+
from .context.import_context import ImportContext # if needed
|
|
5
|
+
from .context.validate_directories import validate_directories # if needed
|
|
6
|
+
|
|
7
|
+
# Re-export storage helpers
|
|
8
|
+
from .file_storage import create_report_file
|
|
9
|
+
from .file_storage import create_video_file
|
|
10
|
+
from .file_storage import sensitive_meta_storage
|
|
11
|
+
|
|
12
|
+
# Re-export import services
|
|
13
|
+
from .report_import_service import ReportImportService
|
|
14
|
+
from .video_import_service import VideoImportService
|
|
15
|
+
|
|
16
|
+
# Public API
|
|
17
|
+
__all__ = [
|
|
18
|
+
"file_lock",
|
|
19
|
+
"create_report_file",
|
|
20
|
+
"create_video_file",
|
|
21
|
+
"sensitive_meta_storage",
|
|
22
|
+
"ReportImportService",
|
|
23
|
+
"VideoImportService",
|
|
24
|
+
"ImportContext",
|
|
25
|
+
"validate_directories",
|
|
26
|
+
"default_sensitive_meta"
|
|
27
|
+
]
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# endoreg_db/import_files/processing/create_sensitive_meta.py
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
import logging
|
|
5
|
+
from datetime import date
|
|
6
|
+
from typing import Optional, Union
|
|
7
|
+
|
|
8
|
+
from endoreg_db.models.media import RawPdfFile, VideoFile
|
|
9
|
+
from endoreg_db.models.metadata.sensitive_meta import SensitiveMeta # adjust path
|
|
10
|
+
|
|
11
|
+
logger = logging.getLogger(__name__)
|
|
12
|
+
|
|
13
|
+
DEFAULT_PATIENT_FIRST_NAME = "unknown"
|
|
14
|
+
DEFAULT_PATIENT_LAST_NAME = "unknown"
|
|
15
|
+
DEFAULT_CENTER_NAME = "endoreg_db_demo"
|
|
16
|
+
# DEFAULT_PATIENT_DOB can be a fixed date or None to let logic.generate_random_dob handle it
|
|
17
|
+
DEFAULT_PATIENT_DOB = date(1970, 1, 1)
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def default_sensitive_meta(instance: Union[RawPdfFile, VideoFile]) -> SensitiveMeta | None:
|
|
21
|
+
"""
|
|
22
|
+
Ensure the given instance has a minimal SensitiveMeta attached.
|
|
23
|
+
|
|
24
|
+
Called after text extraction + merging; only creates meta if none exists.
|
|
25
|
+
"""
|
|
26
|
+
if instance is None:
|
|
27
|
+
logger.warning(
|
|
28
|
+
"No instance available for ensuring default patient data"
|
|
29
|
+
)
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
if instance.sensitive_meta:
|
|
34
|
+
# Already has meta; nothing to do
|
|
35
|
+
return
|
|
36
|
+
|
|
37
|
+
logger.info(
|
|
38
|
+
"No SensitiveMeta found for report %s, creating default",
|
|
39
|
+
getattr(instance, "pdf_hash", instance.pk),
|
|
40
|
+
)
|
|
41
|
+
if not isinstance(instance.center.name, str):
|
|
42
|
+
try:
|
|
43
|
+
center_name = os.environ.get("DEFAULT_CENTER_NAME")
|
|
44
|
+
assert center_name is not None
|
|
45
|
+
instance.center.name = center_name
|
|
46
|
+
except AssertionError as e:
|
|
47
|
+
logger.debug(f"{e}Center name is not set! You can set it in .env under DEFAULT_CENTER_NAME using default from default_sensitive_meta")
|
|
48
|
+
instance.center.name = DEFAULT_CENTER_NAME
|
|
49
|
+
instance.center.get_by_name(DEFAULT_CENTER_NAME)
|
|
50
|
+
|
|
51
|
+
default_data = {
|
|
52
|
+
"patient_first_name": DEFAULT_PATIENT_FIRST_NAME,
|
|
53
|
+
"patient_last_name": DEFAULT_PATIENT_LAST_NAME,
|
|
54
|
+
"patient_dob": DEFAULT_PATIENT_DOB,
|
|
55
|
+
"examination_date": date.today(),
|
|
56
|
+
"center_name": (
|
|
57
|
+
instance.center.name
|
|
58
|
+
if getattr(instance, "center", None) is not None
|
|
59
|
+
else DEFAULT_CENTER_NAME
|
|
60
|
+
),
|
|
61
|
+
# optional: link file_path for debugging/tracing
|
|
62
|
+
"file_path": str(instance.file_path) if getattr(instance, "file_path", None) else None,
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
try:
|
|
66
|
+
meta = SensitiveMeta.create_from_dict(default_data)
|
|
67
|
+
instance.sensitive_meta = meta
|
|
68
|
+
instance.save(update_fields=["sensitive_meta"])
|
|
69
|
+
logger.info(
|
|
70
|
+
"Created default SensitiveMeta for report %s",
|
|
71
|
+
getattr(instance, "pdf_hash", instance.pk),
|
|
72
|
+
)
|
|
73
|
+
return meta
|
|
74
|
+
except Exception as e:
|
|
75
|
+
logger.error(
|
|
76
|
+
"Failed to create default SensitiveMeta for report %s: %s",
|
|
77
|
+
getattr(instance, "pdf_hash", instance.pk),
|
|
78
|
+
e,
|
|
79
|
+
)
|
|
80
|
+
return None
|
|
81
|
+
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
from typing import Union, Optional
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from endoreg_db.models import Center
|
|
5
|
+
from endoreg_db.models.media import RawPdfFile, VideoFile
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def ensure_center(instance: Union[RawPdfFile, VideoFile], center: Optional[str]) -> Center:
|
|
9
|
+
if not isinstance(instance.center, Center):
|
|
10
|
+
raise AssertionError
|
|
11
|
+
if not isinstance(instance.center.name, str):
|
|
12
|
+
raise AssertionError
|
|
13
|
+
assert isinstance(instance.center.get_by_name(center), Center)
|
|
14
|
+
if not instance.center.get_by_name(center).name ==instance.center.name:
|
|
15
|
+
raise AssertionError
|
|
16
|
+
return instance.center
|
|
17
|
+
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from contextlib import contextmanager
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import os
|
|
4
|
+
import time
|
|
5
|
+
from logging import getLogger
|
|
6
|
+
import errno
|
|
7
|
+
import shutil
|
|
8
|
+
from typing import Generator, Any
|
|
9
|
+
|
|
10
|
+
logger = getLogger(__name__)
|
|
11
|
+
|
|
12
|
+
STALE_LOCK_SECONDS = 6000
|
|
13
|
+
MAX_LOCK_WAIT_SECONDS = 90
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
@contextmanager
|
|
17
|
+
def file_lock(path: Path) -> Generator[None, Any, None]:
|
|
18
|
+
"""
|
|
19
|
+
Create a file lock to prevent duplicate processing of the same file.
|
|
20
|
+
|
|
21
|
+
Lock is created *next to* the source file: "<path>.lock".
|
|
22
|
+
"""
|
|
23
|
+
lock_path = Path(str(path) + ".lock")
|
|
24
|
+
fd = None
|
|
25
|
+
try:
|
|
26
|
+
deadline = time.time() + MAX_LOCK_WAIT_SECONDS
|
|
27
|
+
while True:
|
|
28
|
+
try:
|
|
29
|
+
fd = os.open(lock_path, os.O_CREAT | os.O_EXCL | os.O_WRONLY, 0o644)
|
|
30
|
+
break
|
|
31
|
+
except FileExistsError:
|
|
32
|
+
age = None
|
|
33
|
+
try:
|
|
34
|
+
st = os.stat(lock_path)
|
|
35
|
+
age = time.time() - st.st_mtime
|
|
36
|
+
except FileNotFoundError:
|
|
37
|
+
age = None
|
|
38
|
+
|
|
39
|
+
if age is not None and age > STALE_LOCK_SECONDS:
|
|
40
|
+
try:
|
|
41
|
+
logger.warning(
|
|
42
|
+
"Stale lock detected for %s (age %.0fs). Reclaiming lock...",
|
|
43
|
+
path,
|
|
44
|
+
age,
|
|
45
|
+
)
|
|
46
|
+
lock_path.unlink()
|
|
47
|
+
except Exception as e:
|
|
48
|
+
logger.warning("Failed to remove stale lock %s: %s", lock_path, e)
|
|
49
|
+
continue
|
|
50
|
+
|
|
51
|
+
if time.time() >= deadline:
|
|
52
|
+
raise ValueError(f"File already being processed: {path}")
|
|
53
|
+
time.sleep(1.0)
|
|
54
|
+
|
|
55
|
+
os.write(fd, b"lock")
|
|
56
|
+
os.close(fd)
|
|
57
|
+
fd = None
|
|
58
|
+
yield
|
|
59
|
+
finally:
|
|
60
|
+
try:
|
|
61
|
+
if fd is not None:
|
|
62
|
+
os.close(fd)
|
|
63
|
+
if lock_path.exists():
|
|
64
|
+
lock_path.unlink()
|
|
65
|
+
except OSError:
|
|
66
|
+
pass
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
from dataclasses import dataclass, field
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Optional, Dict, Any, Union
|
|
4
|
+
|
|
5
|
+
from lx_anonymizer.sensitive_meta_interface import SensitiveMeta
|
|
6
|
+
from endoreg_db.models.media import RawPdfFile, VideoFile
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@dataclass
|
|
10
|
+
class ImportContext:
|
|
11
|
+
"""
|
|
12
|
+
Tracking the import success and reasons of failure.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
# core import parameters
|
|
16
|
+
file_path: Path
|
|
17
|
+
center_name: str
|
|
18
|
+
processor_name: str = "olympus-cv-500"
|
|
19
|
+
delete_source: bool = True
|
|
20
|
+
retry: bool = False
|
|
21
|
+
import_completed: bool = False
|
|
22
|
+
error_reason: str = ""
|
|
23
|
+
|
|
24
|
+
# paths
|
|
25
|
+
original_path: Optional[Path] = None
|
|
26
|
+
quarantine_path: Optional[Path] = None
|
|
27
|
+
sensitive_path: Optional[Path] = None
|
|
28
|
+
anonymized_path: Optional[Path] = None
|
|
29
|
+
|
|
30
|
+
# associated objects
|
|
31
|
+
current_report: Optional[RawPdfFile] = None
|
|
32
|
+
current_video: Optional[VideoFile] = None
|
|
33
|
+
current_meta: Optional[SensitiveMeta] = None
|
|
34
|
+
instance: Optional[Union[RawPdfFile, VideoFile]] = None
|
|
35
|
+
|
|
36
|
+
file_type: str = "undefined"
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# processing metadata
|
|
40
|
+
file_hash: str = ""
|
|
41
|
+
original_text: Optional[str] = None
|
|
42
|
+
anonymized_text: Optional[str] = None
|
|
43
|
+
extracted_metadata: Dict[str, Any] = field(default_factory=dict)
|