endoreg-db 0.8.8.0__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/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 +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/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 +64 -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/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.8.9.dist-info}/METADATA +2 -2
- {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +217 -335
- {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.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/{migrations/__init__.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/{urls/sensitive_meta.py → import_files/pseudonymization/__init__.py} +0 -0
- /endoreg_db/{views/pdf/pdf_stream_views.py → import_files/pseudonymization/pseudonymize.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.8.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -80,7 +80,9 @@ def parse_any_date(s: str) -> Optional[date]:
|
|
|
80
80
|
# Try dateparser with German locale preference
|
|
81
81
|
import dateparser
|
|
82
82
|
|
|
83
|
-
dt = dateparser.parse(
|
|
83
|
+
dt = dateparser.parse(
|
|
84
|
+
s, settings={"DATE_ORDER": "DMY", "PREFER_DAY_OF_MONTH": "first"}
|
|
85
|
+
)
|
|
84
86
|
return dt.date() if dt else None
|
|
85
87
|
except Exception as e:
|
|
86
88
|
logger.debug(f"Dateparser fallback failed for '{s}': {e}")
|
|
@@ -173,7 +175,9 @@ def calculate_patient_hash(instance: "SensitiveMeta", salt: str = SECRET_SALT) -
|
|
|
173
175
|
return sha256(hash_str.encode()).hexdigest()
|
|
174
176
|
|
|
175
177
|
|
|
176
|
-
def calculate_examination_hash(
|
|
178
|
+
def calculate_examination_hash(
|
|
179
|
+
instance: "SensitiveMeta", salt: str = SECRET_SALT
|
|
180
|
+
) -> str:
|
|
177
181
|
"""Calculates the examination hash for the instance."""
|
|
178
182
|
dob = instance.patient_dob
|
|
179
183
|
first_name = instance.patient_first_name
|
|
@@ -211,17 +215,25 @@ def create_pseudo_examiner_logic(instance: "SensitiveMeta") -> "Examiner":
|
|
|
211
215
|
center = instance.center # Should be set before calling save
|
|
212
216
|
|
|
213
217
|
if not first_name or not last_name or not center:
|
|
214
|
-
logger.warning(
|
|
218
|
+
logger.warning(
|
|
219
|
+
f"Incomplete examiner info for SensitiveMeta (pk={instance.pk or 'new'}). Using default examiner."
|
|
220
|
+
)
|
|
215
221
|
# Ensure default center exists or handle appropriately
|
|
216
222
|
try:
|
|
217
223
|
default_center = Center.objects.get(name="endoreg_db_demo")
|
|
218
224
|
except Center.DoesNotExist:
|
|
219
|
-
logger.error(
|
|
225
|
+
logger.error(
|
|
226
|
+
"Default center 'endoreg_db_demo' not found. Cannot create default examiner."
|
|
227
|
+
)
|
|
220
228
|
raise ValueError("Default center 'endoreg_db_demo' not found.")
|
|
221
229
|
|
|
222
|
-
examiner, _created = Examiner.custom_get_or_create(
|
|
230
|
+
examiner, _created = Examiner.custom_get_or_create(
|
|
231
|
+
first_name="Unknown", last_name="Unknown", center=default_center
|
|
232
|
+
)
|
|
223
233
|
else:
|
|
224
|
-
examiner, _created = Examiner.custom_get_or_create(
|
|
234
|
+
examiner, _created = Examiner.custom_get_or_create(
|
|
235
|
+
first_name=first_name, last_name=last_name, center=center
|
|
236
|
+
)
|
|
225
237
|
|
|
226
238
|
return examiner
|
|
227
239
|
|
|
@@ -267,11 +279,13 @@ def get_or_create_pseudo_patient_examination_logic(
|
|
|
267
279
|
pseudo_patient, _created = get_or_create_pseudo_patient_logic(instance)
|
|
268
280
|
instance.pseudo_patient = pseudo_patient # Assign FK directly
|
|
269
281
|
|
|
270
|
-
patient_examination, _created =
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
282
|
+
patient_examination, _created = (
|
|
283
|
+
PatientExamination.get_or_create_pseudo_patient_examination_by_hash(
|
|
284
|
+
patient_hash=instance.patient_hash,
|
|
285
|
+
examination_hash=instance.examination_hash,
|
|
286
|
+
# Optionally pass pseudo_patient if the method requires it
|
|
287
|
+
# pseudo_patient=instance.pseudo_patient
|
|
288
|
+
)
|
|
275
289
|
)
|
|
276
290
|
return patient_examination, _created
|
|
277
291
|
|
|
@@ -285,7 +299,7 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
|
|
|
285
299
|
This function is called on every save() operation and implements a two-phase approach:
|
|
286
300
|
|
|
287
301
|
**Phase 1: Initial Creation (with defaults)**
|
|
288
|
-
- When a SensitiveMeta is first created (e.g., via
|
|
302
|
+
- When a SensitiveMeta is first created (e.g., via create_from_dict),
|
|
289
303
|
it may have missing patient data (names, DOB, etc.)
|
|
290
304
|
- Default values are set to prevent hash calculation errors:
|
|
291
305
|
* patient_first_name: "unknown"
|
|
@@ -334,10 +348,14 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
|
|
|
334
348
|
|
|
335
349
|
# 1. Ensure DOB and Examination Date exist
|
|
336
350
|
if not instance.patient_dob:
|
|
337
|
-
logger.debug(
|
|
351
|
+
logger.debug(
|
|
352
|
+
f"SensitiveMeta (pk={instance.pk or 'new'}): Patient DOB missing, generating random."
|
|
353
|
+
)
|
|
338
354
|
instance.patient_dob = generate_random_dob()
|
|
339
355
|
if not instance.examination_date:
|
|
340
|
-
logger.debug(
|
|
356
|
+
logger.debug(
|
|
357
|
+
f"SensitiveMeta (pk={instance.pk or 'new'}): Examination date missing, generating random."
|
|
358
|
+
)
|
|
341
359
|
instance.examination_date = generate_random_examination_date()
|
|
342
360
|
|
|
343
361
|
# 2. Ensure Center exists (should be set before calling save)
|
|
@@ -389,7 +407,9 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
|
|
|
389
407
|
first_name = instance.patient_first_name
|
|
390
408
|
gender_str = guess_name_gender(first_name)
|
|
391
409
|
if not gender_str:
|
|
392
|
-
raise ValueError(
|
|
410
|
+
raise ValueError(
|
|
411
|
+
"Patient gender could not be determined and must be set before saving."
|
|
412
|
+
)
|
|
393
413
|
# Convert string to Gender object
|
|
394
414
|
try:
|
|
395
415
|
gender_obj = Gender.objects.get(name=gender_str)
|
|
@@ -416,7 +436,9 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
|
|
|
416
436
|
|
|
417
437
|
# 6. Get or Create Pseudo Examination (depends on hashes)
|
|
418
438
|
# Assign directly to the FK field
|
|
419
|
-
pseudo_examination, _created = get_or_create_pseudo_patient_examination_logic(
|
|
439
|
+
pseudo_examination, _created = get_or_create_pseudo_patient_examination_logic(
|
|
440
|
+
instance
|
|
441
|
+
)
|
|
420
442
|
instance.pseudo_examination = pseudo_examination
|
|
421
443
|
|
|
422
444
|
# 7. Get or Create Pseudo Examiner (depends on names, center)
|
|
@@ -430,7 +452,9 @@ def perform_save_logic(instance: "SensitiveMeta") -> "Examiner":
|
|
|
430
452
|
return examiner_instance
|
|
431
453
|
|
|
432
454
|
|
|
433
|
-
def create_sensitive_meta_from_dict(
|
|
455
|
+
def create_sensitive_meta_from_dict(
|
|
456
|
+
cls: Type["SensitiveMeta"], data: Dict[str, Any]
|
|
457
|
+
) -> "SensitiveMeta":
|
|
434
458
|
"""
|
|
435
459
|
Create a SensitiveMeta instance from a dictionary.
|
|
436
460
|
|
|
@@ -479,7 +503,11 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
479
503
|
ValueError: If center_name does not match any Center in database
|
|
480
504
|
"""
|
|
481
505
|
|
|
482
|
-
field_names = {
|
|
506
|
+
field_names = {
|
|
507
|
+
f.name
|
|
508
|
+
for f in cls._meta.get_fields()
|
|
509
|
+
if not f.is_relation or f.one_to_one or (f.many_to_one and f.related_model)
|
|
510
|
+
}
|
|
483
511
|
selected_data = {k: v for k, v in data.items() if k in field_names}
|
|
484
512
|
|
|
485
513
|
# --- Convert patient_dob if it's a date object ---
|
|
@@ -506,9 +534,13 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
506
534
|
try:
|
|
507
535
|
import dateparser
|
|
508
536
|
|
|
509
|
-
parsed_dob = dateparser.parse(
|
|
537
|
+
parsed_dob = dateparser.parse(
|
|
538
|
+
dob, languages=["de"], settings={"DATE_ORDER": "DMY"}
|
|
539
|
+
)
|
|
510
540
|
if parsed_dob:
|
|
511
|
-
aware_dob = timezone.make_aware(
|
|
541
|
+
aware_dob = timezone.make_aware(
|
|
542
|
+
parsed_dob.replace(hour=0, minute=0, second=0, microsecond=0)
|
|
543
|
+
)
|
|
512
544
|
selected_data["patient_dob"] = aware_dob
|
|
513
545
|
logger.debug(
|
|
514
546
|
"Parsed string patient_dob '%s' to aware datetime: %s",
|
|
@@ -562,7 +594,9 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
562
594
|
# Fall back to dateparser for complex formats
|
|
563
595
|
import dateparser
|
|
564
596
|
|
|
565
|
-
parsed_date = dateparser.parse(
|
|
597
|
+
parsed_date = dateparser.parse(
|
|
598
|
+
exam_date, languages=["de"], settings={"DATE_ORDER": "DMY"}
|
|
599
|
+
)
|
|
566
600
|
if parsed_date:
|
|
567
601
|
selected_data["examination_date"] = parsed_date.date()
|
|
568
602
|
logger.debug(
|
|
@@ -580,7 +614,9 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
580
614
|
# Use dateparser for non-ISO formats
|
|
581
615
|
import dateparser
|
|
582
616
|
|
|
583
|
-
parsed_date = dateparser.parse(
|
|
617
|
+
parsed_date = dateparser.parse(
|
|
618
|
+
exam_date, languages=["de"], settings={"DATE_ORDER": "DMY"}
|
|
619
|
+
)
|
|
584
620
|
if parsed_date:
|
|
585
621
|
selected_data["examination_date"] = parsed_date.date()
|
|
586
622
|
logger.debug(
|
|
@@ -623,7 +659,9 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
623
659
|
raise ValueError(f"Center with name '{center_name}' does not exist.")
|
|
624
660
|
else:
|
|
625
661
|
# Neither center nor center_name provided
|
|
626
|
-
raise ValueError(
|
|
662
|
+
raise ValueError(
|
|
663
|
+
"Either 'center' (Center object) or 'center_name' (string) is required in data dictionary."
|
|
664
|
+
)
|
|
627
665
|
|
|
628
666
|
# Handle Names and Gender
|
|
629
667
|
first_name = selected_data.get("patient_first_name") or DEFAULT_UNKNOWN
|
|
@@ -639,9 +677,13 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
639
677
|
elif isinstance(patient_gender_input, str):
|
|
640
678
|
# Input is a string (gender name)
|
|
641
679
|
try:
|
|
642
|
-
selected_data["patient_gender"] = Gender.objects.get(
|
|
680
|
+
selected_data["patient_gender"] = Gender.objects.get(
|
|
681
|
+
name=patient_gender_input
|
|
682
|
+
)
|
|
643
683
|
except Gender.DoesNotExist:
|
|
644
|
-
logger.warning(
|
|
684
|
+
logger.warning(
|
|
685
|
+
f"Gender with name '{patient_gender_input}' provided but not found. Attempting to guess or use default."
|
|
686
|
+
)
|
|
645
687
|
# Fall through to guessing logic if provided string name is invalid
|
|
646
688
|
normalized = (patient_gender_input or "").lower()
|
|
647
689
|
if normalized in {"male", "female", "unknown"}:
|
|
@@ -656,13 +698,19 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
656
698
|
else:
|
|
657
699
|
patient_gender_input = None # Reset to trigger guessing
|
|
658
700
|
|
|
659
|
-
if not isinstance(
|
|
701
|
+
if not isinstance(
|
|
702
|
+
selected_data.get("patient_gender"), Gender
|
|
703
|
+
): # If not already a Gender object (e.g. was None, or string lookup failed)
|
|
660
704
|
gender_name_to_use = guess_name_gender(first_name)
|
|
661
705
|
if not gender_name_to_use:
|
|
662
|
-
logger.warning(
|
|
706
|
+
logger.warning(
|
|
707
|
+
f"Could not guess gender for name '{first_name}'. Setting Gender to unknown."
|
|
708
|
+
)
|
|
663
709
|
gender_name_to_use = "unknown"
|
|
664
710
|
try:
|
|
665
|
-
selected_data["patient_gender"] = Gender.objects.get(
|
|
711
|
+
selected_data["patient_gender"] = Gender.objects.get(
|
|
712
|
+
name=gender_name_to_use
|
|
713
|
+
)
|
|
666
714
|
except Gender.DoesNotExist:
|
|
667
715
|
gender_obj, _ = Gender.objects.get_or_create(
|
|
668
716
|
name=gender_name_to_use,
|
|
@@ -700,7 +748,9 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
700
748
|
selected_data["examination_time"] = exam_time.time()
|
|
701
749
|
elif isinstance(exam_time, date):
|
|
702
750
|
# no time info — ignore
|
|
703
|
-
logger.debug(
|
|
751
|
+
logger.debug(
|
|
752
|
+
f"examination_time value {exam_time} has no time component; skipping"
|
|
753
|
+
)
|
|
704
754
|
else:
|
|
705
755
|
selected_data["examination_time"] = exam_time
|
|
706
756
|
except Exception as e:
|
|
@@ -709,10 +759,16 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
709
759
|
anonymized_text = data.get("anonymized_text") or data.get("anonym_text")
|
|
710
760
|
if anonymized_text:
|
|
711
761
|
if isinstance(anonymized_text, (str, bytes)):
|
|
712
|
-
selected_data["anonymized_text"] =
|
|
762
|
+
selected_data["anonymized_text"] = (
|
|
763
|
+
anonymized_text.decode()
|
|
764
|
+
if isinstance(anonymized_text, bytes)
|
|
765
|
+
else anonymized_text
|
|
766
|
+
)
|
|
713
767
|
else:
|
|
714
768
|
selected_data["anonymized_text"] = str(anonymized_text)
|
|
715
|
-
logger.debug(
|
|
769
|
+
logger.debug(
|
|
770
|
+
"Set anonymized_text (length=%d)", len(selected_data["anonymized_text"])
|
|
771
|
+
)
|
|
716
772
|
|
|
717
773
|
# Update name DB
|
|
718
774
|
update_name_db(first_name, last_name)
|
|
@@ -726,13 +782,15 @@ def create_sensitive_meta_from_dict(cls: Type["SensitiveMeta"], data: Dict[str,
|
|
|
726
782
|
return sensitive_meta
|
|
727
783
|
|
|
728
784
|
|
|
729
|
-
def update_sensitive_meta_from_dict(
|
|
785
|
+
def update_sensitive_meta_from_dict(
|
|
786
|
+
instance: "SensitiveMeta", data: Dict[str, Any]
|
|
787
|
+
) -> "SensitiveMeta":
|
|
730
788
|
"""
|
|
731
789
|
Updates a SensitiveMeta instance from a dictionary of new values.
|
|
732
790
|
|
|
733
791
|
**Integration with two-phase save pattern:**
|
|
734
792
|
This function is typically called after initial SensitiveMeta creation when real
|
|
735
|
-
patient data becomes available (e.g., extracted from video OCR,
|
|
793
|
+
patient data becomes available (e.g., extracted from video OCR, report parsing, or
|
|
736
794
|
manual annotation).
|
|
737
795
|
|
|
738
796
|
**Example workflow:**
|
|
@@ -771,10 +829,16 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
|
|
|
771
829
|
Raises:
|
|
772
830
|
Exception: If save fails or required conversions fail
|
|
773
831
|
"""
|
|
774
|
-
field_names = {
|
|
832
|
+
field_names = {
|
|
833
|
+
f.name
|
|
834
|
+
for f in instance._meta.get_fields()
|
|
835
|
+
if not f.is_relation or f.one_to_one or (f.many_to_one and f.related_model)
|
|
836
|
+
}
|
|
775
837
|
# Exclude FKs that should not be updated directly from dict keys (handled separately or via save logic)
|
|
776
838
|
excluded_fields = {"pseudo_patient", "pseudo_examination"}
|
|
777
|
-
selected_data = {
|
|
839
|
+
selected_data = {
|
|
840
|
+
k: v for k, v in data.items() if k in field_names and k not in excluded_fields
|
|
841
|
+
}
|
|
778
842
|
|
|
779
843
|
# Handle potential Center update - accept both center_name (string) and center (object)
|
|
780
844
|
from ..administration import Center
|
|
@@ -788,7 +852,9 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
|
|
|
788
852
|
instance.center = center
|
|
789
853
|
logger.debug(f"Updated center from Center object: {center.name}")
|
|
790
854
|
else:
|
|
791
|
-
logger.warning(
|
|
855
|
+
logger.warning(
|
|
856
|
+
f"Invalid center type {type(center)}, expected Center instance. Ignoring."
|
|
857
|
+
)
|
|
792
858
|
# Remove from selected_data to prevent override
|
|
793
859
|
selected_data.pop("center", None)
|
|
794
860
|
elif center_name:
|
|
@@ -798,7 +864,9 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
|
|
|
798
864
|
instance.center = center_obj
|
|
799
865
|
logger.debug(f"Updated center from center_name string: {center_name}")
|
|
800
866
|
except Center.DoesNotExist:
|
|
801
|
-
logger.warning(
|
|
867
|
+
logger.warning(
|
|
868
|
+
f"Center '{center_name}' not found during update. Keeping existing center."
|
|
869
|
+
)
|
|
802
870
|
else:
|
|
803
871
|
# Both are None/missing - remove 'center' from selected_data to preserve existing value
|
|
804
872
|
selected_data.pop("center", None)
|
|
@@ -821,10 +889,14 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
|
|
|
821
889
|
elif isinstance(patient_gender_input, str):
|
|
822
890
|
gender_input_clean = patient_gender_input.strip()
|
|
823
891
|
# Try direct case-insensitive DB lookup first
|
|
824
|
-
gender_obj = Gender.objects.filter(
|
|
892
|
+
gender_obj = Gender.objects.filter(
|
|
893
|
+
name__iexact=gender_input_clean
|
|
894
|
+
).first()
|
|
825
895
|
if gender_obj:
|
|
826
896
|
selected_data["patient_gender"] = gender_obj
|
|
827
|
-
logger.debug(
|
|
897
|
+
logger.debug(
|
|
898
|
+
f"Successfully matched gender string '{patient_gender_input}' to Gender object via iexact lookup"
|
|
899
|
+
)
|
|
828
900
|
else:
|
|
829
901
|
# Use mapping helper for fallback
|
|
830
902
|
mapped = _map_gender_string_to_standard(gender_input_clean)
|
|
@@ -832,40 +904,70 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
|
|
|
832
904
|
gender_obj = Gender.objects.filter(name__iexact=mapped).first()
|
|
833
905
|
if gender_obj:
|
|
834
906
|
selected_data["patient_gender"] = gender_obj
|
|
835
|
-
logger.info(
|
|
907
|
+
logger.info(
|
|
908
|
+
f"Mapped gender '{patient_gender_input}' to '{mapped}' via fallback mapping"
|
|
909
|
+
)
|
|
836
910
|
else:
|
|
837
|
-
logger.warning(
|
|
838
|
-
|
|
911
|
+
logger.warning(
|
|
912
|
+
f"Mapped gender '{patient_gender_input}' to '{mapped}', but no such Gender in DB. Trying 'unknown'."
|
|
913
|
+
)
|
|
914
|
+
unknown_gender = Gender.objects.filter(
|
|
915
|
+
name__iexact="unknown"
|
|
916
|
+
).first()
|
|
839
917
|
if unknown_gender:
|
|
840
918
|
selected_data["patient_gender"] = unknown_gender
|
|
841
|
-
logger.warning(
|
|
919
|
+
logger.warning(
|
|
920
|
+
f"Using 'unknown' gender as fallback for '{patient_gender_input}'"
|
|
921
|
+
)
|
|
842
922
|
else:
|
|
843
|
-
logger.error(
|
|
923
|
+
logger.error(
|
|
924
|
+
f"No 'unknown' gender found in database. Cannot handle gender '{patient_gender_input}'. Skipping gender update."
|
|
925
|
+
)
|
|
844
926
|
selected_data.pop("patient_gender", None)
|
|
845
927
|
else:
|
|
846
928
|
# Last resort: try to get 'unknown' gender
|
|
847
|
-
unknown_gender = Gender.objects.filter(
|
|
929
|
+
unknown_gender = Gender.objects.filter(
|
|
930
|
+
name__iexact="unknown"
|
|
931
|
+
).first()
|
|
848
932
|
if unknown_gender:
|
|
849
933
|
selected_data["patient_gender"] = unknown_gender
|
|
850
|
-
logger.warning(
|
|
934
|
+
logger.warning(
|
|
935
|
+
f"Using 'unknown' gender as fallback for '{patient_gender_input}' (no mapping)"
|
|
936
|
+
)
|
|
851
937
|
else:
|
|
852
|
-
logger.error(
|
|
938
|
+
logger.error(
|
|
939
|
+
f"No 'unknown' gender found in database. Cannot handle gender '{patient_gender_input}'. Skipping gender update."
|
|
940
|
+
)
|
|
853
941
|
selected_data.pop("patient_gender", None)
|
|
854
942
|
else:
|
|
855
|
-
logger.warning(
|
|
943
|
+
logger.warning(
|
|
944
|
+
f"Unexpected patient_gender type {type(patient_gender_input)}: {patient_gender_input}. Skipping gender update."
|
|
945
|
+
)
|
|
856
946
|
selected_data.pop("patient_gender", None)
|
|
857
947
|
except Exception as e:
|
|
858
|
-
logger.exception(
|
|
948
|
+
logger.exception(
|
|
949
|
+
f"Error handling patient_gender '{patient_gender_input}': {e}. Skipping gender update."
|
|
950
|
+
)
|
|
859
951
|
selected_data.pop("patient_gender", None)
|
|
860
952
|
|
|
861
953
|
# TODO Review: Handle new optional fields on update
|
|
862
|
-
for key in (
|
|
954
|
+
for key in (
|
|
955
|
+
"file_path",
|
|
956
|
+
"casenumber",
|
|
957
|
+
"examination_time",
|
|
958
|
+
"anonymized_text",
|
|
959
|
+
"anonym_text",
|
|
960
|
+
):
|
|
863
961
|
if key in data and data[key] is not None:
|
|
864
962
|
val = data[key]
|
|
865
963
|
if key in ("file_path", "casenumber"):
|
|
866
964
|
setattr(instance, key, str(val))
|
|
867
965
|
elif key in ("anonymized_text", "anonym_text"):
|
|
868
|
-
setattr(
|
|
966
|
+
setattr(
|
|
967
|
+
instance,
|
|
968
|
+
"anonymized_text",
|
|
969
|
+
val if isinstance(val, str) else str(val),
|
|
970
|
+
)
|
|
869
971
|
elif key == "examination_time":
|
|
870
972
|
try:
|
|
871
973
|
from datetime import time as dt_time
|
|
@@ -897,69 +999,37 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
|
|
|
897
999
|
value_to_set = v
|
|
898
1000
|
if k == "patient_dob":
|
|
899
1001
|
if isinstance(v, date) and not isinstance(v, datetime):
|
|
900
|
-
aware_dob = timezone.make_aware(
|
|
1002
|
+
aware_dob = timezone.make_aware(
|
|
1003
|
+
datetime.combine(v, datetime.min.time())
|
|
1004
|
+
)
|
|
901
1005
|
value_to_set = aware_dob
|
|
902
1006
|
logger.debug(
|
|
903
1007
|
"Converted patient_dob from date to aware datetime during update: %s",
|
|
904
1008
|
aware_dob,
|
|
905
1009
|
)
|
|
906
1010
|
elif isinstance(v, str):
|
|
907
|
-
|
|
908
|
-
if
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
logger.
|
|
914
|
-
"
|
|
1011
|
+
parsed = parse_any_date(v)
|
|
1012
|
+
if parsed:
|
|
1013
|
+
aware_dob = timezone.make_aware(
|
|
1014
|
+
datetime.combine(parsed, datetime.min.time())
|
|
1015
|
+
)
|
|
1016
|
+
value_to_set = aware_dob
|
|
1017
|
+
logger.debug(
|
|
1018
|
+
"Parsed string patient_dob '%s' during update to aware datetime: %s",
|
|
915
1019
|
v,
|
|
1020
|
+
aware_dob,
|
|
916
1021
|
)
|
|
917
|
-
continue # Skip this field
|
|
918
1022
|
else:
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
value_to_set,
|
|
930
|
-
)
|
|
931
|
-
else:
|
|
932
|
-
logger.warning(
|
|
933
|
-
"Could not parse patient_dob string '%s' during update, skipping",
|
|
934
|
-
v,
|
|
935
|
-
)
|
|
936
|
-
continue
|
|
937
|
-
except Exception as e:
|
|
938
|
-
logger.warning(
|
|
939
|
-
"Error parsing patient_dob string '%s' during update: %s, skipping",
|
|
940
|
-
v,
|
|
941
|
-
e,
|
|
942
|
-
)
|
|
943
|
-
continue
|
|
944
|
-
elif k == "examination_date" and isinstance(v, str):
|
|
945
|
-
if v == "examination_date" or v in [
|
|
946
|
-
"patient_first_name",
|
|
947
|
-
"patient_last_name",
|
|
948
|
-
"patient_dob",
|
|
949
|
-
]:
|
|
950
|
-
logger.warning(
|
|
951
|
-
"Skipping invalid examination_date value '%s' during update - appears to be field name",
|
|
952
|
-
v,
|
|
953
|
-
)
|
|
954
|
-
continue
|
|
955
|
-
else:
|
|
956
|
-
# Try to parse as date string
|
|
957
|
-
try:
|
|
958
|
-
import dateparser
|
|
959
|
-
|
|
960
|
-
parsed_date = dateparser.parse(v, languages=["de"], settings={"DATE_ORDER": "DMY"})
|
|
961
|
-
if parsed_date:
|
|
962
|
-
value_to_set = parsed_date.date()
|
|
1023
|
+
logger.warning(
|
|
1024
|
+
"Could not parse patient_dob string '%s' during update, skipping",
|
|
1025
|
+
v,
|
|
1026
|
+
)
|
|
1027
|
+
continue
|
|
1028
|
+
elif k == "examination_date":
|
|
1029
|
+
if isinstance(v, str):
|
|
1030
|
+
parsed = parse_any_date(v)
|
|
1031
|
+
if parsed:
|
|
1032
|
+
value_to_set = parsed # field is DateField, so keep it as date
|
|
963
1033
|
logger.debug(
|
|
964
1034
|
"Parsed string examination_date '%s' during update to date: %s",
|
|
965
1035
|
v,
|
|
@@ -971,23 +1041,24 @@ def update_sensitive_meta_from_dict(instance: "SensitiveMeta", data: Dict[str, A
|
|
|
971
1041
|
v,
|
|
972
1042
|
)
|
|
973
1043
|
continue
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
e,
|
|
979
|
-
)
|
|
980
|
-
continue
|
|
981
|
-
# --- End Conversion ---
|
|
1044
|
+
elif isinstance(v, date):
|
|
1045
|
+
value_to_set = v
|
|
1046
|
+
|
|
1047
|
+
# --- End Conversion ---
|
|
982
1048
|
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
1049
|
+
# Check if patient name is changing
|
|
1050
|
+
if (
|
|
1051
|
+
k in ["patient_first_name", "patient_last_name"]
|
|
1052
|
+
and getattr(instance, k) != value_to_set
|
|
1053
|
+
):
|
|
1054
|
+
patient_name_changed = True
|
|
986
1055
|
|
|
987
|
-
|
|
1056
|
+
setattr(instance, k, value_to_set) # Use value_to_set
|
|
988
1057
|
|
|
989
1058
|
except Exception as e:
|
|
990
|
-
logger.error(
|
|
1059
|
+
logger.error(
|
|
1060
|
+
f"Error setting attribute '{k}' to '{v}': {e}. Skipping this field."
|
|
1061
|
+
)
|
|
991
1062
|
continue
|
|
992
1063
|
|
|
993
1064
|
# Update name DB if patient names changed
|
|
@@ -1041,19 +1112,24 @@ def _map_gender_string_to_standard(gender_str: str) -> Optional[str]:
|
|
|
1041
1112
|
return standard
|
|
1042
1113
|
return None
|
|
1043
1114
|
|
|
1044
|
-
|
|
1115
|
+
|
|
1116
|
+
def _create_anonymized_record(
|
|
1117
|
+
instance: "SensitiveMeta",
|
|
1118
|
+
DEFAULT_ANONYMIZED=None,
|
|
1119
|
+
DEFAULT_ANONYMIZED_DATE=timezone.make_aware(datetime(1900, 1, 1)),
|
|
1120
|
+
) -> None:
|
|
1045
1121
|
"""
|
|
1046
1122
|
Create a SensitiveMeta instance with all sensitive fields set to anonymized defaults.
|
|
1047
1123
|
This is only called after anonymization and will delete all data that can identify a patient from the database.
|
|
1048
1124
|
What is left will only be the patient hash.
|
|
1049
|
-
|
|
1125
|
+
|
|
1050
1126
|
Args:
|
|
1051
1127
|
instance: The existing SensitiveMeta instance to anonymize
|
|
1052
1128
|
DEFAULT_ANONYMIZED: Usually None, The default string to use for anonymized fields (e.g., "anonymized,")
|
|
1053
1129
|
"""
|
|
1054
|
-
|
|
1130
|
+
|
|
1055
1131
|
instance.refresh_from_db()
|
|
1056
|
-
instance.get_patient_hash()
|
|
1132
|
+
instance.get_patient_hash()
|
|
1057
1133
|
instance.get_patient_examination_hash()
|
|
1058
1134
|
|
|
1059
1135
|
anonymized_data = {
|
|
@@ -1063,5 +1139,5 @@ def _create_anonymized_record(instance: "SensitiveMeta", DEFAULT_ANONYMIZED=None
|
|
|
1063
1139
|
"examination_date": DEFAULT_ANONYMIZED_DATE,
|
|
1064
1140
|
}
|
|
1065
1141
|
sensitive_meta = update_sensitive_meta_from_dict(instance, anonymized_data)
|
|
1066
|
-
|
|
1067
|
-
sensitive_meta.save()
|
|
1142
|
+
|
|
1143
|
+
sensitive_meta.save()
|
|
@@ -2,9 +2,6 @@ from typing import TYPE_CHECKING
|
|
|
2
2
|
|
|
3
3
|
from django.db import models
|
|
4
4
|
|
|
5
|
-
if TYPE_CHECKING:
|
|
6
|
-
from endoreg_db.models import InformationSourceType
|
|
7
|
-
|
|
8
5
|
|
|
9
6
|
def get_prediction_information_source():
|
|
10
7
|
"""
|
|
@@ -47,9 +44,33 @@ class InformationSource(models.Model):
|
|
|
47
44
|
abbreviation = models.CharField(max_length=100, blank=True, null=True, unique=True)
|
|
48
45
|
|
|
49
46
|
if TYPE_CHECKING:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
47
|
+
from endoreg_db.models import (
|
|
48
|
+
Examination,
|
|
49
|
+
ExaminationIndication,
|
|
50
|
+
ExaminationTime,
|
|
51
|
+
Finding,
|
|
52
|
+
FindingClassification,
|
|
53
|
+
FindingIntervention,
|
|
54
|
+
InformationSourceType,
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def examinations(self) -> "models.manager.RelatedManager[Examination]": ...
|
|
59
|
+
|
|
60
|
+
@property
|
|
61
|
+
def examination_indications(self) -> "models.manager.RelatedManager[ExaminationIndication]": ...
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def examination_times(self) -> "models.manager.RelatedManager[ExaminationTime]": ...
|
|
65
|
+
|
|
66
|
+
@property
|
|
67
|
+
def findings(self) -> "models.manager.RelatedManager[Finding]": ...
|
|
68
|
+
|
|
69
|
+
@property
|
|
70
|
+
def finding_interventions(self) -> "models.manager.RelatedManager[FindingIntervention]": ...
|
|
71
|
+
|
|
72
|
+
@property
|
|
73
|
+
def finding_classifications(self) -> "models.manager.RelatedManager[FindingClassification]": ...
|
|
53
74
|
|
|
54
75
|
class Meta:
|
|
55
76
|
verbose_name = "Information Source"
|
|
File without changes
|
|
File without changes
|