endoreg-db 0.6.0__py3-none-any.whl → 0.6.1__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.1.dist-info}/METADATA +4 -4
- endoreg_db-0.6.1.dist-info/RECORD +420 -0
- {endoreg_db-0.6.0.dist-info → endoreg_db-0.6.1.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.1.dist-info/licenses}/LICENSE +0 -0
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
from typing import Optional, Union
|
|
3
|
+
import yaml
|
|
4
|
+
from icecream import ic
|
|
5
|
+
from pydantic import BaseModel
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class DbConfig(BaseModel):
|
|
9
|
+
"""Database configuration model."""
|
|
10
|
+
|
|
11
|
+
engine: str = "django.db.backends.postgresql"
|
|
12
|
+
host: str
|
|
13
|
+
port: int
|
|
14
|
+
user: str
|
|
15
|
+
password: Optional[str] = None
|
|
16
|
+
password_file: str = "/etc/secrets/vault/SCRT_local_password_maintenance_password"
|
|
17
|
+
name: str
|
|
18
|
+
|
|
19
|
+
# class options should allow for unused excess data
|
|
20
|
+
|
|
21
|
+
@classmethod
|
|
22
|
+
def from_file(cls, path: Union[Path, str]) -> "DbConfig":
|
|
23
|
+
"""Create a DbConfig instance from a YAML configuration file."""
|
|
24
|
+
if isinstance(path, str):
|
|
25
|
+
filepath = Path(path)
|
|
26
|
+
else:
|
|
27
|
+
filepath = path
|
|
28
|
+
assert filepath.exists(), f"Missing Config {filepath}"
|
|
29
|
+
|
|
30
|
+
with open(filepath, "r", encoding="utf-8") as f:
|
|
31
|
+
cfg = yaml.safe_load(f)
|
|
32
|
+
|
|
33
|
+
obj = cls(**cfg)
|
|
34
|
+
|
|
35
|
+
return obj
|
|
36
|
+
|
|
37
|
+
def sync_password(self):
|
|
38
|
+
"""Load the password from the configured password file."""
|
|
39
|
+
with open(self.password_file, "r", encoding="utf-8") as f:
|
|
40
|
+
self.password = f.read().strip()
|
|
41
|
+
|
|
42
|
+
def validate(self):
|
|
43
|
+
"""Fully validate all database configuration fields."""
|
|
44
|
+
self.sync_password()
|
|
45
|
+
|
|
46
|
+
assert self.host, "Missing Host"
|
|
47
|
+
assert self.port, "Missing Port"
|
|
48
|
+
assert self.user, "Missing User"
|
|
49
|
+
assert self.password, "Missing Password"
|
|
50
|
+
assert self.name, "Missing Database"
|
|
51
|
+
|
|
52
|
+
def to_file(self, target: str = "./conf/db.yml", ask_override: bool = True):
|
|
53
|
+
"""Export the configuration to a YAML file."""
|
|
54
|
+
ic(target)
|
|
55
|
+
|
|
56
|
+
with open(target, "w") as f:
|
|
57
|
+
yaml.safe_dump(self.model_dump(), f)
|
endoreg_db/utils/uuid.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
def validate_endo_roi(endo_roi_dict:dict):
|
|
2
|
+
"""
|
|
3
|
+
Validate endoscope ROI dictionary. The dictionary must have the following
|
|
4
|
+
keys: x, y, width, height. The values must be greater than or equal to 0
|
|
5
|
+
for x and y, and greater than 0 for width and height.
|
|
6
|
+
"""
|
|
7
|
+
for key, value in endo_roi_dict.items():
|
|
8
|
+
if key == "x":
|
|
9
|
+
assert value >= 0, f"Endoscope ROI x value must be greater than or equal to 0. Got {value}"
|
|
10
|
+
elif key == "y":
|
|
11
|
+
assert value >= 0, f"Endoscope ROI y value must be greater than or equal to 0. Got {value}"
|
|
12
|
+
elif key == "width":
|
|
13
|
+
assert value > 0, f"Endoscope ROI width value must be greater than 0. Got {value}"
|
|
14
|
+
elif key == "height":
|
|
15
|
+
assert value > 0, f"Endoscope ROI height value must be greater than 0. Got {value}"
|
|
16
|
+
else:
|
|
17
|
+
raise ValueError(f"Endoscope ROI key {key} not recognized")
|
|
18
|
+
|
|
19
|
+
return True
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
from endoreg_db.models import Unit
|
|
2
|
+
|
|
3
|
+
def validate_subcategory_dict(self, subcategory_dict:dict=None):
|
|
4
|
+
if subcategory_dict is None:
|
|
5
|
+
return False
|
|
6
|
+
|
|
7
|
+
if not isinstance(subcategory_dict, dict):
|
|
8
|
+
return False
|
|
9
|
+
|
|
10
|
+
# check if key choices exists and is a list of strings
|
|
11
|
+
if "choices" not in subcategory_dict:
|
|
12
|
+
return False
|
|
13
|
+
|
|
14
|
+
if not isinstance(subcategory_dict["choices"], list):
|
|
15
|
+
return False
|
|
16
|
+
|
|
17
|
+
for choice in subcategory_dict["choices"]:
|
|
18
|
+
if not isinstance(choice, str):
|
|
19
|
+
return False
|
|
20
|
+
|
|
21
|
+
# check if key default exists and is a string
|
|
22
|
+
if "default" not in subcategory_dict:
|
|
23
|
+
return False
|
|
24
|
+
|
|
25
|
+
if not isinstance(subcategory_dict["default"], str):
|
|
26
|
+
return False
|
|
27
|
+
|
|
28
|
+
# check if default is in choices
|
|
29
|
+
if subcategory_dict["default"] not in subcategory_dict["choices"]:
|
|
30
|
+
return False
|
|
31
|
+
|
|
32
|
+
if not subcategory_dict["required"]:
|
|
33
|
+
return False
|
|
34
|
+
|
|
35
|
+
return True
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def validate_numerical_descriptor(self, numerical_descriptor_dict:dict=None):
|
|
39
|
+
# Check if numerical_descriptor_dict is None
|
|
40
|
+
error_message = ""
|
|
41
|
+
if numerical_descriptor_dict is None:
|
|
42
|
+
error_message = "numerical_descriptor_dict is None"
|
|
43
|
+
return False, error_message
|
|
44
|
+
|
|
45
|
+
# Check if numerical_descriptor_dict is a dictionary
|
|
46
|
+
if not isinstance(numerical_descriptor_dict, dict):
|
|
47
|
+
error_message = "numerical_descriptor_dict is not a dictionary"
|
|
48
|
+
return False, error_message
|
|
49
|
+
|
|
50
|
+
# if key unit exists and is a string and Unit object with that name exists
|
|
51
|
+
if "unit" not in numerical_descriptor_dict:
|
|
52
|
+
error_message = "unit key does not exist in numerical_descriptor_dict"
|
|
53
|
+
return False, error_message
|
|
54
|
+
|
|
55
|
+
elif not isinstance(numerical_descriptor_dict["unit"], str):
|
|
56
|
+
error_message = "unit key is not a string"
|
|
57
|
+
return False, error_message
|
|
58
|
+
|
|
59
|
+
elif not Unit.objects.filter(name=numerical_descriptor_dict["unit"]).exists():
|
|
60
|
+
error_message = "Unit object with that name does not exist"
|
|
61
|
+
return False, error_message
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
if not "required" in numerical_descriptor_dict:
|
|
65
|
+
error_message = "required key does not exist in numerical_descriptor_dict"
|
|
66
|
+
return False, error_message
|
|
67
|
+
|
|
68
|
+
elif not isinstance(numerical_descriptor_dict["required"], bool):
|
|
69
|
+
error_message = "required key is not a boolean"
|
|
70
|
+
return False, error_message
|
|
71
|
+
|
|
72
|
+
# check if min, max, mean, std exist and are either None or float
|
|
73
|
+
for key in ["min", "max", "mean", "std", "default"]:
|
|
74
|
+
if key not in numerical_descriptor_dict:
|
|
75
|
+
error_message = f"{key} key does not exist in numerical_descriptor_dict"
|
|
76
|
+
return False, error_message
|
|
77
|
+
|
|
78
|
+
if numerical_descriptor_dict[key] is not None and not isinstance(numerical_descriptor_dict[key], float):
|
|
79
|
+
error_message = f"{key} key is not a float"
|
|
80
|
+
return False, error_message
|
|
81
|
+
|
|
82
|
+
# check if distribution exists and is either "normal" or "uniform"
|
|
83
|
+
if "distribution" not in numerical_descriptor_dict:
|
|
84
|
+
error_message = "distribution key does not exist in numerical_descriptor_dict"
|
|
85
|
+
return False, error_message
|
|
86
|
+
|
|
87
|
+
if numerical_descriptor_dict["distribution"] not in ["normal", "uniform"]:
|
|
88
|
+
error_message = "distribution key is not either 'normal' or 'uniform'"
|
|
89
|
+
return False, error_message
|
|
90
|
+
|
|
91
|
+
return True, None
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from .transcode_videofile import (
|
|
2
|
+
transcode_videofile,
|
|
3
|
+
transcode_videofile_if_required,
|
|
4
|
+
)
|
|
5
|
+
|
|
6
|
+
from .extract_frames import extract_frames, initialize_frame_objects
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"extract_frames",
|
|
10
|
+
"initialize_frame_objects",
|
|
11
|
+
"transcode_videofile",
|
|
12
|
+
"transcode_videofile_if_required",
|
|
13
|
+
]
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import shutil
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from icecream import ic
|
|
5
|
+
import subprocess
|
|
6
|
+
from django.db import transaction
|
|
7
|
+
from tqdm import tqdm
|
|
8
|
+
from typing import TYPE_CHECKING, Union, List
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from endoreg_db.models import RawVideoFile, Video
|
|
12
|
+
|
|
13
|
+
from django.core.files import File
|
|
14
|
+
import io
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def prepare_bulk_frames(frame_paths: List[Path]):
|
|
18
|
+
"""
|
|
19
|
+
Reads the frame paths into memory as Django File objects.
|
|
20
|
+
This avoids 'seek of closed file' errors by using BytesIO for each frame.
|
|
21
|
+
"""
|
|
22
|
+
for path in frame_paths:
|
|
23
|
+
frame_number = int(path.stem.split("_")[1])
|
|
24
|
+
with open(path, "rb") as f:
|
|
25
|
+
content = f.read()
|
|
26
|
+
file_obj = File(io.BytesIO(content), name=path.name)
|
|
27
|
+
yield frame_number, file_obj
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def extract_frames(
|
|
31
|
+
video: Union["RawVideoFile", "Video"],
|
|
32
|
+
quality: int = 2,
|
|
33
|
+
overwrite: bool = False,
|
|
34
|
+
ext="jpg",
|
|
35
|
+
verbose=False,
|
|
36
|
+
) -> List[Path]:
|
|
37
|
+
"""
|
|
38
|
+
Extract frames from the video file and save them to the frame_dir.
|
|
39
|
+
For this, ffmpeg must be available in in the current environment.
|
|
40
|
+
"""
|
|
41
|
+
frame_dir = Path(video.frame_dir)
|
|
42
|
+
ic(f"Extracting frames to {frame_dir}")
|
|
43
|
+
if not frame_dir.exists():
|
|
44
|
+
frame_dir.mkdir(parents=True, exist_ok=True)
|
|
45
|
+
|
|
46
|
+
if not overwrite and len(list(frame_dir.glob(f"*.{ext}"))) > 0:
|
|
47
|
+
video.state_frames_extracted = True # Mark frames as extracted
|
|
48
|
+
extracted_paths = sorted(frame_dir.glob(f"*.{ext}"))
|
|
49
|
+
return extracted_paths
|
|
50
|
+
|
|
51
|
+
video_path = Path(video.file.path).resolve().as_posix()
|
|
52
|
+
|
|
53
|
+
frame_path_string = frame_dir.resolve().as_posix()
|
|
54
|
+
command = [
|
|
55
|
+
"ffmpeg",
|
|
56
|
+
"-i",
|
|
57
|
+
video_path, #
|
|
58
|
+
"-q:v",
|
|
59
|
+
str(quality),
|
|
60
|
+
os.path.join(frame_path_string, f"frame_%07d.{ext}"),
|
|
61
|
+
]
|
|
62
|
+
|
|
63
|
+
# Ensure FFmpeg is available
|
|
64
|
+
if not shutil.which("ffmpeg"):
|
|
65
|
+
raise EnvironmentError(
|
|
66
|
+
"FFmpeg could not be found. Ensure it is installed and in your PATH."
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
# Extract frames from the video file
|
|
70
|
+
# Execute the command
|
|
71
|
+
process = subprocess.Popen(
|
|
72
|
+
command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True
|
|
73
|
+
)
|
|
74
|
+
stdout_data, stderr_data = process.communicate()
|
|
75
|
+
|
|
76
|
+
if process.returncode != 0:
|
|
77
|
+
raise Exception(f"Error extracting frames: {stderr_data}")
|
|
78
|
+
|
|
79
|
+
if verbose and stdout_data:
|
|
80
|
+
print(stdout_data)
|
|
81
|
+
|
|
82
|
+
# After extracting frames with ffmpeg, parse frame filenames and batch-create
|
|
83
|
+
extracted_paths = sorted(frame_dir.glob(f"*.{ext}"))
|
|
84
|
+
|
|
85
|
+
return extracted_paths
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def initialize_frame_objects(
|
|
89
|
+
video: Union["RawVideoFile", "Video"], extracted_paths: List[Path]
|
|
90
|
+
):
|
|
91
|
+
"""
|
|
92
|
+
Initialize frame objects for the extracted frames.
|
|
93
|
+
"""
|
|
94
|
+
if video.state_frames_initialized:
|
|
95
|
+
return
|
|
96
|
+
video.frame_count = len(extracted_paths)
|
|
97
|
+
frames_to_create = []
|
|
98
|
+
batch_size = int(os.environ.get("DJANGO_FFMPEG_EXTRACT_FRAME_BATCHSIZE", "500"))
|
|
99
|
+
for i, (frame_number, file_obj) in tqdm(
|
|
100
|
+
enumerate(prepare_bulk_frames(extracted_paths), start=1)
|
|
101
|
+
):
|
|
102
|
+
frame_obj_instance = video.create_frame_object(
|
|
103
|
+
frame_number, image_file=file_obj, extracted=True
|
|
104
|
+
)
|
|
105
|
+
frames_to_create.append(frame_obj_instance)
|
|
106
|
+
|
|
107
|
+
if i % batch_size == 0:
|
|
108
|
+
with transaction.atomic():
|
|
109
|
+
video.bulk_create_frames(frames_to_create)
|
|
110
|
+
frames_to_create.clear()
|
|
111
|
+
|
|
112
|
+
if frames_to_create:
|
|
113
|
+
with transaction.atomic():
|
|
114
|
+
video.bulk_create_frames(frames_to_create)
|
|
115
|
+
|
|
116
|
+
video.set_frames_extracted(True)
|
|
117
|
+
video.save()
|
|
118
|
+
|
|
119
|
+
frame_dir = extracted_paths[0].parent
|
|
120
|
+
ic(f"Removing frame directory: {frame_dir}")
|
|
121
|
+
shutil.rmtree(frame_dir)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import shutil
|
|
2
|
+
import subprocess
|
|
3
|
+
import os
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
from icecream import ic
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def get_transcoded_file_path(source_file_path: Path, suffix: str = "mp4"):
|
|
9
|
+
"""
|
|
10
|
+
Method to get the transcoded file path.
|
|
11
|
+
|
|
12
|
+
Args:
|
|
13
|
+
source_file_path (Path): Source file path.
|
|
14
|
+
suffix (str): Suffix of the transcoded file.
|
|
15
|
+
|
|
16
|
+
Returns:
|
|
17
|
+
transcoded_file_path (Path): Transcoded file path.
|
|
18
|
+
"""
|
|
19
|
+
transcoded_file_name = f"{source_file_path.stem}_transcoded.{suffix}"
|
|
20
|
+
transcoded_file_path = source_file_path.parent / transcoded_file_name
|
|
21
|
+
return transcoded_file_path
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def check_require_transcode(
|
|
25
|
+
filepath: Path, transcoded_file_path: Path, target_suffix=".mp4"
|
|
26
|
+
):
|
|
27
|
+
"""
|
|
28
|
+
Checks whether a video file requires transcoding.\
|
|
29
|
+
We check if the current suffix of the file path matches the target suffix\
|
|
30
|
+
and if the transcoded file path exists.\
|
|
31
|
+
If the current suffix does not match the target suffix and the transcoded file path does not exist,\
|
|
32
|
+
transcoding is required.
|
|
33
|
+
"""
|
|
34
|
+
current_suffix = filepath.suffix
|
|
35
|
+
|
|
36
|
+
require_transcode = False
|
|
37
|
+
if not current_suffix == target_suffix and not transcoded_file_path.exists():
|
|
38
|
+
if not transcoded_file_path.exists():
|
|
39
|
+
require_transcode = True
|
|
40
|
+
return require_transcode
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def transcode_videofile_if_required(filepath: Path):
|
|
44
|
+
"""
|
|
45
|
+
Perform transcoding on a video file if required.
|
|
46
|
+
This method checks whether a transcoded version (with an ".mp4" suffix) of the given
|
|
47
|
+
video file exists or needs to be produced. It first computes the expected transcoded file path,
|
|
48
|
+
then uses a class-specific check (check_require_transcode) to decide if transcoding is necessary.
|
|
49
|
+
If so, it transcodes the video file by calling the class method transcode_videofile and returns
|
|
50
|
+
the path of the transcoded file after ensuring that the resulting file path matches the computed one.
|
|
51
|
+
If transcoding is not required, the original file path is returned.
|
|
52
|
+
Args:
|
|
53
|
+
filepath (Path): The path to the original video file that may require transcoding.
|
|
54
|
+
Returns:
|
|
55
|
+
Path: The path to the transcoded video file if transcoding was performed; otherwise, the original file path.
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
transcoded_file_path = get_transcoded_file_path(filepath, suffix=".mp4")
|
|
59
|
+
if check_require_transcode(filepath, transcoded_file_path):
|
|
60
|
+
transcoded_path = transcode_videofile(
|
|
61
|
+
filepath, transcoded_path=transcoded_file_path
|
|
62
|
+
)
|
|
63
|
+
assert transcoded_file_path == transcoded_path
|
|
64
|
+
return transcoded_path
|
|
65
|
+
else:
|
|
66
|
+
return filepath
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def transcode_videofile(filepath: Path, transcoded_path: Path):
|
|
70
|
+
"""
|
|
71
|
+
Transcodes a video to a compatible MP4 format using ffmpeg.
|
|
72
|
+
If the transcoded file exists, it is returned.
|
|
73
|
+
|
|
74
|
+
Parameters
|
|
75
|
+
----------
|
|
76
|
+
mov_file : str
|
|
77
|
+
The full path to the video file.
|
|
78
|
+
|
|
79
|
+
Returns
|
|
80
|
+
-------
|
|
81
|
+
transcoded_path : str
|
|
82
|
+
The full path to the transcoded video file.
|
|
83
|
+
"""
|
|
84
|
+
ic("Transcoding video")
|
|
85
|
+
ic(f"Input path: {filepath}")
|
|
86
|
+
|
|
87
|
+
# if filepath suffix is .mp4 or .MP4 we dont need to transcode and can copy the file
|
|
88
|
+
if filepath.suffix.lower() in [".mp4"]:
|
|
89
|
+
shutil.copyfile(filepath, transcoded_path)
|
|
90
|
+
return transcoded_path
|
|
91
|
+
|
|
92
|
+
ic(f"Transcoded path: {transcoded_path}")
|
|
93
|
+
if os.path.exists(transcoded_path):
|
|
94
|
+
return transcoded_path
|
|
95
|
+
|
|
96
|
+
# Run ffmpeg to transcode the video using H264 and AAC
|
|
97
|
+
# TODO Document settings, check if we need to change them
|
|
98
|
+
command = [
|
|
99
|
+
"ffmpeg",
|
|
100
|
+
"-i",
|
|
101
|
+
filepath.resolve().as_posix(),
|
|
102
|
+
"-c:v",
|
|
103
|
+
"libx264",
|
|
104
|
+
"-preset",
|
|
105
|
+
"fast",
|
|
106
|
+
"-c:a",
|
|
107
|
+
"aac",
|
|
108
|
+
transcoded_path,
|
|
109
|
+
]
|
|
110
|
+
subprocess.run(command, check=True)
|
|
111
|
+
return transcoded_path
|
endoreg_db/views/csrf.py
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
from django.shortcuts import render
|
|
2
|
+
from django.contrib.admin.views.decorators import staff_member_required
|
|
3
|
+
from django.http import JsonResponse
|
|
4
|
+
from django.http import JsonResponse
|
|
5
|
+
from django.views.decorators.http import require_GET
|
|
6
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
7
|
+
from rest_framework import viewsets
|
|
8
|
+
from ..models import Patient
|
|
9
|
+
from ..serializers import PatientSerializer
|
|
10
|
+
from rest_framework.permissions import IsAuthenticatedOrReadOnly
|
|
11
|
+
from endoreg_db.models import (
|
|
12
|
+
FindingLocationClassification,
|
|
13
|
+
FindingLocationClassificationChoice,
|
|
14
|
+
FindingMorphologyClassification,
|
|
15
|
+
FindingMorphologyClassificationType
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
@staff_member_required # Ensures only staff members can access the page
|
|
19
|
+
def start_examination(request):
|
|
20
|
+
return render(request, 'admin/start_examination.html') # Loads the simple HTML page
|
|
21
|
+
|
|
22
|
+
#from ..models.patient.patient_finding_location import PatientFindingLocation
|
|
23
|
+
from ..models import FindingLocationClassification, FindingLocationClassificationChoice # Correct models
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
#need to implement one with json data after tesing whethe rthis works or not
|
|
27
|
+
"""def get_location_choices(request, location_id):
|
|
28
|
+
|
|
29
|
+
try:
|
|
30
|
+
# Ensure the location exists
|
|
31
|
+
location = FindingLocationClassification.objects.get(id=location_id)
|
|
32
|
+
# Get only choices related to the selected location classification
|
|
33
|
+
#location_choices = FindingLocationClassificationChoice.objects.filter(location_classification=location)
|
|
34
|
+
#its many to may relation so
|
|
35
|
+
location_choices = location.choices.all()
|
|
36
|
+
|
|
37
|
+
except FindingLocationClassification.DoesNotExist:
|
|
38
|
+
location_choices = []
|
|
39
|
+
|
|
40
|
+
# Get previously selected values to retain them after reloading
|
|
41
|
+
selected_location = int(location_id) if location_id else None
|
|
42
|
+
|
|
43
|
+
return render(request, 'admin/patient_finding_intervention.html', {
|
|
44
|
+
"location_choices": location_choices, # Pass updated choices to the template
|
|
45
|
+
"selected_location": location_id, # Keep previous selection
|
|
46
|
+
})
|
|
47
|
+
"""
|
|
48
|
+
|
|
49
|
+
class PatientViewSet(viewsets.ModelViewSet):
|
|
50
|
+
"""API endpoint for managing patients."""
|
|
51
|
+
queryset = Patient.objects.all()
|
|
52
|
+
serializer_class = PatientSerializer
|
|
53
|
+
#permission_classes = [IsAuthenticatedOrReadOnly]
|
|
54
|
+
|
|
55
|
+
def perform_create(self, serializer):
|
|
56
|
+
serializer.save()
|
|
57
|
+
|
|
58
|
+
def update(self, request, *args, **kwargs):
|
|
59
|
+
# custom edit logic here if needed
|
|
60
|
+
return super().update(request, *args, **kwargs)
|
|
61
|
+
|
|
62
|
+
def destroy(self, request, *args, **kwargs):
|
|
63
|
+
# custom delete logic here if needed
|
|
64
|
+
return super().destroy(request, *args, **kwargs)
|
|
65
|
+
|
|
66
|
+
@require_GET
|
|
67
|
+
def get_location_choices(request, location_id):
|
|
68
|
+
"""Fetch location choices dynamically based on FindingLocationClassification."""
|
|
69
|
+
try:
|
|
70
|
+
location = FindingLocationClassification.objects.get(id=location_id)
|
|
71
|
+
location_choices = location.choices.all()
|
|
72
|
+
data = [{"id": choice.id, "name": choice.name} for choice in location_choices]
|
|
73
|
+
return JsonResponse({"location_choices": data})
|
|
74
|
+
except FindingLocationClassification.DoesNotExist:
|
|
75
|
+
return JsonResponse({"error": "Location classification not found", "location_choices": []}, status=404)
|
|
76
|
+
|
|
77
|
+
@require_GET
|
|
78
|
+
def get_morphology_choices(request, morphology_id):
|
|
79
|
+
"""Fetch morphology choices dynamically based on FindingMorphologyClassification."""
|
|
80
|
+
try:
|
|
81
|
+
morphology_classification = FindingMorphologyClassification.objects.get(id=morphology_id)
|
|
82
|
+
morphology_choices = FindingMorphologyClassificationType.objects.filter(
|
|
83
|
+
id=morphology_classification.classification_type_id
|
|
84
|
+
)
|
|
85
|
+
data = [{"id": choice.id, "name": choice.name} for choice in morphology_choices]
|
|
86
|
+
return JsonResponse({"morphology_choices": data})
|
|
87
|
+
except ObjectDoesNotExist:
|
|
88
|
+
return JsonResponse({"error": "Morphology classification not found", "morphology_choices": []}, status=404)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
return JsonResponse({"error": "Internal server error", "morphology_choices": []}, status=500)
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from rest_framework.views import APIView
|
|
2
|
+
from rest_framework.response import Response
|
|
3
|
+
from rest_framework import status
|
|
4
|
+
from ..models import RawVideoFile
|
|
5
|
+
from ..serializers.raw_video_meta_validation import VideoFileForMetaSerializer
|
|
6
|
+
|
|
7
|
+
class VideoFileForMetaView(APIView):
|
|
8
|
+
"""
|
|
9
|
+
API endpoint to fetch video metadata step-by-step.
|
|
10
|
+
If last_id is not provided Returns the first video.
|
|
11
|
+
If last_id is given Returns the next available video.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
##need to change this fucntion , like the previous one
|
|
15
|
+
|
|
16
|
+
def get(self, request):
|
|
17
|
+
"""
|
|
18
|
+
Handles:
|
|
19
|
+
First video if last_id is nt in query params.
|
|
20
|
+
Next video where id > last_id` if provided.
|
|
21
|
+
"""
|
|
22
|
+
last_id = request.GET.get("last_id") # Get last_id from query params (e.g., ?last_id=2)
|
|
23
|
+
|
|
24
|
+
try:
|
|
25
|
+
# If last_id is provided, fetch the next video where id > last_id
|
|
26
|
+
# id__gt is orm syntax which is equal to SELECT * FROM rawvideofile WHERE id > 2 ORDER BY id ASC LIMIT 1;
|
|
27
|
+
|
|
28
|
+
query_filter = {} if last_id is None else {"id__gt": int(last_id)}
|
|
29
|
+
video_entry = RawVideoFile.objects.select_related("sensitive_meta").filter(**query_filter).order_by('id').first()
|
|
30
|
+
|
|
31
|
+
if not video_entry:
|
|
32
|
+
return Response({"message": "No more videos available."}, status=status.HTTP_404_NOT_FOUND)
|
|
33
|
+
|
|
34
|
+
serializer = VideoFileForMetaSerializer(video_entry)
|
|
35
|
+
return Response(serializer.data, status=status.HTTP_200_OK)
|
|
36
|
+
|
|
37
|
+
except Exception as e:
|
|
38
|
+
return Response({"error": f"Internal server error: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
from django.shortcuts import render
|
|
2
|
+
from django.contrib.admin.views.decorators import staff_member_required
|
|
3
|
+
|
|
4
|
+
@staff_member_required # Ensures only staff members can access the page
|
|
5
|
+
def start_examination(request):
|
|
6
|
+
return render(request, 'admin/start_examination.html') # Loads the simple HTML page
|
|
7
|
+
|
|
8
|
+
from django.shortcuts import render
|
|
9
|
+
#from ..models.patient.patient_finding_location import PatientFindingLocation
|
|
10
|
+
from ..models import FindingLocationClassification, FindingLocationClassificationChoice # Correct models
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
#need to implement one with json data after tesing whethe rthis works or not
|
|
14
|
+
"""def get_location_choices(request, location_id):
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
# Ensure the location exists
|
|
18
|
+
location = FindingLocationClassification.objects.get(id=location_id)
|
|
19
|
+
# Get only choices related to the selected location classification
|
|
20
|
+
#location_choices = FindingLocationClassificationChoice.objects.filter(location_classification=location)
|
|
21
|
+
#its many to may relation so
|
|
22
|
+
location_choices = location.choices.all()
|
|
23
|
+
|
|
24
|
+
except FindingLocationClassification.DoesNotExist:
|
|
25
|
+
location_choices = []
|
|
26
|
+
|
|
27
|
+
# Get previously selected values to retain them after reloading
|
|
28
|
+
selected_location = int(location_id) if location_id else None
|
|
29
|
+
|
|
30
|
+
return render(request, 'admin/patient_finding_intervention.html', {
|
|
31
|
+
"location_choices": location_choices, # Pass updated choices to the template
|
|
32
|
+
"selected_location": location_id, # Keep previous selection
|
|
33
|
+
})
|
|
34
|
+
"""
|
|
35
|
+
from django.shortcuts import render
|
|
36
|
+
from rest_framework import viewsets
|
|
37
|
+
from ..models import Patient
|
|
38
|
+
from ..serializers import PatientSerializer
|
|
39
|
+
from rest_framework.permissions import IsAuthenticatedOrReadOnly
|
|
40
|
+
|
|
41
|
+
class PatientViewSet(viewsets.ModelViewSet):
|
|
42
|
+
"""API endpoint for managing patients."""
|
|
43
|
+
queryset = Patient.objects.all()
|
|
44
|
+
serializer_class = PatientSerializer
|
|
45
|
+
permission_classes = [IsAuthenticatedOrReadOnly]
|
|
46
|
+
|
|
47
|
+
def perform_create(self, serializer):
|
|
48
|
+
serializer.save()
|
|
49
|
+
|
|
50
|
+
from django.http import JsonResponse
|
|
51
|
+
from ..models import FindingLocationClassification, FindingLocationClassificationChoice
|
|
52
|
+
|
|
53
|
+
def get_location_choices(request, location_id):
|
|
54
|
+
"""
|
|
55
|
+
Fetch location choices dynamically based on the selected FindingLocationClassification (Location).
|
|
56
|
+
"""
|
|
57
|
+
try:
|
|
58
|
+
location = FindingLocationClassification.objects.get(id=location_id)
|
|
59
|
+
location_choices = location.choices.all() # Get choices via Many-to-Many relationship
|
|
60
|
+
data = [{"id": choice.id, "name": choice.name} for choice in location_choices]
|
|
61
|
+
except FindingLocationClassification.DoesNotExist:
|
|
62
|
+
data = []
|
|
63
|
+
|
|
64
|
+
return JsonResponse({"location_choices": data})
|
|
65
|
+
|
|
66
|
+
from django.http import JsonResponse
|
|
67
|
+
from ..models import FindingMorphologyClassification, FindingMorphologyClassificationChoice, FindingMorphologyClassificationType
|
|
68
|
+
from django.core.exceptions import ObjectDoesNotExist
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
def get_morphology_choices(request, morphology_id):
|
|
73
|
+
"""
|
|
74
|
+
Fetch morphology choices dynamically based on the selected FindingMorphologyClassification.
|
|
75
|
+
"""
|
|
76
|
+
try:
|
|
77
|
+
# Find the selected Morphology Classification
|
|
78
|
+
morphology_classification = FindingMorphologyClassification.objects.get(id=morphology_id)
|
|
79
|
+
|
|
80
|
+
# Fetch choices from FindingMorphologyClassificationType using classification_type_id
|
|
81
|
+
morphology_choices = FindingMorphologyClassificationType.objects.filter(
|
|
82
|
+
id=morphology_classification.classification_type_id
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
# Cpnvert QuerySet to JSON
|
|
86
|
+
data = [{"id": choice.id, "name": choice.name} for choice in morphology_choices]
|
|
87
|
+
|
|
88
|
+
return JsonResponse({"morphology_choices": data}) # Always return JSON
|
|
89
|
+
|
|
90
|
+
except ObjectDoesNotExist:
|
|
91
|
+
return JsonResponse({"error": "Morphology classification not found", "morphology_choices": []}, status=404)
|
|
92
|
+
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"Error fetching morphology choices: {e}") # Debugging Log
|
|
95
|
+
return JsonResponse({"error": "Internal server error", "morphology_choices": []}, status=500)
|
|
96
|
+
|