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
|
@@ -11,22 +11,25 @@ from django.core.validators import FileExtensionValidator
|
|
|
11
11
|
from django.db import models
|
|
12
12
|
from django.db.models import F
|
|
13
13
|
from django.db.models.fields.files import FieldFile
|
|
14
|
-
from librosa import frames_to_samples
|
|
15
|
-
from pandas.core import frame
|
|
16
14
|
|
|
17
15
|
from endoreg_db.utils.calc_duration_seconds import _calc_duration_vf
|
|
18
16
|
from endoreg_db.utils.video.ffmpeg_wrapper import assemble_video_from_frames
|
|
19
17
|
|
|
20
18
|
from ...label import Label, LabelVideoSegment
|
|
21
19
|
from ...state import VideoState
|
|
22
|
-
from
|
|
20
|
+
from endoreg_db.utils.paths import ANONYM_VIDEO_DIR, SENSITIVE_VIDEO_DIR
|
|
23
21
|
|
|
24
22
|
# --- Import model-specific function modules ---
|
|
25
23
|
from .create_from_file import _create_from_file
|
|
26
24
|
from .pipe_1 import _pipe_1, _test_after_pipe_1
|
|
27
25
|
from .pipe_2 import _pipe_2
|
|
28
26
|
from .video_file_ai import _extract_text_from_video_frames, _predict_video_pipeline
|
|
29
|
-
from .video_file_anonymize import
|
|
27
|
+
from .video_file_anonymize import (
|
|
28
|
+
_anonymize,
|
|
29
|
+
_censor_outside_frames,
|
|
30
|
+
_cleanup_raw_assets,
|
|
31
|
+
_create_anonymized_frame_files,
|
|
32
|
+
)
|
|
30
33
|
from .video_file_frames import (
|
|
31
34
|
_bulk_create_frames,
|
|
32
35
|
_create_frame_object,
|
|
@@ -85,7 +88,6 @@ if TYPE_CHECKING:
|
|
|
85
88
|
VideoImportMeta,
|
|
86
89
|
VideoMeta,
|
|
87
90
|
VideoState,
|
|
88
|
-
SensitiveMeta
|
|
89
91
|
)
|
|
90
92
|
|
|
91
93
|
|
|
@@ -115,7 +117,7 @@ class VideoFile(models.Model):
|
|
|
115
117
|
objects = VideoQuerySet.as_manager()
|
|
116
118
|
|
|
117
119
|
raw_file = models.FileField(
|
|
118
|
-
upload_to=
|
|
120
|
+
upload_to=SENSITIVE_VIDEO_DIR.name, # Use .name for relative path
|
|
119
121
|
validators=[FileExtensionValidator(allowed_extensions=["mp4"])],
|
|
120
122
|
null=True,
|
|
121
123
|
blank=True,
|
|
@@ -127,7 +129,9 @@ class VideoFile(models.Model):
|
|
|
127
129
|
blank=True,
|
|
128
130
|
)
|
|
129
131
|
|
|
130
|
-
video_hash = models.CharField(
|
|
132
|
+
video_hash = models.CharField(
|
|
133
|
+
max_length=255, unique=True, help_text="Hash of the raw video file."
|
|
134
|
+
)
|
|
131
135
|
processed_video_hash = models.CharField(
|
|
132
136
|
max_length=255,
|
|
133
137
|
unique=True,
|
|
@@ -144,7 +148,9 @@ class VideoFile(models.Model):
|
|
|
144
148
|
related_name="video_file",
|
|
145
149
|
)
|
|
146
150
|
center = models.ForeignKey("Center", on_delete=models.PROTECT)
|
|
147
|
-
processor = models.ForeignKey(
|
|
151
|
+
processor = models.ForeignKey(
|
|
152
|
+
"EndoscopyProcessor", on_delete=models.PROTECT, blank=True, null=True
|
|
153
|
+
)
|
|
148
154
|
video_meta = models.OneToOneField(
|
|
149
155
|
"VideoMeta",
|
|
150
156
|
on_delete=models.SET_NULL,
|
|
@@ -166,7 +172,9 @@ class VideoFile(models.Model):
|
|
|
166
172
|
null=True,
|
|
167
173
|
related_name="video_files",
|
|
168
174
|
)
|
|
169
|
-
ai_model_meta = models.ForeignKey(
|
|
175
|
+
ai_model_meta = models.ForeignKey(
|
|
176
|
+
"ModelMeta", on_delete=models.SET_NULL, blank=True, null=True
|
|
177
|
+
)
|
|
170
178
|
state = models.OneToOneField(
|
|
171
179
|
"VideoState",
|
|
172
180
|
on_delete=models.SET_NULL,
|
|
@@ -174,7 +182,9 @@ class VideoFile(models.Model):
|
|
|
174
182
|
blank=True,
|
|
175
183
|
related_name="video_file",
|
|
176
184
|
)
|
|
177
|
-
import_meta = models.OneToOneField(
|
|
185
|
+
import_meta = models.OneToOneField(
|
|
186
|
+
"VideoImportMeta", on_delete=models.CASCADE, blank=True, null=True
|
|
187
|
+
)
|
|
178
188
|
|
|
179
189
|
original_file_name = models.CharField(max_length=255, blank=True, null=True)
|
|
180
190
|
uploaded_at = models.DateTimeField(auto_now_add=True)
|
|
@@ -291,7 +301,9 @@ class VideoFile(models.Model):
|
|
|
291
301
|
bulk_create_frames = _bulk_create_frames
|
|
292
302
|
|
|
293
303
|
# Define new methods that call the helper functions
|
|
294
|
-
def extract_specific_frame_range(
|
|
304
|
+
def extract_specific_frame_range(
|
|
305
|
+
self, start_frame: int, end_frame: int, overwrite: bool = False, **kwargs
|
|
306
|
+
) -> bool:
|
|
295
307
|
"""
|
|
296
308
|
Extract frames from the video within the specified frame range.
|
|
297
309
|
|
|
@@ -314,9 +326,13 @@ class VideoFile(models.Model):
|
|
|
314
326
|
|
|
315
327
|
# Log if unexpected kwargs are passed, beyond those used by the helper
|
|
316
328
|
expected_helper_kwargs = {"quality", "ext", "verbose"}
|
|
317
|
-
unexpected_kwargs = {
|
|
329
|
+
unexpected_kwargs = {
|
|
330
|
+
k: v for k, v in kwargs.items() if k not in expected_helper_kwargs
|
|
331
|
+
}
|
|
318
332
|
if unexpected_kwargs:
|
|
319
|
-
logger.warning(
|
|
333
|
+
logger.warning(
|
|
334
|
+
f"Unexpected keyword arguments for extract_specific_frame_range, will be ignored by helper: {unexpected_kwargs}"
|
|
335
|
+
)
|
|
320
336
|
|
|
321
337
|
return _extract_frame_range_helper(
|
|
322
338
|
video=self,
|
|
@@ -332,7 +348,9 @@ class VideoFile(models.Model):
|
|
|
332
348
|
"""
|
|
333
349
|
Deletes frame files for a specific range [start_frame, end_frame).
|
|
334
350
|
"""
|
|
335
|
-
_delete_frame_range_helper(
|
|
351
|
+
_delete_frame_range_helper(
|
|
352
|
+
video=self, start_frame=start_frame, end_frame=end_frame
|
|
353
|
+
)
|
|
336
354
|
|
|
337
355
|
delete_with_file = _delete_with_file
|
|
338
356
|
get_base_frame_dir = _get_base_frame_dir
|
|
@@ -387,7 +405,9 @@ class VideoFile(models.Model):
|
|
|
387
405
|
if isinstance(raw, FieldFile) and raw.name:
|
|
388
406
|
return raw
|
|
389
407
|
|
|
390
|
-
raise ValueError(
|
|
408
|
+
raise ValueError(
|
|
409
|
+
"No active file available. VideoFile has neither raw nor processed file."
|
|
410
|
+
)
|
|
391
411
|
|
|
392
412
|
@property
|
|
393
413
|
def active_file_path(self) -> Path:
|
|
@@ -406,10 +426,14 @@ class VideoFile(models.Model):
|
|
|
406
426
|
elif active is self.raw_file:
|
|
407
427
|
path = _get_raw_file_path(self)
|
|
408
428
|
else:
|
|
409
|
-
raise ValueError(
|
|
429
|
+
raise ValueError(
|
|
430
|
+
"No active file path available. VideoFile has neither raw nor processed file."
|
|
431
|
+
)
|
|
410
432
|
|
|
411
433
|
if path is None:
|
|
412
|
-
raise ValueError(
|
|
434
|
+
raise ValueError(
|
|
435
|
+
"Active file path could not be resolved. VideoFile raw file is missing."
|
|
436
|
+
)
|
|
413
437
|
return path
|
|
414
438
|
|
|
415
439
|
@property
|
|
@@ -426,7 +450,9 @@ class VideoFile(models.Model):
|
|
|
426
450
|
self.uuid,
|
|
427
451
|
exc,
|
|
428
452
|
)
|
|
429
|
-
raise ValueError(
|
|
453
|
+
raise ValueError(
|
|
454
|
+
"Active file URL could not be resolved for this VideoFile."
|
|
455
|
+
) from exc
|
|
430
456
|
|
|
431
457
|
if not url:
|
|
432
458
|
raise ValueError("Active file URL is empty for this VideoFile.")
|
|
@@ -434,7 +460,9 @@ class VideoFile(models.Model):
|
|
|
434
460
|
return str(url)
|
|
435
461
|
|
|
436
462
|
@classmethod
|
|
437
|
-
def create_from_file(
|
|
463
|
+
def create_from_file(
|
|
464
|
+
cls, file_path: Union[str, Path], center_name: str, **kwargs
|
|
465
|
+
) -> Optional["VideoFile"]:
|
|
438
466
|
# Ensure file_path is a Path object
|
|
439
467
|
if isinstance(file_path, str):
|
|
440
468
|
file_path = Path(file_path)
|
|
@@ -443,7 +471,9 @@ class VideoFile(models.Model):
|
|
|
443
471
|
try:
|
|
444
472
|
center_name = os.environ["CENTER_NAME"]
|
|
445
473
|
except KeyError:
|
|
446
|
-
logger.error(
|
|
474
|
+
logger.error(
|
|
475
|
+
"Center name must be provided to create VideoFile from file. You can set CENTER_NAME in environment variables."
|
|
476
|
+
)
|
|
447
477
|
return None
|
|
448
478
|
return _create_from_file(cls, file_path, center_name=center_name, **kwargs)
|
|
449
479
|
|
|
@@ -503,7 +533,9 @@ class VideoFile(models.Model):
|
|
|
503
533
|
# Delete file storage
|
|
504
534
|
if self.raw_file and self.raw_file.storage.exists(self.raw_file.name):
|
|
505
535
|
self.raw_file.storage.delete(self.raw_file.name)
|
|
506
|
-
if self.processed_file and self.processed_file.storage.exists(
|
|
536
|
+
if self.processed_file and self.processed_file.storage.exists(
|
|
537
|
+
self.processed_file.name
|
|
538
|
+
):
|
|
507
539
|
self.processed_file.storage.delete(self.processed_file.name)
|
|
508
540
|
|
|
509
541
|
# Use proper database connection
|
|
@@ -530,7 +562,9 @@ class VideoFile(models.Model):
|
|
|
530
562
|
logger.error(f"Error deleting VideoFile {self.uuid}: {e}")
|
|
531
563
|
raise
|
|
532
564
|
|
|
533
|
-
def validate_metadata_annotation(
|
|
565
|
+
def validate_metadata_annotation(
|
|
566
|
+
self, extracted_data_dict: Optional[dict] = None
|
|
567
|
+
) -> bool:
|
|
534
568
|
"""
|
|
535
569
|
Validate the metadata of the VideoFile instance.
|
|
536
570
|
|
|
@@ -543,12 +577,9 @@ class VideoFile(models.Model):
|
|
|
543
577
|
video is preserved as the final validated output.
|
|
544
578
|
"""
|
|
545
579
|
|
|
546
|
-
if not self.sensitive_meta:
|
|
547
|
-
# Ensure a SensitiveMeta exists so validation can proceed.
|
|
548
|
-
self.sensitive_meta = self.get_or_create_sensitive_meta()
|
|
549
580
|
# CRITICAL FIX: Delete RAW video file, not the processed (anonymized) one
|
|
550
581
|
# CRITICAL: Update metadata BEFORE deleting raw video
|
|
551
|
-
if extracted_data_dict:
|
|
582
|
+
if extracted_data_dict and self.sensitive_meta:
|
|
552
583
|
self.sensitive_meta.update_from_dict(extracted_data_dict)
|
|
553
584
|
else:
|
|
554
585
|
return False
|
|
@@ -564,7 +595,9 @@ class VideoFile(models.Model):
|
|
|
564
595
|
# Clear the raw_file field in database (use delete() to avoid save issues)
|
|
565
596
|
if self.raw_file:
|
|
566
597
|
self.raw_file.delete(save=False)
|
|
567
|
-
logger.info(
|
|
598
|
+
logger.info(
|
|
599
|
+
f"Raw video deleted for {self.uuid}. Anonymized video preserved."
|
|
600
|
+
)
|
|
568
601
|
else:
|
|
569
602
|
logger.warning(
|
|
570
603
|
"Raw video file not found for deletion during validation %s.",
|
|
@@ -576,10 +609,14 @@ class VideoFile(models.Model):
|
|
|
576
609
|
self.get_or_create_state().mark_anonymization_validated(save=True)
|
|
577
610
|
# Save the VideoFile instance to persist changes
|
|
578
611
|
self.save()
|
|
579
|
-
logger.info(
|
|
612
|
+
logger.info(
|
|
613
|
+
f"Metadata annotation validated and saved for video {self.uuid}."
|
|
614
|
+
)
|
|
580
615
|
return True
|
|
581
616
|
else:
|
|
582
|
-
logger.error(
|
|
617
|
+
logger.error(
|
|
618
|
+
f"Failed to validate metadata annotation for video {self.uuid}."
|
|
619
|
+
)
|
|
583
620
|
return False
|
|
584
621
|
|
|
585
622
|
def initialize(self):
|
|
@@ -600,7 +637,6 @@ class VideoFile(models.Model):
|
|
|
600
637
|
# Create a new state if it doesn't exist
|
|
601
638
|
self.state = self.get_or_create_state()
|
|
602
639
|
|
|
603
|
-
self.sensitive_meta = self.get_or_create_sensitive_meta()
|
|
604
640
|
self.save()
|
|
605
641
|
# Initialize frames based on the video specs
|
|
606
642
|
self.initialize_frames()
|
|
@@ -613,7 +649,9 @@ class VideoFile(models.Model):
|
|
|
613
649
|
"""
|
|
614
650
|
active_path = self.active_file_path
|
|
615
651
|
file_name = active_path.name if active_path else "No file"
|
|
616
|
-
state =
|
|
652
|
+
state = (
|
|
653
|
+
"Processed" if self.is_processed else ("Raw" if self.has_raw else "No File")
|
|
654
|
+
)
|
|
617
655
|
return f"VideoFile ({state}): {file_name} (UUID: {self.uuid})"
|
|
618
656
|
|
|
619
657
|
# --- Convenience state/meta helpers used in tests and admin workflows ---
|
|
@@ -622,6 +660,11 @@ class VideoFile(models.Model):
|
|
|
622
660
|
Mark this video's processing state as having its sensitive meta fully processed.
|
|
623
661
|
This proxies to the related VideoState and persists by default.
|
|
624
662
|
"""
|
|
663
|
+
sm = self.sensitive_meta
|
|
664
|
+
from endoreg_db.models.metadata.sensitive_meta import SensitiveMeta
|
|
665
|
+
|
|
666
|
+
if not isinstance(sm, SensitiveMeta):
|
|
667
|
+
raise AttributeError()
|
|
625
668
|
state = self.get_or_create_state()
|
|
626
669
|
state.mark_sensitive_meta_processed(save=save)
|
|
627
670
|
return self
|
|
@@ -631,8 +674,13 @@ class VideoFile(models.Model):
|
|
|
631
674
|
Mark the associated SensitiveMeta as verified by setting both DOB and names as verified.
|
|
632
675
|
Ensures the SensitiveMeta and its state exist.
|
|
633
676
|
"""
|
|
634
|
-
sm = self.
|
|
677
|
+
sm = self.sensitive_meta
|
|
635
678
|
# Use SensitiveMeta methods to update underlying SensitiveMetaState
|
|
679
|
+
from endoreg_db.models.metadata.sensitive_meta import SensitiveMeta
|
|
680
|
+
|
|
681
|
+
if not isinstance(sm, SensitiveMeta):
|
|
682
|
+
raise AttributeError()
|
|
683
|
+
|
|
636
684
|
sm.mark_dob_verified()
|
|
637
685
|
sm.mark_names_verified()
|
|
638
686
|
return self
|
|
@@ -671,66 +719,9 @@ class VideoFile(models.Model):
|
|
|
671
719
|
|
|
672
720
|
return state
|
|
673
721
|
|
|
674
|
-
def
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
**Two-Phase Patient Data Pattern:**
|
|
679
|
-
This method implements a two-phase approach to handle incomplete patient data:
|
|
680
|
-
|
|
681
|
-
**Phase 1: Initial Creation (with defaults)**
|
|
682
|
-
- Creates SensitiveMeta with default patient data to prevent hash calculation errors
|
|
683
|
-
- Default values: patient_first_name="Patient", patient_last_name="Unknown", patient_dob=1990-01-01
|
|
684
|
-
- Allows video import to proceed even without extracted patient data
|
|
685
|
-
- Temporary hash and pseudo-entities are created
|
|
686
|
-
|
|
687
|
-
**Phase 2: Update (with extracted data)**
|
|
688
|
-
- Real patient data is extracted later (e.g., from video OCR via lx_anonymizer)
|
|
689
|
-
- update_from_dict() is called with actual patient information
|
|
690
|
-
- Hash is recalculated automatically using real data
|
|
691
|
-
- Correct pseudo-entities are created/linked based on new hash
|
|
692
|
-
|
|
693
|
-
**Example workflow:**
|
|
694
|
-
```python
|
|
695
|
-
# Phase 1: Video creation
|
|
696
|
-
video = VideoFile.create_from_file_initialized(...)
|
|
697
|
-
video.initialize() # Calls this method
|
|
698
|
-
# → SensitiveMeta created with defaults
|
|
699
|
-
# → Hash: sha256("Patient Unknown 1990-01-01...")
|
|
700
|
-
|
|
701
|
-
# Phase 2: Frame cleaning extracts real data
|
|
702
|
-
extracted = {"patient_first_name": "Max", "patient_last_name": "Mustermann", ...}
|
|
703
|
-
video.sensitive_meta.update_from_dict(extracted)
|
|
704
|
-
# → Hash: sha256("Max Mustermann 1985-03-15...") (RECALCULATED)
|
|
705
|
-
```
|
|
706
|
-
|
|
707
|
-
Returns:
|
|
708
|
-
SensitiveMeta: The related SensitiveMeta instance.
|
|
709
|
-
|
|
710
|
-
See Also:
|
|
711
|
-
- sensitive_meta_logic.perform_save_logic() for hash calculation details
|
|
712
|
-
- sensitive_meta_logic.update_sensitive_meta_from_dict() for update mechanism
|
|
713
|
-
"""
|
|
714
|
-
from datetime import date as dt_date
|
|
715
|
-
|
|
716
|
-
from endoreg_db.models import SensitiveMeta
|
|
717
|
-
|
|
718
|
-
if self.sensitive_meta is None:
|
|
719
|
-
# Use create_from_dict with default patient data
|
|
720
|
-
# to prevent "First name is required to calculate patient hash" error
|
|
721
|
-
default_data = {
|
|
722
|
-
"patient_first_name": "Patient",
|
|
723
|
-
"patient_last_name": "Unknown",
|
|
724
|
-
"patient_dob": dt_date(1990, 1, 1),
|
|
725
|
-
"examination_date": dt_date.today(),
|
|
726
|
-
"center": self.center,
|
|
727
|
-
}
|
|
728
|
-
self.sensitive_meta = SensitiveMeta.create_from_dict(default_data)
|
|
729
|
-
self.save(update_fields=["sensitive_meta"])
|
|
730
|
-
# Do not mark state as processed here; it will be set after extraction/validation steps
|
|
731
|
-
return self.sensitive_meta
|
|
732
|
-
|
|
733
|
-
def get_outside_segments(self, only_validated: bool = False) -> models.QuerySet["LabelVideoSegment"]:
|
|
722
|
+
def get_outside_segments(
|
|
723
|
+
self, only_validated: bool = False
|
|
724
|
+
) -> models.QuerySet["LabelVideoSegment"]:
|
|
734
725
|
"""
|
|
735
726
|
Return all video segments labeled as "outside" for this video.
|
|
736
727
|
|
|
@@ -762,7 +753,9 @@ class VideoFile(models.Model):
|
|
|
762
753
|
return self.label_video_segments.none()
|
|
763
754
|
|
|
764
755
|
@classmethod
|
|
765
|
-
def create_video_without_outside_frames(
|
|
756
|
+
def create_video_without_outside_frames(
|
|
757
|
+
cls, instance: "VideoFile", only_validated: bool = False
|
|
758
|
+
) -> bool:
|
|
766
759
|
"""
|
|
767
760
|
Creates a new video by excluding frames that belong to 'outside' segments.
|
|
768
761
|
|
|
@@ -775,40 +768,64 @@ class VideoFile(models.Model):
|
|
|
775
768
|
video = instance
|
|
776
769
|
|
|
777
770
|
if not video:
|
|
778
|
-
logger.warning(
|
|
771
|
+
logger.warning(
|
|
772
|
+
"No processed video file available for VideoFile %s.", cls.uuid
|
|
773
|
+
)
|
|
779
774
|
return False
|
|
780
775
|
try:
|
|
781
|
-
extracted = video.extract_frames(
|
|
776
|
+
extracted = video.extract_frames(
|
|
777
|
+
quality=2,
|
|
778
|
+
overwrite=False,
|
|
779
|
+
ext="jpg",
|
|
780
|
+
verbose=False,
|
|
781
|
+
from_processed=True,
|
|
782
|
+
)
|
|
782
783
|
assert extracted is True
|
|
783
784
|
except AssertionError:
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
785
|
+
extracted = video.extract_frames(
|
|
786
|
+
quality=2,
|
|
787
|
+
overwrite=False,
|
|
788
|
+
ext="jpg",
|
|
789
|
+
verbose=False,
|
|
790
|
+
from_processed=True,
|
|
791
|
+
)
|
|
787
792
|
assert extracted is True
|
|
788
793
|
try:
|
|
789
794
|
# Step 1: Get the "outside" labeled frames
|
|
790
795
|
censored = _censor_outside_frames(video)
|
|
791
796
|
frames = [instance.get_frame_dir_path()]
|
|
792
797
|
assert len(frames) != 0
|
|
793
|
-
fps =
|
|
798
|
+
fps = (
|
|
799
|
+
video.fps if video.fps else 120.0
|
|
800
|
+
) # Default to 30 FPS if fps is not set
|
|
801
|
+
assert censored is True
|
|
794
802
|
assert fps is not None
|
|
795
803
|
assert video.width is not None
|
|
796
804
|
assert video.height is not None
|
|
805
|
+
# assert isinstance(frames, list[Path]) #TODO improve TypeCheck
|
|
797
806
|
|
|
798
807
|
# Step 2: Reassemble the video with frames excluding the 'outside' labeled frames
|
|
799
808
|
output_video_path = Path(f"/path/to/output/{cls.uuid}_filtered.mp4")
|
|
800
809
|
fps = cls.fps if cls.fps else 30.0 # Default to 30 FPS if fps is not set
|
|
801
|
-
new_video_file = assemble_video_from_frames(
|
|
810
|
+
new_video_file = assemble_video_from_frames(
|
|
811
|
+
frames, output_video_path, fps, width=video.width, height=video.height
|
|
812
|
+
)
|
|
802
813
|
video.processed_file = new_video_file
|
|
803
814
|
return True
|
|
804
815
|
except AssertionError as ae:
|
|
805
|
-
logger.error(
|
|
816
|
+
logger.error(
|
|
817
|
+
f"Assertion error while creating video without 'outside' frames for VideoFile {cls.uuid}: {ae}",
|
|
818
|
+
exc_info=True,
|
|
819
|
+
)
|
|
806
820
|
return False
|
|
807
821
|
except Label.DoesNotExist:
|
|
808
822
|
logger.warning("Outside label not found in the database.")
|
|
809
823
|
return False
|
|
810
824
|
except Exception as e:
|
|
811
|
-
logger.error(
|
|
825
|
+
logger.error(
|
|
826
|
+
f"Error creating video without 'outside' frames for VideoFile {cls.uuid}: {e}",
|
|
827
|
+
exc_info=True,
|
|
828
|
+
)
|
|
812
829
|
return False
|
|
813
830
|
|
|
814
831
|
@classmethod
|
|
@@ -828,7 +845,9 @@ class VideoFile(models.Model):
|
|
|
828
845
|
int: The count of VideoFile records, excluding this instance, where the modification timestamp matches the creation timestamp.
|
|
829
846
|
"""
|
|
830
847
|
return (
|
|
831
|
-
VideoFile.objects.filter(
|
|
848
|
+
VideoFile.objects.filter(
|
|
849
|
+
date_modified=F("date_created")
|
|
850
|
+
) # compare the two fields in SQL
|
|
832
851
|
.exclude(pk=self.pk) # exclude this instance
|
|
833
852
|
.count() # run a fast COUNT(*) on the filtered set
|
|
834
853
|
)
|
|
@@ -851,7 +870,8 @@ class VideoFile(models.Model):
|
|
|
851
870
|
raise ValueError("FPS must be set and greater than zero.")
|
|
852
871
|
return frame_number / fps
|
|
853
872
|
|
|
854
|
-
|
|
873
|
+
@staticmethod
|
|
874
|
+
def get_video_by_pk(pk: int) -> "VideoFile":
|
|
855
875
|
"""
|
|
856
876
|
Retrieve a VideoFile instance by its primary key (ID).
|
|
857
877
|
|
|
@@ -864,4 +884,4 @@ class VideoFile(models.Model):
|
|
|
864
884
|
Raises:
|
|
865
885
|
VideoFile.DoesNotExist: If no VideoFile with the given ID exists.
|
|
866
886
|
"""
|
|
867
|
-
return
|
|
887
|
+
return VideoFile.objects.get(pk=video_id)
|
|
@@ -6,9 +6,9 @@ from typing import TYPE_CHECKING, Iterator, Optional
|
|
|
6
6
|
from django.db import transaction
|
|
7
7
|
|
|
8
8
|
from ....utils import delete_field_file, ensure_local_file, storage_file_exists
|
|
9
|
-
from
|
|
9
|
+
from endoreg_db.utils.paths import (
|
|
10
10
|
ANONYM_VIDEO_DIR,
|
|
11
|
-
|
|
11
|
+
SENSITIVE_VIDEO_DIR,
|
|
12
12
|
data_paths,
|
|
13
13
|
)
|
|
14
14
|
|
|
@@ -23,36 +23,41 @@ def _get_raw_file_path(video: "VideoFile") -> Optional[Path]:
|
|
|
23
23
|
if not (video.has_raw and video.raw_file.name):
|
|
24
24
|
return None
|
|
25
25
|
|
|
26
|
-
#
|
|
27
|
-
|
|
26
|
+
# 1) Canonical: use Django's storage path
|
|
27
|
+
try:
|
|
28
|
+
direct_path = Path(video.raw_file.path)
|
|
29
|
+
if direct_path.is_file():
|
|
30
|
+
return direct_path.resolve()
|
|
31
|
+
else:
|
|
32
|
+
logger.debug(
|
|
33
|
+
"raw_file.path for video %s is not a regular file: %s",
|
|
34
|
+
video.uuid,
|
|
35
|
+
direct_path,
|
|
36
|
+
)
|
|
37
|
+
except Exception as exc:
|
|
38
|
+
logger.debug(
|
|
39
|
+
"Could not access raw_file.path for video %s: %s",
|
|
40
|
+
video.uuid,
|
|
41
|
+
exc,
|
|
42
|
+
)
|
|
28
43
|
|
|
29
|
-
#
|
|
30
|
-
|
|
31
|
-
|
|
44
|
+
# 2) Fallback: use just the filename and search in known dirs
|
|
45
|
+
raw_rel = Path(video.raw_file.name)
|
|
46
|
+
filename = raw_rel.name # strip any (possibly wrong) prefix
|
|
32
47
|
|
|
33
|
-
|
|
34
|
-
|
|
48
|
+
candidates = [
|
|
49
|
+
data_paths["import_video"] / filename,
|
|
50
|
+
data_paths["sensitive_video"] / filename,
|
|
51
|
+
]
|
|
35
52
|
|
|
36
|
-
sensitive_path = data_paths["video"] / "sensitive" / rel_name
|
|
37
|
-
if sensitive_path.exists():
|
|
38
|
-
return sensitive_path.resolve()
|
|
39
53
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
if direct_path.exists():
|
|
44
|
-
return direct_path.resolve()
|
|
45
|
-
except Exception as exc:
|
|
46
|
-
logger.debug(
|
|
47
|
-
"Could not access direct raw_file.path for video %s: %s",
|
|
48
|
-
video.uuid,
|
|
49
|
-
exc,
|
|
50
|
-
)
|
|
54
|
+
for candidate in candidates:
|
|
55
|
+
if candidate.is_file():
|
|
56
|
+
return candidate.resolve()
|
|
51
57
|
|
|
52
58
|
logger.warning(
|
|
53
|
-
"Raw video file '%s' not found
|
|
54
|
-
|
|
55
|
-
data_paths["video"],
|
|
59
|
+
"Raw video file '%s' not found in import/sensitive paths or via stored FileField path for video %s.",
|
|
60
|
+
video.raw_file.name,
|
|
56
61
|
video.uuid,
|
|
57
62
|
)
|
|
58
63
|
return None
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
not yet properly implemented
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, List
|
|
1
|
+
from typing import TYPE_CHECKING, List, cast
|
|
2
2
|
|
|
3
3
|
from django.db import models
|
|
4
4
|
|
|
@@ -28,20 +28,40 @@ class Examination(models.Model):
|
|
|
28
28
|
name = models.CharField(max_length=100, unique=True)
|
|
29
29
|
examination_types = models.ManyToManyField("ExaminationType", blank=True)
|
|
30
30
|
description = models.TextField(blank=True, null=True)
|
|
31
|
+
indications = models.ManyToManyField(
|
|
32
|
+
"ExaminationIndication",
|
|
33
|
+
related_name="examinations",
|
|
34
|
+
blank=True,
|
|
35
|
+
)
|
|
36
|
+
examination_times = models.ManyToManyField(
|
|
37
|
+
"ExaminationTime",
|
|
38
|
+
related_name="examinations",
|
|
39
|
+
blank=True,
|
|
40
|
+
)
|
|
41
|
+
|
|
42
|
+
findings = models.ManyToManyField(
|
|
43
|
+
"Finding",
|
|
44
|
+
blank=True,
|
|
45
|
+
related_name="examinations",
|
|
46
|
+
)
|
|
47
|
+
information_sources = models.ManyToManyField(
|
|
48
|
+
"InformationSource",
|
|
49
|
+
related_name="examinations",
|
|
50
|
+
blank=True,
|
|
51
|
+
)
|
|
31
52
|
|
|
32
53
|
objects = ExaminationManager()
|
|
33
54
|
|
|
34
55
|
if TYPE_CHECKING:
|
|
35
|
-
from endoreg_db.models import ExaminationIndication, Finding, FindingClassification
|
|
56
|
+
from endoreg_db.models import ExaminationIndication, ExaminationTime, Finding, FindingClassification, InformationSource
|
|
36
57
|
|
|
37
|
-
|
|
38
|
-
|
|
58
|
+
indications = cast("models.manager.RelatedManager[ExaminationIndication]", indications)
|
|
59
|
+
examination_times = cast("models.manager.RelatedManager[ExaminationTime]", examination_times)
|
|
60
|
+
findings = cast("models.manager.RelatedManager[Finding]", findings)
|
|
61
|
+
information_sources = cast("models.manager.RelatedManager[InformationSource]", information_sources)
|
|
39
62
|
|
|
40
63
|
@property
|
|
41
|
-
def
|
|
42
|
-
|
|
43
|
-
@property
|
|
44
|
-
def findings(self) -> "models.manager.RelatedManager[Finding]": ...
|
|
64
|
+
def finding_classifications(self) -> "models.manager.RelatedManager[FindingClassification]": ...
|
|
45
65
|
|
|
46
66
|
@property
|
|
47
67
|
def exam_reqset_links(self) -> "models.manager.RelatedManager[ExaminationRequirementSet]": ...
|