endoreg-db 0.6.0__py3-none-any.whl → 0.6.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/case_generator/__init__.py +0 -0
- endoreg_db/case_generator/case_generator.py +159 -0
- endoreg_db/case_generator/lab_sample_factory.py +33 -0
- endoreg_db/case_generator/utils.py +30 -0
- endoreg_db/data/__init__.py +118 -0
- endoreg_db/data/agl_service/data.yaml +19 -0
- endoreg_db/data/ai_model/data.yaml +7 -0
- endoreg_db/data/ai_model_label/label/data.yaml +88 -0
- endoreg_db/data/ai_model_label/label-set/data.yaml +21 -0
- endoreg_db/data/ai_model_label/label-type/data.yaml +7 -0
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +5 -0
- endoreg_db/data/ai_model_type/data.yaml +7 -0
- endoreg_db/data/ai_model_video_segmentation_label/base_segmentation.yaml +176 -0
- endoreg_db/data/ai_model_video_segmentation_labelset/data.yaml +20 -0
- endoreg_db/data/case_template/rule/00_patient_lab_sample_add_default_value.yaml +167 -0
- endoreg_db/data/case_template/rule/01_patient-set-age.yaml +8 -0
- endoreg_db/data/case_template/rule/01_patient-set-gender.yaml +9 -0
- endoreg_db/data/case_template/rule/11_create_patient_lab_sample.yaml +23 -0
- endoreg_db/data/case_template/rule/12_create-patient_medication-anticoagulation.yaml +19 -0
- endoreg_db/data/case_template/rule/13_create-patient_medication_schedule-anticoagulation.yaml +19 -0
- endoreg_db/data/case_template/rule/19_create_patient.yaml +17 -0
- endoreg_db/data/case_template/rule_type/base_types.yaml +35 -0
- endoreg_db/data/case_template/rule_value/.init +0 -0
- endoreg_db/data/case_template/rule_value_type/base_types.yaml +59 -0
- endoreg_db/data/case_template/template/base.yaml +8 -0
- endoreg_db/data/case_template/template_type/pre_endoscopy.yaml +3 -0
- endoreg_db/data/case_template/tmp/_rule_value +13 -0
- endoreg_db/data/case_template/tmp/rule/01_atrial_fibrillation.yaml +21 -0
- endoreg_db/data/case_template/tmp/rule/02_create_object.yaml +10 -0
- endoreg_db/data/case_template/tmp/template/atrial_fibrillation_low_risk.yaml +7 -0
- endoreg_db/data/center/data.yaml +90 -0
- endoreg_db/data/center_resource/green_endoscopy_dashboard_CenterResource.yaml +144 -0
- endoreg_db/data/center_waste/green_endoscopy_dashboard_CenterWaste.yaml +48 -0
- endoreg_db/data/contraindication/bleeding.yaml +11 -0
- endoreg_db/data/disease/cardiovascular.yaml +37 -0
- endoreg_db/data/disease/hepatology.yaml +5 -0
- endoreg_db/data/disease/misc.yaml +6 -0
- endoreg_db/data/disease/renal.yaml +5 -0
- endoreg_db/data/disease_classification/chronic_kidney_disease.yaml +6 -0
- endoreg_db/data/disease_classification/coronary_vessel_disease.yaml +6 -0
- endoreg_db/data/disease_classification_choice/chronic_kidney_disease.yaml +41 -0
- endoreg_db/data/disease_classification_choice/coronary_vessel_disease.yaml +20 -0
- endoreg_db/data/distribution/date/patient.yaml +7 -0
- endoreg_db/data/distribution/multiple_categorical/.init +0 -0
- endoreg_db/data/distribution/numeric/data.yaml +14 -0
- endoreg_db/data/distribution/single_categorical/patient.yaml +7 -0
- endoreg_db/data/emission_factor/green_endoscopy_dashboard_EmissionFactor.yaml +132 -0
- endoreg_db/data/endoscope/data.yaml +93 -0
- endoreg_db/data/endoscope_type/data.yaml +11 -0
- endoreg_db/data/endoscopy_processor/data.yaml +47 -0
- endoreg_db/data/event/cardiology.yaml +28 -0
- endoreg_db/data/event/neurology.yaml +14 -0
- endoreg_db/data/event/surgery.yaml +13 -0
- endoreg_db/data/event/thrombembolism.yaml +20 -0
- endoreg_db/data/examination/examinations/data.yaml +66 -0
- endoreg_db/data/examination/time/data.yaml +48 -0
- endoreg_db/data/examination/time-type/data.yaml +8 -0
- endoreg_db/data/examination/type/data.yaml +5 -0
- endoreg_db/data/examination_indication/endoscopy.yaml +8 -0
- endoreg_db/data/examination_indication_classification/endoscopy.yaml +8 -0
- endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +101 -0
- endoreg_db/data/finding/data.yaml +141 -0
- endoreg_db/data/finding_intervention/endoscopy.yaml +138 -0
- endoreg_db/data/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/finding_location_classification/colonoscopy.yaml +46 -0
- endoreg_db/data/finding_location_classification_choice/colonoscopy.yaml +240 -0
- endoreg_db/data/finding_morphology_classification/colonoscopy.yaml +48 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_circularity_default.yaml +34 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_nice.yaml +20 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_paris.yaml +65 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_planarity_default.yaml +56 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_surface_intact_default.yaml +39 -0
- endoreg_db/data/finding_morphology_classification_choice/colonoscopy_size.yaml +57 -0
- endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +79 -0
- endoreg_db/data/finding_type/data.yaml +30 -0
- endoreg_db/data/gender/data.yaml +35 -0
- endoreg_db/data/information_source/data.yaml +30 -0
- endoreg_db/data/information_source/medication.yaml +6 -0
- endoreg_db/data/lab_value/cardiac_enzymes.yaml +37 -0
- endoreg_db/data/lab_value/coagulation.yaml +54 -0
- endoreg_db/data/lab_value/electrolytes.yaml +228 -0
- endoreg_db/data/lab_value/gastrointestinal_function.yaml +133 -0
- endoreg_db/data/lab_value/hematology.yaml +184 -0
- endoreg_db/data/lab_value/hormones.yaml +59 -0
- endoreg_db/data/lab_value/lipids.yaml +53 -0
- endoreg_db/data/lab_value/misc.yaml +33 -0
- endoreg_db/data/lab_value/renal_function.yaml +12 -0
- endoreg_db/data/log_type/data.yaml +57 -0
- endoreg_db/data/lx_client_tag/base.yaml +54 -0
- endoreg_db/data/lx_client_type/base.yaml +30 -0
- endoreg_db/data/lx_permission/base.yaml +24 -0
- endoreg_db/data/lx_permission/endoreg.yaml +52 -0
- endoreg_db/data/material/material.yaml +91 -0
- endoreg_db/data/medication/anticoagulation.yaml +65 -0
- endoreg_db/data/medication/tah.yaml +70 -0
- endoreg_db/data/medication_indication/anticoagulation.yaml +115 -0
- endoreg_db/data/medication_indication_type/data.yaml +11 -0
- endoreg_db/data/medication_indication_type/thrombembolism.yaml +41 -0
- endoreg_db/data/medication_intake_time/base.yaml +31 -0
- endoreg_db/data/medication_schedule/apixaban.yaml +95 -0
- endoreg_db/data/medication_schedule/ass.yaml +12 -0
- endoreg_db/data/medication_schedule/enoxaparin.yaml +26 -0
- endoreg_db/data/names_first/first_names.yaml +51 -0
- endoreg_db/data/names_last/last_names.yaml +51 -0
- endoreg_db/data/network_device/data.yaml +59 -0
- endoreg_db/data/network_device_type/data.yaml +12 -0
- endoreg_db/data/organ/data.yaml +29 -0
- endoreg_db/data/patient_lab_sample_type/generic.yaml +6 -0
- endoreg_db/data/pdf_type/data.yaml +29 -0
- endoreg_db/data/product/green_endoscopy_dashboard_Product.yaml +66 -0
- endoreg_db/data/product_group/green_endoscopy_dashboard_ProductGroup.yaml +33 -0
- endoreg_db/data/product_material/green_endoscopy_dashboard_ProductMaterial.yaml +308 -0
- endoreg_db/data/product_weight/green_endoscopy_dashboard_ProductWeight.yaml +88 -0
- endoreg_db/data/profession/data.yaml +70 -0
- endoreg_db/data/reference_product/green_endoscopy_dashboard_ReferenceProduct.yaml +55 -0
- endoreg_db/data/report_reader_flag/ukw-examination-generic.yaml +30 -0
- endoreg_db/data/report_reader_flag/ukw-histology-generic.yaml +19 -0
- endoreg_db/data/resource/green_endoscopy_dashboard_Resource.yaml +15 -0
- endoreg_db/data/tmp/chronic_kidney_disease.yaml +0 -0
- endoreg_db/data/tmp/congestive_heart_failure.yaml +0 -0
- endoreg_db/data/transport_route/green_endoscopy_dashboard_TransportRoute.yaml +12 -0
- endoreg_db/data/unit/concentration.yaml +92 -0
- endoreg_db/data/unit/data.yaml +17 -0
- endoreg_db/data/unit/length.yaml +31 -0
- endoreg_db/data/unit/misc.yaml +20 -0
- endoreg_db/data/unit/rate.yaml +6 -0
- endoreg_db/data/unit/time.yaml +13 -0
- endoreg_db/data/unit/volume.yaml +35 -0
- endoreg_db/data/unit/weight.yaml +38 -0
- endoreg_db/data/waste/data.yaml +12 -0
- endoreg_db/forms/__init__.py +5 -0
- endoreg_db/forms/examination_form.py +11 -0
- endoreg_db/forms/patient_finding_intervention_form.py +19 -0
- endoreg_db/forms/patient_form.py +26 -0
- endoreg_db/forms/questionnaires/__init__.py +1 -0
- endoreg_db/forms/questionnaires/tto_questionnaire.py +23 -0
- endoreg_db/forms/settings/__init__.py +8 -0
- endoreg_db/forms/unit.py +6 -0
- endoreg_db/management/__init__.py +0 -0
- endoreg_db/management/commands/__init__.py +0 -0
- endoreg_db/management/commands/_load_model_template.py +41 -0
- endoreg_db/management/commands/delete_all.py +18 -0
- endoreg_db/management/commands/fetch_legacy_image_dataset.py +32 -0
- endoreg_db/management/commands/fix_auth_permission.py +20 -0
- endoreg_db/management/commands/load_active_model_data.py +45 -0
- endoreg_db/management/commands/load_ai_model_data.py +79 -0
- endoreg_db/management/commands/load_ai_model_label_data.py +59 -0
- endoreg_db/management/commands/load_base_db_data.py +178 -0
- endoreg_db/management/commands/load_center_data.py +43 -0
- endoreg_db/management/commands/load_contraindication_data.py +41 -0
- endoreg_db/management/commands/load_disease_classification_choices_data.py +41 -0
- endoreg_db/management/commands/load_disease_classification_data.py +41 -0
- endoreg_db/management/commands/load_disease_data.py +62 -0
- endoreg_db/management/commands/load_distribution_data.py +66 -0
- endoreg_db/management/commands/load_endoscope_data.py +68 -0
- endoreg_db/management/commands/load_event_data.py +41 -0
- endoreg_db/management/commands/load_examination_data.py +75 -0
- endoreg_db/management/commands/load_examination_indication_data.py +65 -0
- endoreg_db/management/commands/load_finding_data.py +171 -0
- endoreg_db/management/commands/load_g_play_data.py +113 -0
- endoreg_db/management/commands/load_gender_data.py +44 -0
- endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +133 -0
- endoreg_db/management/commands/load_information_source.py +45 -0
- endoreg_db/management/commands/load_lab_value_data.py +50 -0
- endoreg_db/management/commands/load_logging_data.py +39 -0
- endoreg_db/management/commands/load_lx_data.py +64 -0
- endoreg_db/management/commands/load_medication_data.py +103 -0
- endoreg_db/management/commands/load_medication_indication_data.py +63 -0
- endoreg_db/management/commands/load_medication_indication_type_data.py +41 -0
- endoreg_db/management/commands/load_medication_intake_time_data.py +41 -0
- endoreg_db/management/commands/load_medication_schedule_data.py +55 -0
- endoreg_db/management/commands/load_name_data.py +37 -0
- endoreg_db/management/commands/load_network_data.py +57 -0
- endoreg_db/management/commands/load_organ_data.py +43 -0
- endoreg_db/management/commands/load_pdf_type_data.py +61 -0
- endoreg_db/management/commands/load_profession_data.py +44 -0
- endoreg_db/management/commands/load_report_reader_flag_data.py +46 -0
- endoreg_db/management/commands/load_unit_data.py +46 -0
- endoreg_db/management/commands/load_user_groups.py +28 -0
- endoreg_db/management/commands/register_ai_model.py +64 -0
- endoreg_db/management/commands/reset_celery_schedule.py +9 -0
- endoreg_db/migrations/0001_initial.py +2045 -0
- endoreg_db/migrations/0002_alter_frame_image_alter_rawframe_image.py +23 -0
- endoreg_db/migrations/0003_alter_frame_image_alter_rawframe_image.py +23 -0
- endoreg_db/migrations/0004_alter_rawvideofile_file_alter_video_file.py +25 -0
- endoreg_db/migrations/0005_rawvideofile_frame_count_and_more.py +33 -0
- endoreg_db/migrations/0006_frame_extracted_rawframe_extracted.py +23 -0
- endoreg_db/migrations/0007_rename_pseudo_patient_video_patient_and_more.py +24 -0
- endoreg_db/migrations/0008_remove_reportfile_patient_examination_and_more.py +48 -0
- endoreg_db/migrations/__init__.py +0 -0
- endoreg_db/models/__init__.py +376 -0
- endoreg_db/models/ai_model/__init__.py +4 -0
- endoreg_db/models/ai_model/active_model.py +9 -0
- endoreg_db/models/ai_model/ai_model.py +103 -0
- endoreg_db/models/ai_model/lightning/__init__.py +3 -0
- endoreg_db/models/ai_model/lightning/inference_dataset.py +53 -0
- endoreg_db/models/ai_model/lightning/multilabel_classification_net.py +155 -0
- endoreg_db/models/ai_model/lightning/postprocess.py +53 -0
- endoreg_db/models/ai_model/lightning/predict.py +172 -0
- endoreg_db/models/ai_model/lightning/prediction_visualizer.py +55 -0
- endoreg_db/models/ai_model/lightning/preprocess.py +68 -0
- endoreg_db/models/ai_model/lightning/run_visualizer.py +21 -0
- endoreg_db/models/ai_model/model_meta.py +250 -0
- endoreg_db/models/ai_model/model_type.py +36 -0
- endoreg_db/models/ai_model/utils.py +8 -0
- endoreg_db/models/annotation/__init__.py +32 -0
- endoreg_db/models/annotation/anonymized_image_annotation.py +115 -0
- endoreg_db/models/annotation/binary_classification_annotation_task.py +117 -0
- endoreg_db/models/annotation/image_classification.py +86 -0
- endoreg_db/models/annotation/video_segmentation_annotation.py +52 -0
- endoreg_db/models/annotation/video_segmentation_labelset.py +20 -0
- endoreg_db/models/case/__init__.py +1 -0
- endoreg_db/models/case/case.py +34 -0
- endoreg_db/models/case_template/__init__.py +15 -0
- endoreg_db/models/case_template/case_template.py +125 -0
- endoreg_db/models/case_template/case_template_rule.py +276 -0
- endoreg_db/models/case_template/case_template_rule_value.py +88 -0
- endoreg_db/models/case_template/case_template_type.py +28 -0
- endoreg_db/models/center/__init__.py +11 -0
- endoreg_db/models/center/center.py +51 -0
- endoreg_db/models/center/center_product.py +33 -0
- endoreg_db/models/center/center_resource.py +33 -0
- endoreg_db/models/center/center_waste.py +16 -0
- endoreg_db/models/contraindication/__init__.py +21 -0
- endoreg_db/models/data_file/__init__.py +39 -0
- endoreg_db/models/data_file/base_classes/__init__.py +7 -0
- endoreg_db/models/data_file/base_classes/abstract_frame.py +100 -0
- endoreg_db/models/data_file/base_classes/abstract_pdf.py +136 -0
- endoreg_db/models/data_file/base_classes/abstract_video.py +807 -0
- endoreg_db/models/data_file/base_classes/frame_helpers.py +17 -0
- endoreg_db/models/data_file/base_classes/prepare_bulk_frames.py +19 -0
- endoreg_db/models/data_file/base_classes/utils.py +80 -0
- endoreg_db/models/data_file/frame.py +29 -0
- endoreg_db/models/data_file/import_classes/__init__.py +18 -0
- endoreg_db/models/data_file/import_classes/processing_functions/__init__.py +35 -0
- endoreg_db/models/data_file/import_classes/processing_functions/pdf.py +28 -0
- endoreg_db/models/data_file/import_classes/processing_functions/video.py +260 -0
- endoreg_db/models/data_file/import_classes/raw_pdf.py +260 -0
- endoreg_db/models/data_file/import_classes/raw_video.py +288 -0
- endoreg_db/models/data_file/metadata/__init__.py +13 -0
- endoreg_db/models/data_file/metadata/pdf_meta.py +74 -0
- endoreg_db/models/data_file/metadata/sensitive_meta.py +290 -0
- endoreg_db/models/data_file/metadata/video_meta.py +199 -0
- endoreg_db/models/data_file/report_file.py +56 -0
- endoreg_db/models/data_file/video/__init__.py +11 -0
- endoreg_db/models/data_file/video/import_meta.py +25 -0
- endoreg_db/models/data_file/video/video.py +196 -0
- endoreg_db/models/data_file/video_segment.py +214 -0
- endoreg_db/models/disease.py +79 -0
- endoreg_db/models/emission/__init__.py +5 -0
- endoreg_db/models/emission/emission_factor.py +85 -0
- endoreg_db/models/event.py +73 -0
- endoreg_db/models/examination/__init__.py +9 -0
- endoreg_db/models/examination/examination.py +67 -0
- endoreg_db/models/examination/examination_indication.py +170 -0
- endoreg_db/models/examination/examination_time.py +53 -0
- endoreg_db/models/examination/examination_time_type.py +48 -0
- endoreg_db/models/examination/examination_type.py +40 -0
- endoreg_db/models/finding/__init__.py +11 -0
- endoreg_db/models/finding/finding.py +75 -0
- endoreg_db/models/finding/finding_intervention.py +60 -0
- endoreg_db/models/finding/finding_location_classification.py +94 -0
- endoreg_db/models/finding/finding_morphology_classification.py +89 -0
- endoreg_db/models/finding/finding_type.py +22 -0
- endoreg_db/models/hardware/__init__.py +2 -0
- endoreg_db/models/hardware/endoscope.py +60 -0
- endoreg_db/models/hardware/endoscopy_processor.py +155 -0
- endoreg_db/models/information_source.py +29 -0
- endoreg_db/models/label/__init__.py +1 -0
- endoreg_db/models/label/label.py +112 -0
- endoreg_db/models/laboratory/__init__.py +1 -0
- endoreg_db/models/laboratory/lab_value.py +111 -0
- endoreg_db/models/logging/__init__.py +11 -0
- endoreg_db/models/logging/agl_service.py +19 -0
- endoreg_db/models/logging/base.py +22 -0
- endoreg_db/models/logging/log_type.py +23 -0
- endoreg_db/models/logging/network_device.py +27 -0
- endoreg_db/models/lx/__init__.py +4 -0
- endoreg_db/models/lx/client.py +57 -0
- endoreg_db/models/lx/identity.py +34 -0
- endoreg_db/models/lx/permission.py +18 -0
- endoreg_db/models/lx/user.py +16 -0
- endoreg_db/models/medication/__init__.py +19 -0
- endoreg_db/models/medication/medication.py +33 -0
- endoreg_db/models/medication/medication_indication.py +50 -0
- endoreg_db/models/medication/medication_indication_type.py +34 -0
- endoreg_db/models/medication/medication_intake_time.py +26 -0
- endoreg_db/models/medication/medication_schedule.py +37 -0
- endoreg_db/models/network/__init__.py +9 -0
- endoreg_db/models/network/agl_service.py +38 -0
- endoreg_db/models/network/network_device.py +58 -0
- endoreg_db/models/network/network_device_type.py +23 -0
- endoreg_db/models/organ/__init__.py +38 -0
- endoreg_db/models/other/__init__.py +23 -0
- endoreg_db/models/other/distribution/__init__.py +44 -0
- endoreg_db/models/other/distribution/base_value_distribution.py +20 -0
- endoreg_db/models/other/distribution/date_value_distribution.py +91 -0
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +32 -0
- endoreg_db/models/other/distribution/numeric_value_distribution.py +97 -0
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +22 -0
- endoreg_db/models/other/distribution.py +5 -0
- endoreg_db/models/other/material.py +20 -0
- endoreg_db/models/other/resource.py +18 -0
- endoreg_db/models/other/transport_route.py +22 -0
- endoreg_db/models/other/waste.py +20 -0
- endoreg_db/models/patient/__init__.py +24 -0
- endoreg_db/models/patient/patient_examination.py +182 -0
- endoreg_db/models/patient/patient_finding.py +143 -0
- endoreg_db/models/patient/patient_finding_intervention.py +26 -0
- endoreg_db/models/patient/patient_finding_location.py +120 -0
- endoreg_db/models/patient/patient_finding_morphology.py +166 -0
- endoreg_db/models/permissions/__init__.py +44 -0
- endoreg_db/models/persons/__init__.py +34 -0
- endoreg_db/models/persons/examiner/__init__.py +2 -0
- endoreg_db/models/persons/examiner/examiner.py +60 -0
- endoreg_db/models/persons/examiner/examiner_type.py +2 -0
- endoreg_db/models/persons/first_name.py +18 -0
- endoreg_db/models/persons/gender.py +22 -0
- endoreg_db/models/persons/last_name.py +20 -0
- endoreg_db/models/persons/patient/__init__.py +8 -0
- endoreg_db/models/persons/patient/patient.py +389 -0
- endoreg_db/models/persons/patient/patient_disease.py +22 -0
- endoreg_db/models/persons/patient/patient_event.py +52 -0
- endoreg_db/models/persons/patient/patient_examination_indication.py +32 -0
- endoreg_db/models/persons/patient/patient_lab_sample.py +108 -0
- endoreg_db/models/persons/patient/patient_lab_value.py +197 -0
- endoreg_db/models/persons/patient/patient_medication.py +59 -0
- endoreg_db/models/persons/patient/patient_medication_schedule.py +88 -0
- endoreg_db/models/persons/person.py +31 -0
- endoreg_db/models/persons/portal_user_information.py +27 -0
- endoreg_db/models/prediction/__init__.py +8 -0
- endoreg_db/models/prediction/image_classification.py +51 -0
- endoreg_db/models/prediction/video_prediction_meta.py +306 -0
- endoreg_db/models/product/__init__.py +14 -0
- endoreg_db/models/product/product.py +110 -0
- endoreg_db/models/product/product_group.py +27 -0
- endoreg_db/models/product/product_material.py +28 -0
- endoreg_db/models/product/product_weight.py +38 -0
- endoreg_db/models/product/reference_product.py +115 -0
- endoreg_db/models/questionnaires/__init__.py +114 -0
- endoreg_db/models/quiz/__init__.py +9 -0
- endoreg_db/models/quiz/quiz_answer.py +41 -0
- endoreg_db/models/quiz/quiz_question.py +54 -0
- endoreg_db/models/report_reader/__init__.py +7 -0
- endoreg_db/models/report_reader/report_reader_config.py +53 -0
- endoreg_db/models/report_reader/report_reader_flag.py +20 -0
- endoreg_db/models/rules/__init__.py +5 -0
- endoreg_db/models/rules/rule.py +24 -0
- endoreg_db/models/rules/rule_applicator.py +224 -0
- endoreg_db/models/rules/rule_attribute_dtype.py +19 -0
- endoreg_db/models/rules/rule_type.py +22 -0
- endoreg_db/models/rules/ruleset.py +19 -0
- endoreg_db/models/unit.py +22 -0
- endoreg_db/queries/__init__.py +5 -0
- endoreg_db/queries/annotations/__init__.py +3 -0
- endoreg_db/queries/annotations/legacy.py +158 -0
- endoreg_db/queries/get/__init__.py +6 -0
- endoreg_db/queries/get/annotation.py +0 -0
- endoreg_db/queries/get/center.py +42 -0
- endoreg_db/queries/get/model.py +13 -0
- endoreg_db/queries/get/patient.py +14 -0
- endoreg_db/queries/get/patient_examination.py +20 -0
- endoreg_db/queries/get/prediction.py +0 -0
- endoreg_db/queries/get/report_file.py +33 -0
- endoreg_db/queries/get/video.py +31 -0
- endoreg_db/queries/get/video_import_meta.py +0 -0
- endoreg_db/queries/get/video_prediction_meta.py +0 -0
- endoreg_db/queries/sanity/__init_.py +0 -0
- endoreg_db/serializers/__init__.py +10 -0
- endoreg_db/serializers/ai_model.py +19 -0
- endoreg_db/serializers/annotation.py +14 -0
- endoreg_db/serializers/center.py +11 -0
- endoreg_db/serializers/examination.py +33 -0
- endoreg_db/serializers/frame.py +9 -0
- endoreg_db/serializers/hardware.py +21 -0
- endoreg_db/serializers/label.py +22 -0
- endoreg_db/serializers/patient.py +33 -0
- endoreg_db/serializers/prediction.py +10 -0
- endoreg_db/serializers/raw_video_meta_validation.py +13 -0
- endoreg_db/serializers/report_file.py +7 -0
- endoreg_db/serializers/video.py +20 -0
- endoreg_db/serializers/video_segmentation.py +492 -0
- endoreg_db/templates/admin/patient_finding_intervention.html +253 -0
- endoreg_db/templates/admin/start_examination.html +12 -0
- endoreg_db/templates/timeline.html +176 -0
- endoreg_db/utils/__init__.py +36 -0
- endoreg_db/utils/cropping.py +29 -0
- endoreg_db/utils/dataloader.py +118 -0
- endoreg_db/utils/dates.py +39 -0
- endoreg_db/utils/file_operations.py +30 -0
- endoreg_db/utils/hashs.py +152 -0
- endoreg_db/utils/legacy_ocr.py +201 -0
- endoreg_db/utils/names.py +74 -0
- endoreg_db/utils/ocr.py +190 -0
- endoreg_db/utils/parse_and_generate_yaml.py +46 -0
- endoreg_db/utils/pydantic_models/__init__.py +6 -0
- endoreg_db/utils/pydantic_models/db_config.py +57 -0
- endoreg_db/utils/uuid.py +4 -0
- endoreg_db/utils/validate_endo_roi.py +19 -0
- endoreg_db/utils/validate_subcategory_dict.py +91 -0
- endoreg_db/utils/video/__init__.py +13 -0
- endoreg_db/utils/video/extract_frames.py +121 -0
- endoreg_db/utils/video/transcode_videofile.py +111 -0
- endoreg_db/views/__init__.py +2 -0
- endoreg_db/views/csrf.py +7 -0
- endoreg_db/views/patient_views.py +90 -0
- endoreg_db/views/raw_video_meta_validation_views.py +38 -0
- endoreg_db/views/report_views.py +96 -0
- endoreg_db/views/video_segmentation_views.py +149 -0
- endoreg_db/views/views_for_timeline.py +46 -0
- {endoreg_db-0.6.0.dist-info → endoreg_db-0.6.2.dist-info}/METADATA +14 -4
- endoreg_db-0.6.2.dist-info/RECORD +420 -0
- {endoreg_db-0.6.0.dist-info → endoreg_db-0.6.2.dist-info}/WHEEL +1 -2
- endoreg_db-0.6.0.dist-info/RECORD +0 -11
- endoreg_db-0.6.0.dist-info/top_level.txt +0 -1
- {endoreg_db-0.6.0.dist-info → endoreg_db-0.6.2.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# models/data_file/import_classes/raw_pdf.py
|
|
2
|
+
# django db model "RawPdf"
|
|
3
|
+
# Class to store raw pdf file using django file field
|
|
4
|
+
# Class contains classmethod to create object from pdf file
|
|
5
|
+
# objects contains methods to extract text, extract metadata from text and anonymize text from pdf file uzing agl_report_reader.ReportReader class
|
|
6
|
+
# ------------------------------------------------------------------------------
|
|
7
|
+
|
|
8
|
+
from django.db import models
|
|
9
|
+
from django.core.files.storage import FileSystemStorage
|
|
10
|
+
from django.conf import settings
|
|
11
|
+
from django.core.exceptions import ValidationError
|
|
12
|
+
from django.core.validators import FileExtensionValidator
|
|
13
|
+
from endoreg_db.utils.file_operations import get_uuid_filename
|
|
14
|
+
from icecream import ic
|
|
15
|
+
|
|
16
|
+
from agl_report_reader.report_reader import ReportReader
|
|
17
|
+
|
|
18
|
+
from endoreg_db.utils.hashs import get_pdf_hash
|
|
19
|
+
from ..metadata import SensitiveMeta
|
|
20
|
+
from ..base_classes.abstract_pdf import AbstractPdfFile
|
|
21
|
+
|
|
22
|
+
# setup logging to pdf_import.log
|
|
23
|
+
import logging
|
|
24
|
+
|
|
25
|
+
import shutil
|
|
26
|
+
from pathlib import Path
|
|
27
|
+
|
|
28
|
+
from ..base_classes.utils import (
|
|
29
|
+
STORAGE_LOCATION,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
logger = logging.getLogger("pdf_import")
|
|
33
|
+
|
|
34
|
+
RAW_PDF_DIR_NAME = "raw_pdf"
|
|
35
|
+
RAW_PDF_DIR = STORAGE_LOCATION / RAW_PDF_DIR_NAME
|
|
36
|
+
|
|
37
|
+
if not RAW_PDF_DIR.exists():
|
|
38
|
+
RAW_PDF_DIR.mkdir(parents=True)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class RawPdfFile(AbstractPdfFile):
|
|
42
|
+
file = models.FileField(
|
|
43
|
+
upload_to=f"{RAW_PDF_DIR_NAME}/",
|
|
44
|
+
validators=[FileExtensionValidator(allowed_extensions=["pdf"])],
|
|
45
|
+
storage=FileSystemStorage(location=STORAGE_LOCATION.resolve().as_posix()),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
patient = models.ForeignKey(
|
|
49
|
+
"Patient",
|
|
50
|
+
on_delete=models.SET_NULL,
|
|
51
|
+
blank=True,
|
|
52
|
+
null=True,
|
|
53
|
+
related_name="raw_pdf_files",
|
|
54
|
+
)
|
|
55
|
+
|
|
56
|
+
state_report_processing_required = models.BooleanField(default=True)
|
|
57
|
+
state_report_processed = models.BooleanField(default=False)
|
|
58
|
+
raw_meta = models.JSONField(blank=True, null=True)
|
|
59
|
+
# report_file = models.OneToOneField("ReportFile", on_delete=models.CASCADE, null=True, blank=True)
|
|
60
|
+
sensitive_meta = models.ForeignKey(
|
|
61
|
+
"SensitiveMeta",
|
|
62
|
+
on_delete=models.SET_NULL,
|
|
63
|
+
related_name="raw_pdf_files",
|
|
64
|
+
null=True,
|
|
65
|
+
blank=True,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
report_file = models.ForeignKey(
|
|
69
|
+
"ReportFile",
|
|
70
|
+
on_delete=models.SET_NULL,
|
|
71
|
+
related_name="raw_pdf_files",
|
|
72
|
+
null=True,
|
|
73
|
+
blank=True,
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
anonymized_text = models.TextField(blank=True, null=True)
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def create_from_file(
|
|
80
|
+
cls,
|
|
81
|
+
file_path: Path,
|
|
82
|
+
center_name,
|
|
83
|
+
save=True,
|
|
84
|
+
delete_source=True,
|
|
85
|
+
):
|
|
86
|
+
from endoreg_db.models import Center
|
|
87
|
+
|
|
88
|
+
logger.info(f"Creating RawPdfFile object from file: {file_path}")
|
|
89
|
+
ic(f"Creating RawPdfFile object from file: {file_path}")
|
|
90
|
+
|
|
91
|
+
new_file_name, uuid = get_uuid_filename(file_path)
|
|
92
|
+
|
|
93
|
+
pdf_hash = get_pdf_hash(file_path)
|
|
94
|
+
ic(pdf_hash)
|
|
95
|
+
new_file_path = RAW_PDF_DIR / new_file_name
|
|
96
|
+
# check if pdf file already exists
|
|
97
|
+
|
|
98
|
+
if cls.objects.filter(pdf_hash=pdf_hash).exists():
|
|
99
|
+
existing_pdf_file = cls.objects.filter(pdf_hash=pdf_hash).get()
|
|
100
|
+
logger.warning(f"RawPdfFile with hash {pdf_hash} already exists")
|
|
101
|
+
ic(f"RawPdfFile with hash {pdf_hash} already exists")
|
|
102
|
+
|
|
103
|
+
existing_pdf_file.verify_existing_file(fallback_file=file_path)
|
|
104
|
+
|
|
105
|
+
return existing_pdf_file
|
|
106
|
+
|
|
107
|
+
else:
|
|
108
|
+
ic(f"No existing pdf file found for hash {pdf_hash}")
|
|
109
|
+
|
|
110
|
+
# assert pdf_type_name is not None, "pdf_type_name is required"
|
|
111
|
+
assert center_name is not None, "center_name is required"
|
|
112
|
+
|
|
113
|
+
# pdf_type = PdfType.objects.get(name=pdf_type_name)
|
|
114
|
+
center = Center.objects.get(name=center_name)
|
|
115
|
+
|
|
116
|
+
logger.info(f"Copying file to {new_file_path}")
|
|
117
|
+
ic(f"Copying file to {new_file_path}")
|
|
118
|
+
_success = shutil.copy(file_path, new_file_path)
|
|
119
|
+
|
|
120
|
+
# validate copy operation by comparing hashs
|
|
121
|
+
assert get_pdf_hash(new_file_path) == pdf_hash, "Copy operation failed"
|
|
122
|
+
|
|
123
|
+
raw_pdf = cls(
|
|
124
|
+
file=new_file_path.resolve().as_posix(),
|
|
125
|
+
pdf_hash=pdf_hash,
|
|
126
|
+
# pdf_type=pdf_type,
|
|
127
|
+
center=center,
|
|
128
|
+
)
|
|
129
|
+
raw_pdf.save()
|
|
130
|
+
logger.info(f"RawPdfFile object created: {raw_pdf}")
|
|
131
|
+
ic(f"RawPdfFile object created: {raw_pdf}")
|
|
132
|
+
|
|
133
|
+
# remove source file
|
|
134
|
+
if delete_source:
|
|
135
|
+
file_path.unlink()
|
|
136
|
+
logger.info(f"Source file removed: {file_path}")
|
|
137
|
+
ic(f"Source file removed: {file_path}")
|
|
138
|
+
|
|
139
|
+
if save:
|
|
140
|
+
raw_pdf.save()
|
|
141
|
+
|
|
142
|
+
return raw_pdf
|
|
143
|
+
|
|
144
|
+
def save(self, *args, **kwargs):
|
|
145
|
+
if not self.file.name.endswith(".pdf"):
|
|
146
|
+
raise ValidationError("Only PDF files are allowed")
|
|
147
|
+
|
|
148
|
+
if not self.pdf_hash:
|
|
149
|
+
self.pdf_hash = get_pdf_hash(self.file.path)
|
|
150
|
+
|
|
151
|
+
super().save(*args, **kwargs)
|
|
152
|
+
|
|
153
|
+
def verify_existing_file(self, fallback_file):
|
|
154
|
+
if not Path(self.file.path).exists():
|
|
155
|
+
logger.warning(f"File not found: {self.file.path}")
|
|
156
|
+
logger.warning(f"Using fallback file: {fallback_file}")
|
|
157
|
+
ic(f"File not found: {self.file.path}")
|
|
158
|
+
ic(f"Copy fallback file: {fallback_file} to existing filepath")
|
|
159
|
+
|
|
160
|
+
shutil.copy(fallback_file, self.file.path)
|
|
161
|
+
|
|
162
|
+
self.save()
|
|
163
|
+
|
|
164
|
+
def process_file(self, verbose=False):
|
|
165
|
+
pdf_path = self.file.path
|
|
166
|
+
rr_config = self.get_report_reader_config()
|
|
167
|
+
|
|
168
|
+
rr = ReportReader(
|
|
169
|
+
**rr_config
|
|
170
|
+
) # FIXME In future we need to pass a configuration file
|
|
171
|
+
# This configuration file should be associated with pdf type
|
|
172
|
+
|
|
173
|
+
text, anonymized_text, report_meta = rr.process_report(
|
|
174
|
+
pdf_path, verbose=verbose
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
self.text = text
|
|
178
|
+
self.anonymized_text = anonymized_text
|
|
179
|
+
|
|
180
|
+
report_meta["center_name"] = self.center.name
|
|
181
|
+
if not self.sensitive_meta:
|
|
182
|
+
sensitive_meta = SensitiveMeta.create_from_dict(report_meta)
|
|
183
|
+
self.sensitive_meta = sensitive_meta
|
|
184
|
+
|
|
185
|
+
else:
|
|
186
|
+
# update existing sensitive meta
|
|
187
|
+
sensitive_meta = self.sensitive_meta
|
|
188
|
+
sensitive_meta.update_from_dict(report_meta)
|
|
189
|
+
|
|
190
|
+
self.raw_meta = report_meta
|
|
191
|
+
|
|
192
|
+
sensitive_meta.save()
|
|
193
|
+
self.save()
|
|
194
|
+
|
|
195
|
+
return text, anonymized_text, report_meta
|
|
196
|
+
|
|
197
|
+
def get_report_reader_config(self):
|
|
198
|
+
from endoreg_db.models import PdfType, Center
|
|
199
|
+
from warnings import warn
|
|
200
|
+
|
|
201
|
+
if not self.pdf_type:
|
|
202
|
+
warn("PdfType not set, using default settings")
|
|
203
|
+
pdf_type = PdfType.default_pdf_type()
|
|
204
|
+
else:
|
|
205
|
+
pdf_type: PdfType = self.pdf_type
|
|
206
|
+
center: Center = self.center
|
|
207
|
+
if pdf_type.endoscope_info_line:
|
|
208
|
+
endoscope_info_line = pdf_type.endoscope_info_line.value
|
|
209
|
+
|
|
210
|
+
else:
|
|
211
|
+
endoscope_info_line = None
|
|
212
|
+
settings_dict = {
|
|
213
|
+
"locale": "de_DE",
|
|
214
|
+
"employee_first_names": [_.name for _ in center.first_names.all()],
|
|
215
|
+
"employee_last_names": [_.name for _ in center.last_names.all()],
|
|
216
|
+
"text_date_format": "%d.%m.%Y",
|
|
217
|
+
"flags": {
|
|
218
|
+
"patient_info_line": pdf_type.patient_info_line.value,
|
|
219
|
+
"endoscope_info_line": endoscope_info_line,
|
|
220
|
+
"examiner_info_line": pdf_type.examiner_info_line.value,
|
|
221
|
+
"cut_off_below": [_.value for _ in pdf_type.cut_off_below_lines.all()],
|
|
222
|
+
"cut_off_above": [_.value for _ in pdf_type.cut_off_above_lines.all()],
|
|
223
|
+
},
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
return settings_dict
|
|
227
|
+
|
|
228
|
+
def get_or_create_report_file(self):
|
|
229
|
+
from endoreg_db.models import ReportFile
|
|
230
|
+
|
|
231
|
+
if self.report_file:
|
|
232
|
+
report_file = self.report_file
|
|
233
|
+
|
|
234
|
+
elif ReportFile.objects.filter(pdf_hash=self.pdf_hash).exists():
|
|
235
|
+
report_file = ReportFile.objects.filter(pdf_hash=self.pdf_hash).get()
|
|
236
|
+
self.report_file = report_file
|
|
237
|
+
self.save()
|
|
238
|
+
else:
|
|
239
|
+
# TODO Make sure all required states are set
|
|
240
|
+
patient = self.sensitive_meta.get_or_create_pseudo_patient()
|
|
241
|
+
examiner = self.sensitive_meta.get_or_create_pseudo_examiner()
|
|
242
|
+
patient_examination = (
|
|
243
|
+
self.sensitive_meta.get_or_create_pseudo_patient_examination()
|
|
244
|
+
)
|
|
245
|
+
|
|
246
|
+
report_file = ReportFile.objects.create(
|
|
247
|
+
pdf_hash=self.pdf_hash,
|
|
248
|
+
center=self.center,
|
|
249
|
+
sensitive_meta=self.sensitive_meta,
|
|
250
|
+
patient=patient,
|
|
251
|
+
examiner=examiner,
|
|
252
|
+
examination=patient_examination,
|
|
253
|
+
text=self.anonymized_text,
|
|
254
|
+
)
|
|
255
|
+
|
|
256
|
+
report_file.save()
|
|
257
|
+
self.report_file = report_file
|
|
258
|
+
self.save()
|
|
259
|
+
|
|
260
|
+
return report_file
|
|
@@ -0,0 +1,288 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import subprocess
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from django.db import models
|
|
5
|
+
from typing import TYPE_CHECKING, List, Tuple
|
|
6
|
+
|
|
7
|
+
from icecream import ic
|
|
8
|
+
from tqdm import tqdm
|
|
9
|
+
import cv2
|
|
10
|
+
from django.core.validators import FileExtensionValidator
|
|
11
|
+
from django.core.files.storage import FileSystemStorage
|
|
12
|
+
|
|
13
|
+
from endoreg_db.utils.validate_endo_roi import validate_endo_roi
|
|
14
|
+
from ..base_classes.utils import (
|
|
15
|
+
anonymize_frame,
|
|
16
|
+
RAW_VIDEO_DIR_NAME,
|
|
17
|
+
VIDEO_DIR,
|
|
18
|
+
STORAGE_LOCATION,
|
|
19
|
+
)
|
|
20
|
+
from ..base_classes.abstract_video import AbstractVideoFile
|
|
21
|
+
|
|
22
|
+
if TYPE_CHECKING:
|
|
23
|
+
# import Queryset
|
|
24
|
+
from django.db.models import QuerySet
|
|
25
|
+
from endoreg_db.models import (
|
|
26
|
+
SensitiveMeta,
|
|
27
|
+
LabelVideoSegment,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
# pylint: disable=attribute-defined-outside-init,no-member
|
|
32
|
+
class RawVideoFile(AbstractVideoFile):
|
|
33
|
+
""" """
|
|
34
|
+
|
|
35
|
+
file = models.FileField(
|
|
36
|
+
upload_to=RAW_VIDEO_DIR_NAME,
|
|
37
|
+
validators=[FileExtensionValidator(allowed_extensions=["mp4"])], # FIXME
|
|
38
|
+
storage=FileSystemStorage(location=STORAGE_LOCATION.resolve().as_posix()),
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
patient = models.ForeignKey(
|
|
42
|
+
"Patient", on_delete=models.SET_NULL, blank=True, null=True
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
sensitive_meta = models.ForeignKey(
|
|
46
|
+
"SensitiveMeta",
|
|
47
|
+
on_delete=models.SET_NULL,
|
|
48
|
+
related_name="raw_videos",
|
|
49
|
+
null=True,
|
|
50
|
+
blank=True,
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
video = models.ForeignKey(
|
|
54
|
+
"Video",
|
|
55
|
+
on_delete=models.SET_NULL,
|
|
56
|
+
related_name="raw_videos",
|
|
57
|
+
null=True,
|
|
58
|
+
blank=True,
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
if TYPE_CHECKING:
|
|
62
|
+
sensitive_meta: "SensitiveMeta"
|
|
63
|
+
label_video_segments: "QuerySet[LabelVideoSegment]"
|
|
64
|
+
|
|
65
|
+
# Crop Frames
|
|
66
|
+
state_anonymized_frames_generated = models.BooleanField(default=False)
|
|
67
|
+
|
|
68
|
+
## OCR
|
|
69
|
+
state_ocr_required = models.BooleanField(default=True)
|
|
70
|
+
state_ocr_completed = models.BooleanField(default=False)
|
|
71
|
+
## Validation
|
|
72
|
+
state_outside_validated = models.BooleanField(default=False)
|
|
73
|
+
state_ocr_result_validated = models.BooleanField(default=False)
|
|
74
|
+
|
|
75
|
+
state_sensitive_data_retrieved = models.BooleanField(default=False)
|
|
76
|
+
|
|
77
|
+
# Censor Outside
|
|
78
|
+
state_censor_outside_required = models.BooleanField(default=True)
|
|
79
|
+
state_censor_outside_completed = models.BooleanField(default=False)
|
|
80
|
+
state_make_anonymized_video_required = models.BooleanField(default=True)
|
|
81
|
+
state_make_anonymized_video_completed = models.BooleanField(default=False)
|
|
82
|
+
|
|
83
|
+
def get_anonymized_video_path(self):
|
|
84
|
+
video_dir = VIDEO_DIR
|
|
85
|
+
video_suffix = Path(self.file.path).suffix
|
|
86
|
+
video_name = f"{self.uuid}{video_suffix}"
|
|
87
|
+
anonymized_video_name = f"TMP_anonymized_{video_name}"
|
|
88
|
+
anonymized_video_path = video_dir / anonymized_video_name
|
|
89
|
+
|
|
90
|
+
return anonymized_video_path
|
|
91
|
+
|
|
92
|
+
def censor_outside_frames(self):
|
|
93
|
+
assert self.state_frames_extracted, "Frames not extracted"
|
|
94
|
+
assert self.state_initial_prediction_completed, (
|
|
95
|
+
"Initial prediction not completed"
|
|
96
|
+
)
|
|
97
|
+
assert self.state_sensitive_data_retrieved, "Sensitive data not retrieved"
|
|
98
|
+
|
|
99
|
+
ic(
|
|
100
|
+
"WARNING: Outside validation is not yet implemented and automatically set to true in this function"
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
self.state_outside_validated = True
|
|
104
|
+
self.save()
|
|
105
|
+
|
|
106
|
+
assert self.state_outside_validated, "Outside validation not completed"
|
|
107
|
+
|
|
108
|
+
outside_frame_paths = self.get_outside_frame_paths()
|
|
109
|
+
|
|
110
|
+
if not outside_frame_paths:
|
|
111
|
+
ic("No outside frames found")
|
|
112
|
+
|
|
113
|
+
else:
|
|
114
|
+
ic(f"Found {len(outside_frame_paths)} outside frames")
|
|
115
|
+
# use cv2 to replace all outside frames with completely black frames
|
|
116
|
+
|
|
117
|
+
for frame_path in tqdm(outside_frame_paths):
|
|
118
|
+
frame = cv2.imread(frame_path.as_posix())
|
|
119
|
+
frame.fill(0)
|
|
120
|
+
cv2.imwrite(frame_path.as_posix(), frame)
|
|
121
|
+
|
|
122
|
+
self.state_censor_outside_required = False
|
|
123
|
+
self.state_censor_outside_completed = True
|
|
124
|
+
self.save()
|
|
125
|
+
|
|
126
|
+
def get_anonymized_frame_dir(self):
|
|
127
|
+
anonymized_frame_dir = Path(self.frame_dir).parent / f"tmp_{self.uuid}"
|
|
128
|
+
return anonymized_frame_dir
|
|
129
|
+
|
|
130
|
+
def make_temporary_anonymized_frames(self) -> Tuple[Path, List[Path]]:
|
|
131
|
+
anonymized_frame_dir = self.get_anonymized_frame_dir()
|
|
132
|
+
|
|
133
|
+
assert self.state_frames_extracted, "Frames not extracted"
|
|
134
|
+
assert self.processor, "Processor not set"
|
|
135
|
+
|
|
136
|
+
anonymized_frame_dir.mkdir(parents=True, exist_ok=True)
|
|
137
|
+
endo_roi = self.get_endo_roi()
|
|
138
|
+
assert validate_endo_roi(endo_roi), "Endoscope ROI is not valid"
|
|
139
|
+
generated_frame_paths = []
|
|
140
|
+
|
|
141
|
+
all_frames = self.frames.all()
|
|
142
|
+
outside_frames = self.get_outside_frames() #
|
|
143
|
+
outside_frame_numbers = [frame.frame_number for frame in outside_frames]
|
|
144
|
+
|
|
145
|
+
# anonymize frames: copy endo-roi content while making other pixels black. (frames are Path objects to jpgs or pngs)
|
|
146
|
+
for frame in tqdm(all_frames):
|
|
147
|
+
frame_path = Path(frame.image.path)
|
|
148
|
+
frame_name = frame_path.name
|
|
149
|
+
frame_number = frame.frame_number
|
|
150
|
+
|
|
151
|
+
if frame_number in outside_frame_numbers:
|
|
152
|
+
all_black = True
|
|
153
|
+
else:
|
|
154
|
+
all_black = False
|
|
155
|
+
|
|
156
|
+
target_frame_path = anonymized_frame_dir / frame_name
|
|
157
|
+
anonymize_frame(
|
|
158
|
+
frame_path, target_frame_path, endo_roi, all_black=all_black
|
|
159
|
+
)
|
|
160
|
+
generated_frame_paths.append(target_frame_path)
|
|
161
|
+
|
|
162
|
+
return anonymized_frame_dir, generated_frame_paths
|
|
163
|
+
|
|
164
|
+
def make_anonymized_video(self):
|
|
165
|
+
"""
|
|
166
|
+
Make an anonymized video from the anonymized frames.
|
|
167
|
+
"""
|
|
168
|
+
|
|
169
|
+
assert self.state_initial_prediction_completed, (
|
|
170
|
+
"Initial prediction not completed"
|
|
171
|
+
)
|
|
172
|
+
assert self.state_sensitive_data_retrieved, "Sensitive data not retrieved"
|
|
173
|
+
|
|
174
|
+
ic(
|
|
175
|
+
"WARNING: Outside validation is not yet implemented and automatically set to true in this function"
|
|
176
|
+
)
|
|
177
|
+
self.state_outside_validated = True
|
|
178
|
+
self.save()
|
|
179
|
+
|
|
180
|
+
assert self.state_outside_validated, "Outside validation not completed"
|
|
181
|
+
|
|
182
|
+
_anonymized_frame_dir, generated_frame_paths = (
|
|
183
|
+
self.make_temporary_anonymized_frames()
|
|
184
|
+
)
|
|
185
|
+
|
|
186
|
+
anonymized_video_path = self.get_anonymized_video_path()
|
|
187
|
+
# if anonymized video already exists, delete it
|
|
188
|
+
if anonymized_video_path.exists():
|
|
189
|
+
anonymized_video_path.unlink()
|
|
190
|
+
|
|
191
|
+
# Use ffmpeg and the frame paths to create a video
|
|
192
|
+
fps = self.get_fps()
|
|
193
|
+
height, width = cv2.imread(generated_frame_paths[0].as_posix()).shape[:2]
|
|
194
|
+
ic("Assembling anonymized video")
|
|
195
|
+
ic(f"Frame width: {width}, height: {height}")
|
|
196
|
+
ic(f"FPS: {fps}")
|
|
197
|
+
|
|
198
|
+
command = [
|
|
199
|
+
"ffmpeg",
|
|
200
|
+
"-y",
|
|
201
|
+
"-pattern_type",
|
|
202
|
+
"glob",
|
|
203
|
+
"-f",
|
|
204
|
+
"image2",
|
|
205
|
+
"-framerate",
|
|
206
|
+
str(fps),
|
|
207
|
+
"-i",
|
|
208
|
+
f"{generated_frame_paths[0].parent.as_posix()}/frame_[0-9]*.jpg",
|
|
209
|
+
"-c:v",
|
|
210
|
+
"libx264",
|
|
211
|
+
"-pix_fmt",
|
|
212
|
+
"yuv420p",
|
|
213
|
+
"-vf",
|
|
214
|
+
f"scale={width}:{height}",
|
|
215
|
+
anonymized_video_path.as_posix(),
|
|
216
|
+
]
|
|
217
|
+
|
|
218
|
+
subprocess.run(command, check=True)
|
|
219
|
+
ic(f"Anonymized video saved at {anonymized_video_path}")
|
|
220
|
+
|
|
221
|
+
self.state_make_anonymized_video_required = False
|
|
222
|
+
self.state_make_anonymized_video_completed = True
|
|
223
|
+
self.save()
|
|
224
|
+
|
|
225
|
+
return anonymized_video_path, generated_frame_paths
|
|
226
|
+
|
|
227
|
+
def delete_frames_anonymized(self):
|
|
228
|
+
"""
|
|
229
|
+
Delete anonymized frames extracted from the video file.
|
|
230
|
+
"""
|
|
231
|
+
frame_dir = Path(self.frame_dir)
|
|
232
|
+
anonymized_frame_dir = frame_dir.parent / f"anonymized_{self.uuid}"
|
|
233
|
+
if anonymized_frame_dir.exists():
|
|
234
|
+
shutil.rmtree(anonymized_frame_dir)
|
|
235
|
+
return f"Anonymized frames deleted from {anonymized_frame_dir}"
|
|
236
|
+
else:
|
|
237
|
+
return f"No anonymized frames to delete for {self.file.name}"
|
|
238
|
+
|
|
239
|
+
def get_or_create_video(self):
|
|
240
|
+
from endoreg_db.models import Video, Patient, PatientExamination
|
|
241
|
+
|
|
242
|
+
video = self.video
|
|
243
|
+
expected_path = self.get_anonymized_video_path()
|
|
244
|
+
if not video:
|
|
245
|
+
video_hash = self.video_hash
|
|
246
|
+
if Video.objects.filter(video_hash=video_hash).exists():
|
|
247
|
+
video = Video.objects.filter(video_hash=video_hash).first()
|
|
248
|
+
|
|
249
|
+
else:
|
|
250
|
+
if not expected_path.exists():
|
|
251
|
+
ic(
|
|
252
|
+
f"No anonymized video found at {expected_path}, Creating new one"
|
|
253
|
+
)
|
|
254
|
+
video_path, frame_paths = self.make_anonymized_video()
|
|
255
|
+
|
|
256
|
+
else:
|
|
257
|
+
ic(f"Anonymized video found at {expected_path}")
|
|
258
|
+
video_path = expected_path
|
|
259
|
+
frame_dir = self.get_anonymized_frame_dir()
|
|
260
|
+
ic(f"Frame dir: {frame_dir}")
|
|
261
|
+
frame_paths = list(frame_dir.glob("*.jpg"))
|
|
262
|
+
ic(f"Found {len(frame_paths)} frames")
|
|
263
|
+
|
|
264
|
+
video_object = Video.create_from_file(
|
|
265
|
+
video_path,
|
|
266
|
+
self.center,
|
|
267
|
+
self.processor,
|
|
268
|
+
video_dir=VIDEO_DIR,
|
|
269
|
+
frame_paths=frame_paths,
|
|
270
|
+
)
|
|
271
|
+
|
|
272
|
+
ex: PatientExamination = self.sensitive_meta.pseudo_examination
|
|
273
|
+
pat: Patient = self.sensitive_meta.pseudo_patient
|
|
274
|
+
video_object.examination = ex
|
|
275
|
+
video_object.patient = pat
|
|
276
|
+
|
|
277
|
+
self.video = video_object
|
|
278
|
+
self.save()
|
|
279
|
+
video_object.sync_from_raw_video()
|
|
280
|
+
|
|
281
|
+
ic(f"Video object created: {video_object}")
|
|
282
|
+
return video_object
|
|
283
|
+
|
|
284
|
+
self.video = video
|
|
285
|
+
self.save()
|
|
286
|
+
|
|
287
|
+
# self.vi
|
|
288
|
+
return video
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .sensitive_meta import SensitiveMeta
|
|
2
|
+
from .pdf_meta import PdfMeta, PdfType
|
|
3
|
+
from .video_meta import VideoMeta, FFMpegMeta, VideoImportMeta
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
__all__ = [
|
|
7
|
+
"SensitiveMeta",
|
|
8
|
+
"PdfMeta",
|
|
9
|
+
"PdfType",
|
|
10
|
+
"VideoMeta",
|
|
11
|
+
"FFMpegMeta",
|
|
12
|
+
"VideoImportMeta",
|
|
13
|
+
]
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
|
|
3
|
+
# import endoreg_center_id from django settings
|
|
4
|
+
from django.conf import settings
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# import File class
|
|
8
|
+
from django.core.files import File
|
|
9
|
+
|
|
10
|
+
# # check if endoreg_center_id is set
|
|
11
|
+
# if not hasattr(settings, 'ENDOREG_CENTER_ID'):
|
|
12
|
+
# ENDOREG_CENTER_ID = 9999
|
|
13
|
+
# else:
|
|
14
|
+
# ENDOREG_CENTER_ID = settings.ENDOREG_CENTER_ID
|
|
15
|
+
|
|
16
|
+
class PdfType(models.Model):
|
|
17
|
+
name = models.CharField(max_length=255)
|
|
18
|
+
|
|
19
|
+
patient_info_line = models.ForeignKey(
|
|
20
|
+
"ReportReaderFlag",
|
|
21
|
+
related_name="pdf_type_patient_info_line",
|
|
22
|
+
on_delete=models.CASCADE
|
|
23
|
+
)
|
|
24
|
+
endoscope_info_line = models.ForeignKey(
|
|
25
|
+
"ReportReaderFlag",
|
|
26
|
+
related_name="pdf_type_endoscopy_info_line",
|
|
27
|
+
on_delete=models.CASCADE,
|
|
28
|
+
)
|
|
29
|
+
examiner_info_line = models.ForeignKey(
|
|
30
|
+
"ReportReaderFlag",
|
|
31
|
+
related_name="pdf_type_examiner_info_line",
|
|
32
|
+
on_delete=models.CASCADE
|
|
33
|
+
)
|
|
34
|
+
cut_off_above_lines = models.ManyToManyField(
|
|
35
|
+
"ReportReaderFlag",
|
|
36
|
+
related_name="pdf_type_cut_off_above_lines",
|
|
37
|
+
)
|
|
38
|
+
cut_off_below_lines = models.ManyToManyField(
|
|
39
|
+
"ReportReaderFlag",
|
|
40
|
+
related_name="pdf_type_cut_off_below_lines",
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def __str__(self):
|
|
45
|
+
summary = f"{self.name}"
|
|
46
|
+
# add lines to summary
|
|
47
|
+
summary += f"\nPatient Info Line: {self.patient_info_line.value}"
|
|
48
|
+
summary += f"\nEndoscope Info Line: {self.endoscope_info_line.value}"
|
|
49
|
+
summary += f"\nExaminer Info Line: {self.examiner_info_line.value}"
|
|
50
|
+
summary += f"\nCut Off Above Lines: {[_.value for _ in self.cut_off_above_lines.all()]}"
|
|
51
|
+
summary += f"\nCut Off Below Lines: {[_.value for _ in self.cut_off_below_lines.all()]}"
|
|
52
|
+
|
|
53
|
+
return summary
|
|
54
|
+
|
|
55
|
+
@classmethod
|
|
56
|
+
def default_pdf_type(cls):
|
|
57
|
+
return PdfType.objects.get(name="ukw-endoscopy-examination-report-generic")
|
|
58
|
+
|
|
59
|
+
class PdfMeta(models.Model):
|
|
60
|
+
pdf_type = models.ForeignKey(PdfType, on_delete=models.CASCADE)
|
|
61
|
+
date = models.DateField()
|
|
62
|
+
time = models.TimeField()
|
|
63
|
+
pdf_hash = models.CharField(max_length=255, unique=True)
|
|
64
|
+
|
|
65
|
+
def __str__(self):
|
|
66
|
+
return self.pdf_hash
|
|
67
|
+
|
|
68
|
+
@classmethod
|
|
69
|
+
def create_from_file(cls, pdf_file):
|
|
70
|
+
pdf_file = File(pdf_file)
|
|
71
|
+
pdf_meta = cls(file=pdf_file)
|
|
72
|
+
pdf_meta.save()
|
|
73
|
+
return pdf_meta
|
|
74
|
+
|