endoreg-db 0.3.6__py3-none-any.whl → 0.8.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/admin.py +92 -3
- endoreg_db/api_urls.py +4 -0
- endoreg_db/apps.py +18 -6
- endoreg_db/assets/dummy_model.ckpt +1 -0
- endoreg_db/codemods/readme.md +88 -0
- endoreg_db/codemods/rename_datetime_fields.py +92 -0
- endoreg_db/config/env.py +101 -0
- endoreg_db/data/__init__.py +144 -65
- endoreg_db/data/ai_model/data.yaml +7 -0
- endoreg_db/data/{label → ai_model_label}/label/data.yaml +88 -62
- endoreg_db/data/ai_model_label/label/polyp_classification.yaml +52 -0
- endoreg_db/data/ai_model_label/label-set/data.yaml +40 -0
- endoreg_db/data/ai_model_label/label-set/polyp_classifications.yaml +25 -0
- endoreg_db/data/{label → ai_model_label}/label-type/data.yaml +6 -6
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +27 -0
- endoreg_db/data/{model_type → ai_model_type}/data.yaml +6 -6
- 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 -167
- endoreg_db/data/case_template/rule/01_patient-set-age.yaml +7 -7
- endoreg_db/data/case_template/rule/01_patient-set-gender.yaml +8 -8
- endoreg_db/data/case_template/rule/11_create_patient_lab_sample.yaml +22 -22
- endoreg_db/data/case_template/rule/12_create-patient_medication-anticoagulation.yaml +18 -18
- endoreg_db/data/case_template/rule/13_create-patient_medication_schedule-anticoagulation.yaml +18 -18
- endoreg_db/data/case_template/rule/19_create_patient.yaml +16 -16
- endoreg_db/data/case_template/rule_type/base_types.yaml +35 -35
- endoreg_db/data/case_template/rule_value_type/base_types.yaml +58 -58
- endoreg_db/data/case_template/template/base.yaml +7 -7
- endoreg_db/data/case_template/template_type/pre_endoscopy.yaml +2 -2
- endoreg_db/data/case_template/tmp/_rule_value +13 -13
- endoreg_db/data/case_template/tmp/rule/01_atrial_fibrillation.yaml +21 -21
- endoreg_db/data/case_template/tmp/rule/02_create_object.yaml +9 -9
- endoreg_db/data/case_template/tmp/template/atrial_fibrillation_low_risk.yaml +6 -6
- endoreg_db/data/center/data.yaml +90 -59
- endoreg_db/data/center_resource/green_endoscopy_dashboard_CenterResource.yaml +144 -144
- endoreg_db/data/center_shift/ukw.yaml +9 -0
- endoreg_db/data/center_waste/green_endoscopy_dashboard_CenterWaste.yaml +48 -48
- endoreg_db/data/contraindication/bleeding.yaml +11 -0
- endoreg_db/data/db_summary.csv +58 -0
- endoreg_db/data/db_summary.xlsx +0 -0
- endoreg_db/data/disease/cardiovascular.yaml +37 -37
- endoreg_db/data/disease/hepatology.yaml +4 -4
- endoreg_db/data/disease/misc.yaml +5 -6
- endoreg_db/data/disease/renal.yaml +4 -4
- endoreg_db/data/disease_classification/chronic_kidney_disease.yaml +6 -6
- endoreg_db/data/disease_classification/coronary_vessel_disease.yaml +5 -5
- endoreg_db/data/disease_classification_choice/chronic_kidney_disease.yaml +41 -41
- endoreg_db/data/disease_classification_choice/coronary_vessel_disease.yaml +19 -19
- endoreg_db/data/distribution/date/patient.yaml +6 -6
- endoreg_db/data/distribution/numeric/data.yaml +14 -0
- endoreg_db/data/distribution/single_categorical/patient.yaml +6 -6
- endoreg_db/data/emission_factor/green_endoscopy_dashboard_EmissionFactor.yaml +132 -132
- endoreg_db/data/endoscope/data.yaml +93 -0
- endoreg_db/data/endoscope_type/data.yaml +10 -10
- endoreg_db/data/endoscopy_processor/data.yaml +50 -45
- endoreg_db/data/event/cardiology.yaml +15 -28
- endoreg_db/data/event/neurology.yaml +13 -13
- endoreg_db/data/event/surgery.yaml +12 -12
- endoreg_db/data/event/thrombembolism.yaml +19 -19
- endoreg_db/data/examination/examinations/data.yaml +72 -66
- endoreg_db/data/examination/time/data.yaml +47 -47
- endoreg_db/data/examination/time-type/data.yaml +7 -7
- endoreg_db/data/examination/type/data.yaml +17 -5
- endoreg_db/data/examination_indication/endoscopy.yaml +424 -0
- endoreg_db/data/examination_indication_classification/endoscopy.yaml +160 -0
- endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +101 -0
- endoreg_db/data/examination_requirement_set/colonoscopy.yaml +15 -0
- endoreg_db/data/finding/anatomy_colon.yaml +128 -0
- endoreg_db/data/finding/colonoscopy.yaml +40 -0
- endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +56 -0
- endoreg_db/data/finding/complication.yaml +16 -0
- endoreg_db/data/finding/data.yaml +105 -0
- endoreg_db/data/finding/examination_setting.yaml +16 -0
- endoreg_db/data/finding/medication_related.yaml +18 -0
- endoreg_db/data/finding/outcome.yaml +12 -0
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +95 -0
- endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +22 -0
- endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +25 -0
- endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
- endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +68 -0
- endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +80 -0
- endoreg_db/data/finding_classification/colonoscopy_lst.yaml +21 -0
- endoreg_db/data/finding_classification/colonoscopy_nice.yaml +20 -0
- endoreg_db/data/finding_classification/colonoscopy_paris.yaml +26 -0
- endoreg_db/data/finding_classification/colonoscopy_sano.yaml +22 -0
- endoreg_db/data/finding_classification/colonoscopy_summary.yaml +53 -0
- endoreg_db/data/finding_classification/complication_generic.yaml +25 -0
- endoreg_db/data/finding_classification/examination_setting_generic.yaml +40 -0
- endoreg_db/data/finding_classification/histology_colo.yaml +51 -0
- endoreg_db/data/finding_classification/intervention_required.yaml +26 -0
- endoreg_db/data/finding_classification/medication_related.yaml +23 -0
- endoreg_db/data/finding_classification/visualized.yaml +33 -0
- endoreg_db/data/finding_classification_choice/bowel_preparation.yaml +78 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +15 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +17 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_paris.yaml +57 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +14 -0
- endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
- endoreg_db/data/finding_classification_choice/colonoscopy_location.yaml +229 -0
- endoreg_db/data/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
- endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +82 -0
- endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
- endoreg_db/data/finding_classification_choice/complication_generic_types.yaml +15 -0
- endoreg_db/data/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
- endoreg_db/data/finding_classification_choice/histology.yaml +24 -0
- endoreg_db/data/finding_classification_choice/histology_polyp.yaml +20 -0
- endoreg_db/data/finding_classification_choice/outcome.yaml +19 -0
- endoreg_db/data/finding_classification_choice/yes_no_na.yaml +11 -0
- endoreg_db/data/finding_classification_type/colonoscopy_basic.yaml +48 -0
- endoreg_db/data/finding_intervention/endoscopy.yaml +43 -0
- endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
- endoreg_db/data/finding_intervention/endoscopy_egd.yaml +128 -0
- endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +32 -0
- endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +9 -0
- endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +36 -0
- endoreg_db/data/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +79 -0
- endoreg_db/data/finding_type/data.yaml +43 -0
- endoreg_db/data/gender/data.yaml +42 -18
- endoreg_db/data/information_source/annotation.yaml +6 -0
- endoreg_db/data/information_source/data.yaml +30 -30
- endoreg_db/data/information_source/endoscopy_guidelines.yaml +7 -0
- endoreg_db/data/information_source/medication.yaml +5 -5
- endoreg_db/data/information_source/prediction.yaml +7 -0
- endoreg_db/data/information_source_type/data.yaml +8 -0
- endoreg_db/data/lab_value/cardiac_enzymes.yaml +37 -31
- endoreg_db/data/lab_value/coagulation.yaml +54 -49
- endoreg_db/data/lab_value/electrolytes.yaml +228 -190
- endoreg_db/data/lab_value/gastrointestinal_function.yaml +133 -121
- endoreg_db/data/lab_value/hematology.yaml +184 -169
- endoreg_db/data/lab_value/hormones.yaml +59 -53
- endoreg_db/data/lab_value/lipids.yaml +53 -44
- endoreg_db/data/lab_value/misc.yaml +76 -30
- endoreg_db/data/lab_value/renal_function.yaml +12 -11
- 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/medication/anticoagulation.yaml +64 -64
- endoreg_db/data/medication/tah.yaml +69 -69
- endoreg_db/data/medication_indication/anticoagulation.yaml +115 -120
- endoreg_db/data/medication_indication_type/data.yaml +10 -10
- endoreg_db/data/medication_indication_type/thrombembolism.yaml +40 -40
- endoreg_db/data/medication_intake_time/base.yaml +30 -30
- endoreg_db/data/medication_schedule/apixaban.yaml +94 -94
- endoreg_db/data/medication_schedule/ass.yaml +12 -12
- endoreg_db/data/medication_schedule/enoxaparin.yaml +26 -26
- endoreg_db/data/names_first/first_names.yaml +54 -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 +5 -5
- endoreg_db/data/pdf_type/data.yaml +46 -28
- endoreg_db/data/product/green_endoscopy_dashboard_Product.yaml +66 -66
- endoreg_db/data/product_group/green_endoscopy_dashboard_ProductGroup.yaml +33 -33
- endoreg_db/data/product_material/green_endoscopy_dashboard_ProductMaterial.yaml +308 -308
- endoreg_db/data/product_weight/green_endoscopy_dashboard_ProductWeight.yaml +88 -88
- endoreg_db/data/profession/data.yaml +70 -70
- endoreg_db/data/qualification/endoscopy.yaml +36 -0
- endoreg_db/data/qualification/m2.yaml +39 -0
- endoreg_db/data/qualification/outpatient_clinic.yaml +35 -0
- endoreg_db/data/qualification/sonography.yaml +36 -0
- endoreg_db/data/qualification_type/base.yaml +29 -0
- endoreg_db/data/reference_product/green_endoscopy_dashboard_ReferenceProduct.yaml +55 -55
- endoreg_db/data/report_reader_flag/rkh-histology-generic.yaml +10 -0
- endoreg_db/data/report_reader_flag/ukw-examination-generic.yaml +30 -26
- endoreg_db/data/report_reader_flag/ukw-histology-generic.yaml +24 -19
- endoreg_db/data/requirement/age.yaml +26 -0
- endoreg_db/data/requirement/colonoscopy_baseline_austria.yaml +45 -0
- endoreg_db/data/requirement/disease_cardiovascular.yaml +79 -0
- endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
- endoreg_db/data/requirement/disease_hepatology.yaml +12 -0
- endoreg_db/data/requirement/disease_misc.yaml +12 -0
- endoreg_db/data/requirement/disease_renal.yaml +96 -0
- endoreg_db/data/requirement/endoscopy_bleeding_risk.yaml +59 -0
- endoreg_db/data/requirement/event_cardiology.yaml +251 -0
- endoreg_db/data/requirement/event_requirements.yaml +145 -0
- endoreg_db/data/requirement/finding_colon_polyp.yaml +50 -0
- endoreg_db/data/requirement/gender.yaml +25 -0
- endoreg_db/data/requirement/lab_value.yaml +441 -0
- endoreg_db/data/requirement/medication.yaml +93 -0
- endoreg_db/data/requirement_operator/age.yaml +13 -0
- endoreg_db/data/requirement_operator/lab_operators.yaml +129 -0
- endoreg_db/data/requirement_operator/model_operators.yaml +96 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +48 -0
- endoreg_db/data/requirement_set/colonoscopy_austria_screening.yaml +57 -0
- endoreg_db/data/requirement_set/endoscopy_bleeding_risk.yaml +52 -0
- endoreg_db/data/requirement_set_type/data.yaml +20 -0
- endoreg_db/data/requirement_type/requirement_types.yaml +165 -0
- endoreg_db/data/resource/green_endoscopy_dashboard_Resource.yaml +15 -15
- endoreg_db/data/risk/bleeding.yaml +26 -0
- endoreg_db/data/risk/thrombosis.yaml +37 -0
- endoreg_db/data/risk_type/data.yaml +27 -0
- endoreg_db/data/setup_config.yaml +38 -0
- endoreg_db/data/shift/endoscopy.yaml +21 -0
- endoreg_db/data/shift/m2.yaml +0 -0
- endoreg_db/data/shift_type/base.yaml +35 -0
- endoreg_db/data/tag/requirement_set_tags.yaml +11 -0
- endoreg_db/data/transport_route/green_endoscopy_dashboard_TransportRoute.yaml +12 -12
- endoreg_db/data/unit/concentration.yaml +115 -92
- endoreg_db/data/unit/data.yaml +17 -17
- endoreg_db/data/unit/length.yaml +30 -30
- endoreg_db/data/unit/misc.yaml +19 -19
- endoreg_db/data/unit/rate.yaml +5 -5
- endoreg_db/data/unit/time.yaml +48 -13
- endoreg_db/data/unit/volume.yaml +35 -35
- endoreg_db/data/unit/weight.yaml +37 -37
- endoreg_db/data/waste/data.yaml +11 -11
- endoreg_db/exceptions.py +19 -0
- endoreg_db/factories/__init__.py +0 -0
- endoreg_db/forms/__init__.py +5 -3
- endoreg_db/forms/examination_form.py +11 -0
- endoreg_db/forms/patient_finding_intervention_form.py +18 -0
- endoreg_db/forms/patient_form.py +27 -0
- endoreg_db/forms/questionnaires/__init__.py +1 -1
- endoreg_db/forms/questionnaires/tto_questionnaire.py +23 -23
- endoreg_db/forms/settings/__init__.py +8 -8
- endoreg_db/forms/unit.py +5 -5
- endoreg_db/helpers/__init__.py +0 -0
- endoreg_db/helpers/count_db.py +45 -0
- endoreg_db/helpers/data_loader.py +208 -0
- endoreg_db/helpers/default_objects.py +378 -0
- endoreg_db/helpers/download_segmentation_model.py +31 -0
- endoreg_db/helpers/interact.py +6 -0
- endoreg_db/helpers/test_video_helper.py +119 -0
- endoreg_db/logger_conf.py +140 -0
- endoreg_db/management/__init__.py +1 -0
- endoreg_db/management/commands/__init__.py +1 -0
- endoreg_db/management/commands/anonymize_video.py +0 -0
- endoreg_db/management/commands/check_auth.py +125 -0
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +115 -0
- endoreg_db/management/commands/create_multilabel_model_meta.py +214 -0
- endoreg_db/management/commands/fix_missing_patient_data.py +172 -0
- endoreg_db/management/commands/fix_video_paths.py +165 -0
- endoreg_db/management/commands/import_fallback_video.py +203 -0
- endoreg_db/management/commands/import_report.py +298 -0
- endoreg_db/management/commands/import_video.py +423 -0
- endoreg_db/management/commands/import_video_with_classification.py +367 -0
- endoreg_db/management/commands/init_default_ai_model.py +112 -0
- endoreg_db/management/commands/load_ai_model_data.py +77 -45
- endoreg_db/management/commands/load_ai_model_label_data.py +59 -0
- endoreg_db/management/commands/load_base_db_data.py +192 -128
- endoreg_db/management/commands/load_center_data.py +68 -43
- endoreg_db/management/commands/{load_medication_intake_time_data.py → load_contraindication_data.py} +40 -40
- endoreg_db/management/commands/load_disease_classification_choices_data.py +40 -40
- endoreg_db/management/commands/load_disease_classification_data.py +40 -40
- endoreg_db/management/commands/load_disease_data.py +61 -39
- endoreg_db/management/commands/load_distribution_data.py +65 -65
- endoreg_db/management/commands/{load_endoscope_type_data.py → load_endoscope_data.py} +67 -44
- endoreg_db/management/commands/load_event_data.py +40 -40
- endoreg_db/management/commands/load_examination_data.py +74 -74
- endoreg_db/management/commands/load_examination_indication_data.py +86 -0
- endoreg_db/management/commands/load_finding_data.py +128 -0
- endoreg_db/management/commands/load_gender_data.py +43 -43
- endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +131 -132
- endoreg_db/management/commands/load_information_source.py +50 -44
- endoreg_db/management/commands/load_lab_value_data.py +49 -49
- endoreg_db/management/commands/load_medication_data.py +103 -41
- endoreg_db/management/commands/load_name_data.py +37 -0
- endoreg_db/management/commands/{load_medication_indication_type_data.py → load_organ_data.py} +42 -40
- endoreg_db/management/commands/load_pdf_type_data.py +60 -60
- endoreg_db/management/commands/load_profession_data.py +43 -43
- endoreg_db/management/commands/load_qualification_data.py +59 -0
- endoreg_db/management/commands/{load_report_reader_flag.py → load_report_reader_flag_data.py} +45 -45
- endoreg_db/management/commands/load_requirement_data.py +180 -0
- endoreg_db/management/commands/load_risk_data.py +56 -0
- endoreg_db/management/commands/load_shift_data.py +60 -0
- endoreg_db/management/commands/load_tag_data.py +57 -0
- endoreg_db/management/commands/load_unit_data.py +45 -45
- endoreg_db/management/commands/load_user_groups.py +28 -28
- endoreg_db/management/commands/register_ai_model.py +64 -65
- endoreg_db/management/commands/reset_celery_schedule.py +9 -9
- endoreg_db/management/commands/setup_endoreg_db.py +381 -0
- endoreg_db/management/commands/start_filewatcher.py +106 -0
- endoreg_db/management/commands/storage_management.py +548 -0
- endoreg_db/management/commands/summarize_db_content.py +189 -0
- endoreg_db/management/commands/validate_video.py +204 -0
- endoreg_db/management/commands/validate_video_files.py +161 -0
- endoreg_db/management/commands/video_validation.py +22 -0
- endoreg_db/mermaid/Overall_flow_patient_finding_intervention.md +10 -0
- endoreg_db/mermaid/anonymized_image_annotation.md +20 -0
- endoreg_db/mermaid/binary_classification_annotation.md +50 -0
- endoreg_db/mermaid/classification.md +8 -0
- endoreg_db/mermaid/examination.md +8 -0
- endoreg_db/mermaid/findings.md +7 -0
- endoreg_db/mermaid/image_classification.md +28 -0
- endoreg_db/mermaid/interventions.md +8 -0
- endoreg_db/mermaid/morphology.md +8 -0
- endoreg_db/mermaid/patient_creation.md +14 -0
- endoreg_db/mermaid/video_segmentation_annotation.md +17 -0
- endoreg_db/migrations/0001_initial.py +1857 -582
- endoreg_db/migrations/0002_add_video_correction_models.py +52 -0
- endoreg_db/migrations/0003_add_center_display_name.py +30 -0
- endoreg_db/models/__init__.py +359 -74
- endoreg_db/models/administration/__init__.py +116 -0
- endoreg_db/models/administration/ai/__init__.py +9 -0
- endoreg_db/models/administration/ai/active_model.py +35 -0
- endoreg_db/models/administration/ai/ai_model.py +156 -0
- endoreg_db/models/{ai_model → administration/ai}/model_type.py +41 -26
- endoreg_db/models/administration/case/__init__.py +19 -0
- endoreg_db/models/administration/case/case.py +114 -0
- endoreg_db/models/{case_template → administration/case/case_template}/__init__.py +15 -6
- endoreg_db/models/{case_template → administration/case/case_template}/case_template.py +125 -81
- endoreg_db/models/{case_template → administration/case/case_template}/case_template_rule.py +269 -276
- endoreg_db/models/{case_template → administration/case/case_template}/case_template_rule_value.py +86 -73
- endoreg_db/models/{case_template → administration/case/case_template}/case_template_type.py +26 -28
- endoreg_db/models/{center → administration/center}/__init__.py +13 -4
- endoreg_db/models/administration/center/center.py +67 -0
- endoreg_db/models/administration/center/center_product.py +64 -0
- endoreg_db/models/administration/center/center_resource.py +49 -0
- endoreg_db/models/administration/center/center_shift.py +88 -0
- endoreg_db/models/administration/center/center_waste.py +30 -0
- endoreg_db/models/administration/permissions/__init__.py +44 -0
- endoreg_db/models/administration/person/__init__.py +24 -0
- endoreg_db/models/administration/person/employee/__init__.py +3 -0
- endoreg_db/models/administration/person/employee/employee.py +35 -0
- endoreg_db/models/administration/person/employee/employee_qualification.py +39 -0
- endoreg_db/models/administration/person/employee/employee_type.py +42 -0
- endoreg_db/models/administration/person/examiner/__init__.py +4 -0
- endoreg_db/models/administration/person/examiner/examiner.py +54 -0
- endoreg_db/models/administration/person/names/__init__.py +0 -0
- endoreg_db/models/{persons → administration/person/names}/first_name.py +18 -18
- endoreg_db/models/{persons → administration/person/names}/last_name.py +18 -19
- endoreg_db/models/administration/person/patient/__init__.py +5 -0
- endoreg_db/models/administration/person/patient/patient.py +460 -0
- endoreg_db/models/{persons → administration/person}/person.py +31 -31
- endoreg_db/models/administration/person/profession/__init__.py +24 -0
- endoreg_db/models/administration/person/user/__init__.py +5 -0
- endoreg_db/models/administration/person/user/portal_user_information.py +37 -0
- endoreg_db/models/administration/product/__init__.py +14 -0
- endoreg_db/models/administration/product/product.py +97 -0
- endoreg_db/models/administration/product/product_group.py +39 -0
- endoreg_db/models/administration/product/product_material.py +54 -0
- endoreg_db/models/{product → administration/product}/product_weight.py +47 -26
- endoreg_db/models/{product → administration/product}/reference_product.py +130 -99
- endoreg_db/models/administration/qualification/__init__.py +7 -0
- endoreg_db/models/administration/qualification/qualification.py +37 -0
- endoreg_db/models/administration/qualification/qualification_type.py +35 -0
- endoreg_db/models/administration/shift/__init__.py +9 -0
- endoreg_db/models/administration/shift/scheduled_days.py +69 -0
- endoreg_db/models/administration/shift/shift.py +51 -0
- endoreg_db/models/administration/shift/shift_type.py +108 -0
- endoreg_db/models/label/__init__.py +24 -1
- endoreg_db/models/label/annotation/__init__.py +12 -0
- endoreg_db/models/label/annotation/image_classification.py +84 -0
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +66 -0
- endoreg_db/models/label/label.py +83 -84
- endoreg_db/models/label/label_set.py +53 -0
- endoreg_db/models/label/label_type.py +29 -0
- endoreg_db/models/label/label_video_segment/__init__.py +3 -0
- endoreg_db/models/label/label_video_segment/_create_from_video.py +41 -0
- endoreg_db/models/label/label_video_segment/label_video_segment.py +511 -0
- endoreg_db/models/label/video_segmentation_label.py +31 -0
- endoreg_db/models/label/video_segmentation_labelset.py +27 -0
- endoreg_db/models/media/__init__.py +16 -0
- endoreg_db/models/media/frame/__init__.py +3 -0
- endoreg_db/models/media/frame/frame.py +111 -0
- endoreg_db/models/media/pdf/__init__.py +11 -0
- endoreg_db/models/media/pdf/raw_pdf.py +757 -0
- endoreg_db/models/media/pdf/report_file.py +162 -0
- endoreg_db/models/media/pdf/report_reader/__init__.py +7 -0
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +77 -0
- endoreg_db/models/{report_reader → media/pdf/report_reader}/report_reader_flag.py +19 -19
- endoreg_db/models/media/video/__init__.py +8 -0
- endoreg_db/models/media/video/create_from_file.py +358 -0
- endoreg_db/models/media/video/pipe_1.py +213 -0
- endoreg_db/models/media/video/pipe_2.py +105 -0
- endoreg_db/models/media/video/refactor_plan.md +0 -0
- endoreg_db/models/media/video/video_file.py +825 -0
- endoreg_db/models/media/video/video_file_ai.py +443 -0
- endoreg_db/models/media/video/video_file_anonymize.py +349 -0
- endoreg_db/models/media/video/video_file_frames/__init__.py +47 -0
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +22 -0
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +23 -0
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +104 -0
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +174 -0
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +28 -0
- endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +27 -0
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +20 -0
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +27 -0
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +34 -0
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +27 -0
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +129 -0
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +141 -0
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +65 -0
- endoreg_db/models/media/video/video_file_frames.py +0 -0
- endoreg_db/models/media/video/video_file_io.py +168 -0
- endoreg_db/models/media/video/video_file_meta/__init__.py +22 -0
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +45 -0
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +39 -0
- endoreg_db/models/media/video/video_file_meta/get_fps.py +147 -0
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +143 -0
- endoreg_db/models/media/video/video_file_meta/text_meta.py +134 -0
- endoreg_db/models/media/video/video_file_meta/video_meta.py +70 -0
- endoreg_db/models/media/video/video_file_segments.py +209 -0
- endoreg_db/models/media/video/video_metadata.py +65 -0
- endoreg_db/models/media/video/video_processing.py +152 -0
- endoreg_db/models/medical/__init__.py +146 -0
- endoreg_db/models/medical/contraindication/__init__.py +17 -0
- endoreg_db/models/medical/disease.py +156 -0
- endoreg_db/models/medical/event.py +137 -0
- endoreg_db/models/medical/examination/__init__.py +9 -0
- endoreg_db/models/medical/examination/examination.py +148 -0
- endoreg_db/models/medical/examination/examination_indication.py +278 -0
- endoreg_db/models/medical/examination/examination_time.py +49 -0
- endoreg_db/models/medical/examination/examination_time_type.py +41 -0
- endoreg_db/models/medical/examination/examination_type.py +48 -0
- endoreg_db/models/medical/finding/__init__.py +18 -0
- endoreg_db/models/medical/finding/finding.py +96 -0
- endoreg_db/models/medical/finding/finding_classification.py +142 -0
- endoreg_db/models/medical/finding/finding_intervention.py +52 -0
- endoreg_db/models/medical/finding/finding_type.py +35 -0
- endoreg_db/models/medical/hardware/__init__.py +8 -0
- endoreg_db/models/medical/hardware/endoscope.py +65 -0
- endoreg_db/models/{hardware → medical/hardware}/endoscopy_processor.py +182 -143
- endoreg_db/models/medical/laboratory/__init__.py +5 -0
- endoreg_db/models/medical/laboratory/lab_value.py +419 -0
- endoreg_db/models/medical/medication/__init__.py +19 -0
- endoreg_db/models/medical/medication/medication.py +31 -0
- endoreg_db/models/medical/medication/medication_indication.py +50 -0
- endoreg_db/models/medical/medication/medication_indication_type.py +39 -0
- endoreg_db/models/medical/medication/medication_intake_time.py +44 -0
- endoreg_db/models/medical/medication/medication_schedule.py +45 -0
- endoreg_db/models/medical/organ/__init__.py +35 -0
- endoreg_db/models/medical/patient/__init__.py +56 -0
- endoreg_db/models/medical/patient/medication_examples.py +38 -0
- endoreg_db/models/medical/patient/patient_disease.py +63 -0
- endoreg_db/models/medical/patient/patient_event.py +75 -0
- endoreg_db/models/medical/patient/patient_examination.py +249 -0
- endoreg_db/models/medical/patient/patient_examination_indication.py +44 -0
- endoreg_db/models/medical/patient/patient_finding.py +357 -0
- endoreg_db/models/medical/patient/patient_finding_classification.py +207 -0
- endoreg_db/models/medical/patient/patient_finding_intervention.py +40 -0
- endoreg_db/models/medical/patient/patient_lab_sample.py +148 -0
- endoreg_db/models/{persons → medical}/patient/patient_lab_value.py +222 -176
- endoreg_db/models/medical/patient/patient_medication.py +104 -0
- endoreg_db/models/medical/patient/patient_medication_schedule.py +136 -0
- endoreg_db/models/medical/risk/__init__.py +7 -0
- endoreg_db/models/medical/risk/risk.py +72 -0
- endoreg_db/models/medical/risk/risk_type.py +51 -0
- endoreg_db/models/metadata/__init__.py +19 -0
- endoreg_db/models/metadata/frame_ocr_result.py +0 -0
- endoreg_db/models/metadata/model_meta.py +206 -0
- endoreg_db/models/metadata/model_meta_logic.py +343 -0
- endoreg_db/models/{data_file/metadata → metadata}/pdf_meta.py +89 -70
- endoreg_db/models/metadata/sensitive_meta.py +288 -0
- endoreg_db/models/metadata/sensitive_meta_logic.py +1048 -0
- endoreg_db/models/metadata/video_meta.py +332 -0
- endoreg_db/models/metadata/video_prediction_logic.py +190 -0
- endoreg_db/models/metadata/video_prediction_meta.py +270 -0
- endoreg_db/models/other/__init__.py +40 -5
- 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 +89 -0
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +32 -0
- endoreg_db/models/other/distribution/numeric_value_distribution.py +125 -0
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +22 -0
- endoreg_db/models/other/emission/__init__.py +5 -0
- endoreg_db/models/other/emission/emission_factor.py +94 -0
- endoreg_db/models/{persons → other}/gender.py +27 -22
- endoreg_db/models/other/information_source.py +159 -0
- endoreg_db/models/other/material.py +28 -16
- endoreg_db/models/other/resource.py +21 -17
- endoreg_db/models/other/tag.py +27 -0
- endoreg_db/models/other/transport_route.py +33 -21
- endoreg_db/models/{unit.py → other/unit.py} +32 -22
- endoreg_db/models/other/waste.py +27 -20
- endoreg_db/models/requirement/__init__.py +11 -0
- endoreg_db/models/requirement/requirement.py +767 -0
- endoreg_db/models/requirement/requirement_evaluation/__init__.py +6 -0
- endoreg_db/models/requirement/requirement_evaluation/get_values.py +40 -0
- endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +9 -0
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +95 -0
- endoreg_db/models/requirement/requirement_operator.py +176 -0
- endoreg_db/models/requirement/requirement_set.py +287 -0
- endoreg_db/models/rule/__init__.py +13 -0
- endoreg_db/models/{rules → rule}/rule.py +27 -24
- endoreg_db/models/{rules → rule}/rule_applicator.py +224 -224
- endoreg_db/models/{rules → rule}/rule_attribute_dtype.py +16 -18
- endoreg_db/models/{rules → rule}/rule_type.py +19 -21
- endoreg_db/models/{rules → rule}/ruleset.py +17 -19
- endoreg_db/models/state/__init__.py +12 -0
- endoreg_db/models/state/abstract.py +11 -0
- endoreg_db/models/state/audit_ledger.py +150 -0
- endoreg_db/models/state/label_video_segment.py +22 -0
- endoreg_db/models/state/raw_pdf.py +187 -0
- endoreg_db/models/state/sensitive_meta.py +46 -0
- endoreg_db/models/state/video.py +232 -0
- endoreg_db/models/upload_job.py +99 -0
- endoreg_db/models/utils.py +135 -0
- endoreg_db/queries/__init__.py +4 -4
- endoreg_db/queries/annotations/__init__.py +2 -2
- endoreg_db/queries/annotations/legacy.py +158 -159
- endoreg_db/renames.yml +8 -0
- endoreg_db/root_urls.py +9 -0
- endoreg_db/schemas/__init__.py +0 -0
- endoreg_db/schemas/examination_evaluation.py +27 -0
- endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +775 -0
- endoreg_db/serializers/__init__.py +118 -10
- endoreg_db/serializers/_old/raw_pdf_meta_validation.py +223 -0
- endoreg_db/serializers/_old/raw_video_meta_validation.py +179 -0
- endoreg_db/serializers/_old/video.py +71 -0
- endoreg_db/serializers/administration/__init__.py +14 -0
- endoreg_db/serializers/administration/ai/__init__.py +10 -0
- endoreg_db/serializers/administration/ai/active_model.py +10 -0
- endoreg_db/serializers/administration/ai/ai_model.py +18 -0
- endoreg_db/serializers/administration/ai/model_type.py +10 -0
- endoreg_db/serializers/administration/center.py +9 -0
- endoreg_db/serializers/administration/gender.py +9 -0
- endoreg_db/serializers/anonymization.py +69 -0
- endoreg_db/serializers/evaluation/examination_evaluation.py +1 -0
- endoreg_db/serializers/examination/__init__.py +10 -0
- endoreg_db/serializers/examination/base.py +46 -0
- endoreg_db/serializers/examination/dropdown.py +21 -0
- endoreg_db/serializers/examination_serializer.py +12 -0
- endoreg_db/serializers/finding/__init__.py +5 -0
- endoreg_db/serializers/finding/finding.py +54 -0
- endoreg_db/serializers/finding_classification/__init__.py +7 -0
- endoreg_db/serializers/finding_classification/choice.py +19 -0
- endoreg_db/serializers/finding_classification/classification.py +13 -0
- endoreg_db/serializers/label/__init__.py +7 -0
- endoreg_db/serializers/label/image_classification_annotation.py +62 -0
- endoreg_db/serializers/label/label.py +15 -0
- endoreg_db/serializers/label_video_segment/__init__.py +7 -0
- endoreg_db/serializers/label_video_segment/_lvs_create.py +149 -0
- endoreg_db/serializers/label_video_segment/_lvs_update.py +138 -0
- endoreg_db/serializers/label_video_segment/_lvs_validate.py +149 -0
- endoreg_db/serializers/label_video_segment/label_video_segment.py +344 -0
- endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +99 -0
- endoreg_db/serializers/label_video_segment/label_video_segment_update.py +163 -0
- endoreg_db/serializers/meta/__init__.py +19 -0
- endoreg_db/serializers/meta/pdf_file_meta_extraction.py +115 -0
- endoreg_db/serializers/meta/report_meta.py +53 -0
- endoreg_db/serializers/meta/sensitive_meta_detail.py +162 -0
- endoreg_db/serializers/meta/sensitive_meta_update.py +148 -0
- endoreg_db/serializers/meta/sensitive_meta_verification.py +59 -0
- endoreg_db/serializers/meta/video_meta.py +39 -0
- endoreg_db/serializers/misc/__init__.py +14 -0
- endoreg_db/serializers/misc/file_overview.py +182 -0
- endoreg_db/serializers/misc/sensitive_patient_data.py +120 -0
- endoreg_db/serializers/misc/stats.py +33 -0
- endoreg_db/serializers/misc/translatable_field_mix_in.py +44 -0
- endoreg_db/serializers/misc/upload_job.py +71 -0
- endoreg_db/serializers/patient/__init__.py +11 -0
- endoreg_db/serializers/patient/patient.py +86 -0
- endoreg_db/serializers/patient/patient_dropdown.py +27 -0
- endoreg_db/serializers/patient_examination/__init__.py +7 -0
- endoreg_db/serializers/patient_examination/patient_examination.py +141 -0
- endoreg_db/serializers/patient_finding/__init__.py +15 -0
- endoreg_db/serializers/patient_finding/patient_finding.py +31 -0
- endoreg_db/serializers/patient_finding/patient_finding_classification.py +39 -0
- endoreg_db/serializers/patient_finding/patient_finding_detail.py +53 -0
- endoreg_db/serializers/patient_finding/patient_finding_intervention.py +26 -0
- endoreg_db/serializers/patient_finding/patient_finding_list.py +41 -0
- endoreg_db/serializers/patient_finding/patient_finding_write.py +126 -0
- endoreg_db/serializers/pdf/__init__.py +5 -0
- endoreg_db/serializers/pdf/anony_text_validation.py +85 -0
- endoreg_db/serializers/report/__init__.py +9 -0
- endoreg_db/serializers/report/mixins.py +45 -0
- endoreg_db/serializers/report/report.py +105 -0
- endoreg_db/serializers/report/report_list.py +22 -0
- endoreg_db/serializers/report/secure_file_url.py +26 -0
- endoreg_db/serializers/requirements/requirement_schema.py +25 -0
- endoreg_db/serializers/requirements/requirement_sets.py +29 -0
- endoreg_db/serializers/sensitive_meta_serializer.py +282 -0
- endoreg_db/serializers/video/__init__.py +7 -0
- endoreg_db/serializers/video/segmentation.py +263 -0
- endoreg_db/serializers/video/video_file_brief.py +10 -0
- endoreg_db/serializers/video/video_file_detail.py +83 -0
- endoreg_db/serializers/video/video_file_list.py +67 -0
- endoreg_db/serializers/video/video_metadata.py +105 -0
- endoreg_db/serializers/video/video_processing_history.py +153 -0
- endoreg_db/serializers/video_examination.py +198 -0
- endoreg_db/services/__init__.py +5 -0
- endoreg_db/services/anonymization.py +223 -0
- endoreg_db/services/examination_evaluation.py +149 -0
- endoreg_db/services/finding_description_service.py +0 -0
- endoreg_db/services/lookup_service.py +411 -0
- endoreg_db/services/lookup_store.py +266 -0
- endoreg_db/services/pdf_import.py +1382 -0
- endoreg_db/services/polling_coordinator.py +288 -0
- endoreg_db/services/pseudonym_service.py +89 -0
- endoreg_db/services/requirements_object.py +147 -0
- endoreg_db/services/segment_sync.py +155 -0
- endoreg_db/services/storage_aware_video_processor.py +344 -0
- endoreg_db/services/video_import.py +1259 -0
- endoreg_db/tasks/upload_tasks.py +207 -0
- endoreg_db/tasks/video_ingest.py +157 -0
- endoreg_db/tasks/video_processing_tasks.py +327 -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/urls/__init__.py +83 -0
- endoreg_db/urls/anonymization.py +32 -0
- endoreg_db/urls/auth.py +16 -0
- endoreg_db/urls/classification.py +39 -0
- endoreg_db/urls/examination.py +54 -0
- endoreg_db/urls/files.py +6 -0
- endoreg_db/urls/label_video_segment_validate.py +33 -0
- endoreg_db/urls/label_video_segments.py +46 -0
- endoreg_db/urls/media.py +227 -0
- endoreg_db/urls/patient.py +19 -0
- endoreg_db/urls/report.py +48 -0
- endoreg_db/urls/requirements.py +13 -0
- endoreg_db/urls/sensitive_meta.py +0 -0
- endoreg_db/urls/stats.py +46 -0
- endoreg_db/urls/upload.py +20 -0
- endoreg_db/urls/video.py +61 -0
- endoreg_db/urls.py +9 -0
- endoreg_db/utils/__init__.py +88 -1
- endoreg_db/utils/ai/__init__.py +9 -0
- endoreg_db/{models/ai_model/utils.py → utils/ai/get.py} +5 -8
- endoreg_db/utils/ai/inference_dataset.py +52 -0
- endoreg_db/utils/ai/multilabel_classification_net.py +159 -0
- endoreg_db/utils/ai/postprocess.py +63 -0
- endoreg_db/utils/ai/predict.py +291 -0
- endoreg_db/utils/ai/preprocess.py +68 -0
- endoreg_db/utils/calc_duration_seconds.py +24 -0
- endoreg_db/utils/case_generator/__init__.py +0 -0
- endoreg_db/utils/case_generator/case_generator.py +159 -0
- endoreg_db/utils/case_generator/lab_sample_factory.py +33 -0
- endoreg_db/utils/case_generator/utils.py +30 -0
- endoreg_db/utils/check_video_files.py +148 -0
- endoreg_db/utils/cropping.py +28 -28
- endoreg_db/utils/dataloader.py +175 -92
- endoreg_db/utils/dates.py +60 -0
- endoreg_db/utils/env.py +33 -0
- endoreg_db/utils/extract_specific_frames.py +72 -0
- endoreg_db/utils/file_operations.py +58 -30
- endoreg_db/utils/fix_video_path_direct.py +141 -0
- endoreg_db/utils/frame_anonymization_utils.py +463 -0
- endoreg_db/utils/hashs.py +153 -34
- endoreg_db/utils/links/__init__.py +0 -0
- endoreg_db/utils/links/requirement_link.py +193 -0
- endoreg_db/utils/mime_types.py +0 -0
- endoreg_db/utils/names.py +76 -0
- endoreg_db/utils/ocr.py +190 -197
- endoreg_db/utils/parse_and_generate_yaml.py +46 -0
- endoreg_db/utils/paths.py +95 -0
- endoreg_db/utils/permissions.py +143 -0
- endoreg_db/utils/pipelines/Readme.md +235 -0
- endoreg_db/utils/pipelines/__init__.py +0 -0
- endoreg_db/utils/pipelines/process_video_dir.py +120 -0
- endoreg_db/utils/product/__init__.py +0 -0
- endoreg_db/utils/product/sum_emissions.py +20 -0
- endoreg_db/utils/product/sum_weights.py +18 -0
- endoreg_db/utils/pydantic_models/__init__.py +6 -0
- endoreg_db/utils/pydantic_models/db_config.py +57 -0
- endoreg_db/utils/requirement_helpers.py +0 -0
- endoreg_db/utils/requirement_operator_logic/__init__.py +0 -0
- endoreg_db/utils/requirement_operator_logic/lab_value_operators.py +578 -0
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +368 -0
- endoreg_db/utils/setup_config.py +177 -0
- endoreg_db/utils/translation.py +27 -0
- endoreg_db/utils/uuid.py +4 -4
- endoreg_db/utils/validate_endo_roi.py +19 -0
- endoreg_db/utils/validate_subcategory_dict.py +91 -0
- endoreg_db/utils/validate_video_detailed.py +357 -0
- endoreg_db/utils/video/__init__.py +26 -0
- endoreg_db/utils/video/extract_frames.py +88 -0
- endoreg_db/utils/video/ffmpeg_wrapper.py +835 -0
- endoreg_db/utils/video/names.py +42 -0
- endoreg_db/utils/video/streaming_processor.py +312 -0
- endoreg_db/utils/video/video_splitter.py +94 -0
- endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +238 -0
- endoreg_db/views/__init__.py +275 -0
- endoreg_db/views/anonymization/__init__.py +27 -0
- endoreg_db/views/anonymization/media_management.py +454 -0
- endoreg_db/views/anonymization/overview.py +216 -0
- endoreg_db/views/anonymization/validate.py +107 -0
- endoreg_db/views/auth/__init__.py +13 -0
- endoreg_db/views/auth/keycloak.py +113 -0
- endoreg_db/views/examination/__init__.py +33 -0
- endoreg_db/views/examination/examination.py +37 -0
- endoreg_db/views/examination/examination_manifest_cache.py +26 -0
- endoreg_db/views/examination/get_finding_classification_choices.py +59 -0
- endoreg_db/views/examination/get_finding_classifications.py +36 -0
- endoreg_db/views/examination/get_findings.py +41 -0
- endoreg_db/views/examination/get_instruments.py +18 -0
- endoreg_db/views/examination/get_interventions.py +14 -0
- endoreg_db/views/finding/__init__.py +9 -0
- endoreg_db/views/finding/finding.py +112 -0
- endoreg_db/views/finding/get_classifications.py +14 -0
- endoreg_db/views/finding/get_interventions.py +17 -0
- endoreg_db/views/finding_classification/__init__.py +13 -0
- endoreg_db/views/finding_classification/base.py +0 -0
- endoreg_db/views/finding_classification/finding_classification.py +42 -0
- endoreg_db/views/finding_classification/get_classification_choices.py +55 -0
- endoreg_db/views/label/__init__.py +5 -0
- endoreg_db/views/label/label.py +15 -0
- endoreg_db/views/label_video_segment/__init__.py +16 -0
- endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +44 -0
- endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +50 -0
- endoreg_db/views/label_video_segment/label_video_segment.py +77 -0
- endoreg_db/views/label_video_segment/label_video_segment_by_label.py +174 -0
- endoreg_db/views/label_video_segment/label_video_segment_detail.py +73 -0
- endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +46 -0
- endoreg_db/views/label_video_segment/validate.py +226 -0
- endoreg_db/views/media/__init__.py +45 -0
- endoreg_db/views/media/pdf_media.py +388 -0
- endoreg_db/views/media/segments.py +71 -0
- endoreg_db/views/media/sensitive_metadata.py +314 -0
- endoreg_db/views/media/video_media.py +272 -0
- endoreg_db/views/media/video_segments.py +524 -0
- endoreg_db/views/meta/__init__.py +15 -0
- endoreg_db/views/meta/available_files_list.py +146 -0
- endoreg_db/views/meta/report_meta.py +53 -0
- endoreg_db/views/meta/sensitive_meta_detail.py +148 -0
- endoreg_db/views/meta/sensitive_meta_list.py +104 -0
- endoreg_db/views/meta/sensitive_meta_verification.py +71 -0
- endoreg_db/views/misc/__init__.py +63 -0
- endoreg_db/views/misc/center.py +13 -0
- endoreg_db/views/misc/csrf.py +7 -0
- endoreg_db/views/misc/gender.py +14 -0
- endoreg_db/views/misc/secure_file_serving_view.py +80 -0
- endoreg_db/views/misc/secure_file_url_view.py +84 -0
- endoreg_db/views/misc/secure_url_validate.py +79 -0
- endoreg_db/views/misc/stats.py +220 -0
- endoreg_db/views/misc/translation.py +182 -0
- endoreg_db/views/misc/upload_views.py +240 -0
- endoreg_db/views/patient/__init__.py +5 -0
- endoreg_db/views/patient/patient.py +210 -0
- endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +164 -0
- endoreg_db/views/patient_examination/__init__.py +11 -0
- endoreg_db/views/patient_examination/patient_examination.py +140 -0
- endoreg_db/views/patient_examination/patient_examination_create.py +63 -0
- endoreg_db/views/patient_examination/patient_examination_detail.py +66 -0
- endoreg_db/views/patient_examination/patient_examination_list.py +68 -0
- endoreg_db/views/patient_examination/video.py +194 -0
- endoreg_db/views/patient_finding/__init__.py +7 -0
- endoreg_db/views/patient_finding/base.py +0 -0
- endoreg_db/views/patient_finding/patient_finding.py +64 -0
- endoreg_db/views/patient_finding/patient_finding_optimized.py +259 -0
- endoreg_db/views/patient_finding_classification/__init__.py +5 -0
- endoreg_db/views/patient_finding_classification/pfc_create.py +67 -0
- endoreg_db/views/patient_finding_location/__init__.py +5 -0
- endoreg_db/views/patient_finding_location/pfl_create.py +70 -0
- endoreg_db/views/patient_finding_morphology/__init__.py +5 -0
- endoreg_db/views/patient_finding_morphology/pfm_create.py +70 -0
- endoreg_db/views/pdf/__init__.py +8 -0
- endoreg_db/views/pdf/pdf_stream.py +187 -0
- endoreg_db/views/pdf/reimport.py +177 -0
- endoreg_db/views/report/__init__.py +9 -0
- endoreg_db/views/report/report_list.py +112 -0
- endoreg_db/views/report/report_with_secure_url.py +28 -0
- endoreg_db/views/report/start_examination.py +7 -0
- endoreg_db/views/requirement/__init__.py +10 -0
- endoreg_db/views/requirement/evaluate.py +279 -0
- endoreg_db/views/requirement/lookup.py +367 -0
- endoreg_db/views/requirement/lookup_store.py +252 -0
- endoreg_db/views/requirement_lookup/lookup.py +0 -0
- endoreg_db/views/requirement_lookup/lookup_store.py +0 -0
- endoreg_db/views/stats/__init__.py +13 -0
- endoreg_db/views/stats/stats_views.py +229 -0
- endoreg_db/views/video/__init__.py +59 -0
- endoreg_db/views/video/correction.py +530 -0
- endoreg_db/views/video/reimport.py +195 -0
- endoreg_db/views/video/segmentation.py +274 -0
- endoreg_db/views/video/task_status.py +49 -0
- endoreg_db/views/video/timeline.py +46 -0
- endoreg_db/views/video/video_analyze.py +52 -0
- endoreg_db/views/video/video_apply_mask.py +48 -0
- endoreg_db/views/video/video_correction.py +21 -0
- endoreg_db/views/video/video_download_processed.py +58 -0
- endoreg_db/views/video/video_examination_viewset.py +242 -0
- endoreg_db/views/video/video_meta.py +29 -0
- endoreg_db/views/video/video_processing_history.py +24 -0
- endoreg_db/views/video/video_remove_frames.py +48 -0
- endoreg_db/views/video/video_stream.py +306 -0
- endoreg_db/views.py +0 -3
- endoreg_db-0.8.6.1.dist-info/METADATA +383 -0
- endoreg_db-0.8.6.1.dist-info/RECORD +793 -0
- {endoreg_db-0.3.6.dist-info → endoreg_db-0.8.6.1.dist-info}/WHEEL +1 -1
- {endoreg_db-0.3.6.dist-info → endoreg_db-0.8.6.1.dist-info/licenses}/LICENSE +674 -674
- endoreg_db/data/active_model/data.yaml +0 -3
- endoreg_db/data/label/label-set/data.yaml +0 -18
- endoreg_db/management/commands/_load_model_template.py +0 -41
- endoreg_db/management/commands/delete_all.py +0 -18
- endoreg_db/management/commands/delete_legacy_images.py +0 -19
- endoreg_db/management/commands/delete_legacy_videos.py +0 -17
- endoreg_db/management/commands/extract_legacy_video_frames.py +0 -18
- endoreg_db/management/commands/fetch_legacy_image_dataset.py +0 -32
- endoreg_db/management/commands/fix_auth_permission.py +0 -20
- endoreg_db/management/commands/import_legacy_images.py +0 -94
- endoreg_db/management/commands/import_legacy_videos.py +0 -76
- endoreg_db/management/commands/load_active_model_data.py +0 -45
- endoreg_db/management/commands/load_endoscopy_processor_data.py +0 -45
- endoreg_db/management/commands/load_g_play_data.py +0 -113
- endoreg_db/management/commands/load_label_data.py +0 -67
- endoreg_db/management/commands/load_medication_indication_data.py +0 -63
- endoreg_db/management/commands/load_medication_schedule_data.py +0 -55
- endoreg_db/migrations/0002_rawvideofile.py +0 -26
- endoreg_db/migrations/0003_rawvideofile_frames_required.py +0 -18
- endoreg_db/migrations/0004_rename_hash_rawvideofile_video_hash.py +0 -18
- endoreg_db/migrations/0005_ffmpegmeta_remove_videoimportmeta_center_and_more.py +0 -56
- endoreg_db/migrations/0006_rawvideofile_center_alter_videometa_processor.py +0 -25
- endoreg_db/migrations/0007_rawvideofile_processor.py +0 -19
- endoreg_db/migrations/0008_rename_frames_required_rawvideofile_state_frames_required.py +0 -18
- endoreg_db/migrations/0009_sensitivemeta_rawvideofile_sensitive_meta.py +0 -31
- endoreg_db/migrations/0010_rename_endoscope_serial_number_sensitivemeta_endoscope_sn.py +0 -18
- endoreg_db/migrations/0011_rawvideofile_state_sensitive_data_retrieved.py +0 -18
- endoreg_db/migrations/0012_rawvideofile_prediction_dir_and_more.py +0 -109
- endoreg_db/migrations/0013_rawpdffile.py +0 -31
- endoreg_db/migrations/0014_pdftype_alter_rawpdffile_file_pdfmeta.py +0 -38
- endoreg_db/migrations/0015_rename_report_processed_rawpdffile_state_report_processed_and_more.py +0 -31
- endoreg_db/migrations/0016_rawpdffile_state_report_processing_required.py +0 -18
- endoreg_db/migrations/0017_firstname_lastname_center_first_names_and_more.py +0 -37
- endoreg_db/migrations/0018_reportreaderflag_reportreaderconfig.py +0 -37
- endoreg_db/migrations/0019_pdftype_cut_off_above_lines_and_more.py +0 -42
- endoreg_db/migrations/0020_rename_endoscopy_info_line_pdftype_endoscope_info_line.py +0 -18
- endoreg_db/migrations/0021_alter_pdftype_endoscope_info_line.py +0 -19
- endoreg_db/migrations/0022_alter_pdftype_endoscope_info_line.py +0 -19
- endoreg_db/migrations/0023_ttoquestionnaire_alter_pdftype_endoscope_info_line.py +0 -59
- endoreg_db/migrations/0024_remove_ttoquestionnaire_infections_and_more.py +0 -27
- endoreg_db/migrations/0025_event_alter_rawpdffile_file_patientevent.py +0 -42
- endoreg_db/migrations/0026_disease_diseaseclassification_and_more.py +0 -166
- endoreg_db/migrations/0027_labvalue_abbreviation_labvalue_default_normal_range_and_more.py +0 -38
- endoreg_db/migrations/0028_alter_unit_abbreviation.py +0 -18
- endoreg_db/migrations/0029_medicationintaketime_and_more.py +0 -75
- endoreg_db/migrations/0030_medicationindicationtype_and_more.py +0 -101
- endoreg_db/migrations/0031_rename_adapt_to_liver_function_medication_adapt_to_age_and_more.py +0 -38
- endoreg_db/migrations/0032_alter_medicationschedule_therapy_duration_d.py +0 -18
- endoreg_db/migrations/0033_medicationindication_sources.py +0 -18
- endoreg_db/migrations/0034_alter_rawpdffile_file.py +0 -20
- endoreg_db/migrations/0035_alter_medicationindication_sources.py +0 -18
- endoreg_db/migrations/0036_alter_rawpdffile_file.py +0 -20
- endoreg_db/migrations/0037_alter_medicationindication_sources.py +0 -18
- endoreg_db/migrations/0038_emissionfactor_material_product_productgroup_and_more.py +0 -164
- endoreg_db/migrations/0039_referenceproduct_name.py +0 -19
- endoreg_db/migrations/0040_quizanswertype_quizquestiontype_quizquestion_and_more.py +0 -50
- endoreg_db/migrations/0041_gender_patientmedication_medication_indication_and_more.py +0 -40
- endoreg_db/migrations/0042_casetemplateruletype_casetemplaterulevalue_and_more.py +0 -74
- endoreg_db/migrations/0043_casetemplatetype_name_de_casetemplatetype_name_en.py +0 -23
- endoreg_db/migrations/0044_casetemplateruletype_name_de_and_more.py +0 -23
- endoreg_db/migrations/0045_casetemplaterulevalue_value_type.py +0 -19
- endoreg_db/migrations/0046_casetemplaterulevalue_target_field.py +0 -18
- endoreg_db/migrations/0047_casetemplaterule_target_model.py +0 -18
- endoreg_db/migrations/0048_remove_casetemplaterule_chained_rules_and_more.py +0 -22
- endoreg_db/migrations/0049_remove_casetemplaterule_rule_values.py +0 -17
- endoreg_db/migrations/0050_casetemplaterule_rule_values.py +0 -18
- endoreg_db/migrations/0051_remove_casetemplaterule_calling_rules_and_more.py +0 -27
- endoreg_db/migrations/0052_rename_case_template_type_casetemplate_template_type.py +0 -18
- endoreg_db/migrations/0053_patientlabsampletype_patientlabsample_and_more.py +0 -38
- endoreg_db/migrations/0054_multiplecategoricalvaluedistribution_and_more.py +0 -69
- endoreg_db/migrations/0055_remove_casetemplaterule_rule_values_and_more.py +0 -59
- endoreg_db/migrations/0056_datevaluedistribution_and_more.py +0 -32
- endoreg_db/migrations/0057_remove_datevaluedistribution_max_date_and_more.py +0 -72
- endoreg_db/migrations/0058_datevaluedistribution_description_and_more.py +0 -28
- endoreg_db/migrations/0059_casetemplaterule_rule_values.py +0 -18
- endoreg_db/migrations/0060_labvalue__default_date_value_distribution_and_more.py +0 -44
- endoreg_db/migrations/0061_remove_patientlabvalue_date_patientlabvalue_datetime.py +0 -24
- endoreg_db/migrations/0062_labvalue_numeric_precision.py +0 -18
- endoreg_db/migrations/0063_alter_labvalue_numeric_precision.py +0 -18
- endoreg_db/migrations/0064_casetemplaterule_extra_parameters_and_more.py +0 -23
- endoreg_db/migrations/0065_rename__date_value_distribution_casetemplaterule_date_value_distribution_and_more.py +0 -58
- endoreg_db/migrations/0066_alter_patientlabvalue_patient_and_more.py +0 -29
- endoreg_db/migrations/0067_alter_medicationindication_indication_type.py +0 -19
- endoreg_db/models/ai_model/__init__.py +0 -3
- endoreg_db/models/ai_model/active_model.py +0 -9
- endoreg_db/models/ai_model/model_meta.py +0 -24
- endoreg_db/models/annotation/__init__.py +0 -2
- endoreg_db/models/annotation/binary_classification_annotation_task.py +0 -80
- endoreg_db/models/annotation/image_classification.py +0 -27
- endoreg_db/models/center/center.py +0 -25
- endoreg_db/models/center/center_product.py +0 -34
- endoreg_db/models/center/center_resource.py +0 -19
- endoreg_db/models/center/center_waste.py +0 -11
- endoreg_db/models/data_file/__init__.py +0 -6
- endoreg_db/models/data_file/base_classes/__init__.py +0 -2
- endoreg_db/models/data_file/base_classes/abstract_frame.py +0 -51
- endoreg_db/models/data_file/base_classes/abstract_video.py +0 -201
- endoreg_db/models/data_file/frame.py +0 -45
- endoreg_db/models/data_file/import_classes/__init__.py +0 -32
- endoreg_db/models/data_file/import_classes/processing_functions/__init__.py +0 -35
- endoreg_db/models/data_file/import_classes/processing_functions/pdf.py +0 -28
- endoreg_db/models/data_file/import_classes/processing_functions/video.py +0 -260
- endoreg_db/models/data_file/import_classes/raw_pdf.py +0 -188
- endoreg_db/models/data_file/import_classes/raw_video.py +0 -343
- endoreg_db/models/data_file/metadata/__init__.py +0 -3
- endoreg_db/models/data_file/metadata/sensitive_meta.py +0 -31
- endoreg_db/models/data_file/metadata/video_meta.py +0 -133
- endoreg_db/models/data_file/report_file.py +0 -89
- endoreg_db/models/data_file/video/__init__.py +0 -7
- endoreg_db/models/data_file/video/import_meta.py +0 -25
- endoreg_db/models/data_file/video/video.py +0 -25
- endoreg_db/models/data_file/video_segment.py +0 -107
- endoreg_db/models/disease.py +0 -56
- endoreg_db/models/emission/__init__.py +0 -1
- endoreg_db/models/emission/emission_factor.py +0 -20
- endoreg_db/models/event.py +0 -22
- endoreg_db/models/examination/__init__.py +0 -4
- endoreg_db/models/examination/examination.py +0 -26
- endoreg_db/models/examination/examination_time.py +0 -27
- endoreg_db/models/examination/examination_time_type.py +0 -24
- endoreg_db/models/examination/examination_type.py +0 -18
- endoreg_db/models/hardware/__init__.py +0 -2
- endoreg_db/models/hardware/endoscope.py +0 -44
- endoreg_db/models/information_source.py +0 -29
- endoreg_db/models/laboratory/__init__.py +0 -1
- endoreg_db/models/laboratory/lab_value.py +0 -102
- endoreg_db/models/legacy_data/__init__.py +0 -3
- endoreg_db/models/legacy_data/image.py +0 -34
- endoreg_db/models/medication/__init__.py +0 -1
- endoreg_db/models/medication/medication.py +0 -148
- endoreg_db/models/other/distribution.py +0 -215
- endoreg_db/models/patient_examination/__init__.py +0 -35
- endoreg_db/models/permissions/__init__.py +0 -44
- endoreg_db/models/persons/__init__.py +0 -7
- endoreg_db/models/persons/examiner/__init__.py +0 -2
- endoreg_db/models/persons/examiner/examiner.py +0 -16
- endoreg_db/models/persons/examiner/examiner_type.py +0 -2
- endoreg_db/models/persons/patient/__init__.py +0 -8
- endoreg_db/models/persons/patient/case/case.py +0 -30
- endoreg_db/models/persons/patient/patient.py +0 -216
- endoreg_db/models/persons/patient/patient_disease.py +0 -16
- endoreg_db/models/persons/patient/patient_event.py +0 -22
- endoreg_db/models/persons/patient/patient_lab_sample.py +0 -106
- endoreg_db/models/persons/patient/patient_medication.py +0 -44
- endoreg_db/models/persons/patient/patient_medication_schedule.py +0 -28
- endoreg_db/models/persons/portal_user_information.py +0 -27
- endoreg_db/models/prediction/__init__.py +0 -2
- endoreg_db/models/prediction/image_classification.py +0 -37
- endoreg_db/models/prediction/video_prediction_meta.py +0 -244
- endoreg_db/models/product/__init__.py +0 -5
- endoreg_db/models/product/product.py +0 -97
- endoreg_db/models/product/product_group.py +0 -19
- endoreg_db/models/product/product_material.py +0 -24
- endoreg_db/models/questionnaires/__init__.py +0 -114
- endoreg_db/models/quiz/__init__.py +0 -2
- endoreg_db/models/quiz/quiz_answer.py +0 -41
- endoreg_db/models/quiz/quiz_question.py +0 -54
- endoreg_db/models/report_reader/__init__.py +0 -2
- endoreg_db/models/report_reader/report_reader_config.py +0 -53
- endoreg_db/models/rules/__init__.py +0 -5
- endoreg_db/queries/get/__init__.py +0 -6
- endoreg_db/queries/get/center.py +0 -42
- endoreg_db/queries/get/model.py +0 -13
- endoreg_db/queries/get/patient.py +0 -14
- endoreg_db/queries/get/patient_examination.py +0 -20
- endoreg_db/queries/get/report_file.py +0 -33
- endoreg_db/queries/get/video.py +0 -31
- endoreg_db/serializers/ai_model.py +0 -19
- endoreg_db/serializers/annotation.py +0 -17
- endoreg_db/serializers/center.py +0 -11
- endoreg_db/serializers/examination.py +0 -33
- endoreg_db/serializers/frame.py +0 -13
- endoreg_db/serializers/hardware.py +0 -21
- endoreg_db/serializers/label.py +0 -22
- endoreg_db/serializers/patient.py +0 -10
- endoreg_db/serializers/prediction.py +0 -15
- endoreg_db/serializers/report_file.py +0 -7
- endoreg_db/serializers/video.py +0 -27
- endoreg_db/tests.py +0 -3
- endoreg_db/utils/legacy_ocr.py +0 -201
- endoreg_db/utils/video_metadata.py +0 -87
- endoreg_db-0.3.6.dist-info/METADATA +0 -33
- endoreg_db-0.3.6.dist-info/RECORD +0 -357
- /endoreg_db/{models/persons/patient/case/__init__.py → api/serializers/finding_descriptions.py} +0 -0
- /endoreg_db/{queries/get/annotation.py → api/views/finding_descriptions.py} +0 -0
- /endoreg_db/{queries/get/prediction.py → config/__init__.py} +0 -0
- /endoreg_db/{queries/get/video_import_meta.py → data/case_template/rule_value/.init} +0 -0
- /endoreg_db/{queries/get/video_prediction_meta.py → data/distribution/multiple_categorical/.init} +0 -0
|
@@ -0,0 +1,767 @@
|
|
|
1
|
+
from django.db import models
|
|
2
|
+
from typing import TYPE_CHECKING, Dict, List, Union
|
|
3
|
+
from endoreg_db.utils.links.requirement_link import RequirementLinks
|
|
4
|
+
import logging
|
|
5
|
+
from subprocess import run
|
|
6
|
+
|
|
7
|
+
logger = logging.getLogger(__name__)
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
QuerySet = models.QuerySet
|
|
11
|
+
|
|
12
|
+
if TYPE_CHECKING:
|
|
13
|
+
from endoreg_db.models import (
|
|
14
|
+
Disease,
|
|
15
|
+
DiseaseClassificationChoice,
|
|
16
|
+
Event,
|
|
17
|
+
EventClassification,
|
|
18
|
+
EventClassificationChoice,
|
|
19
|
+
Examination,
|
|
20
|
+
ExaminationIndication,
|
|
21
|
+
Finding,
|
|
22
|
+
FindingIntervention,
|
|
23
|
+
FindingClassification,
|
|
24
|
+
FindingClassificationChoice,
|
|
25
|
+
FindingClassificationType,
|
|
26
|
+
LabValue,
|
|
27
|
+
Medication,
|
|
28
|
+
MedicationIndication,
|
|
29
|
+
MedicationIntakeTime, # Added MedicationIntakeTime
|
|
30
|
+
MedicationSchedule,
|
|
31
|
+
PatientDisease,
|
|
32
|
+
PatientEvent,
|
|
33
|
+
PatientExamination,
|
|
34
|
+
PatientFinding,
|
|
35
|
+
PatientFindingIntervention,
|
|
36
|
+
PatientFindingClassification,
|
|
37
|
+
PatientLabValue,
|
|
38
|
+
PatientMedicationSchedule, # Added PatientMedicationSchedule
|
|
39
|
+
RequirementOperator,
|
|
40
|
+
RequirementSet,
|
|
41
|
+
Gender
|
|
42
|
+
)
|
|
43
|
+
# from endoreg_db.utils.links.requirement_link import RequirementLinks # Already imported above
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
class RequirementTypeManager(models.Manager):
|
|
47
|
+
def get_by_natural_key(self, name):
|
|
48
|
+
"""
|
|
49
|
+
Retrieve a model instance using its natural key.
|
|
50
|
+
|
|
51
|
+
Queries the database for an instance with a matching name, serving as the natural key.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
name: The natural key identifying the model instance.
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
The model instance matching the provided natural key.
|
|
58
|
+
"""
|
|
59
|
+
return self.get(name=name)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class RequirementType(models.Model):
|
|
63
|
+
"""
|
|
64
|
+
A class representing a type of requirement.
|
|
65
|
+
|
|
66
|
+
Attributes:
|
|
67
|
+
name (str): The name of the requirement type.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
name = models.CharField(max_length=100, unique=True)
|
|
71
|
+
description = models.TextField(blank=True, null=True)
|
|
72
|
+
|
|
73
|
+
objects = RequirementTypeManager()
|
|
74
|
+
|
|
75
|
+
def natural_key(self):
|
|
76
|
+
"""
|
|
77
|
+
Return the natural key for the instance as a tuple containing its name.
|
|
78
|
+
|
|
79
|
+
This tuple enables the use of natural key lookups for serialization and deserialization.
|
|
80
|
+
"""
|
|
81
|
+
return (self.name,)
|
|
82
|
+
|
|
83
|
+
def __str__(self):
|
|
84
|
+
"""Return the string representation of the requirement type's name.
|
|
85
|
+
|
|
86
|
+
Returns:
|
|
87
|
+
str: The name of the requirement type.
|
|
88
|
+
"""
|
|
89
|
+
return str(self.name)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
class RequirementManager(models.Manager):
|
|
93
|
+
def get_by_natural_key(self, name):
|
|
94
|
+
"""
|
|
95
|
+
Retrieve an instance using its natural key.
|
|
96
|
+
|
|
97
|
+
Args:
|
|
98
|
+
name: The natural key used to look up the instance.
|
|
99
|
+
|
|
100
|
+
Returns:
|
|
101
|
+
The object whose 'name' field matches the given key.
|
|
102
|
+
"""
|
|
103
|
+
return self.get(name=name)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
class Requirement(models.Model):
|
|
107
|
+
"""
|
|
108
|
+
A class representing a requirement.
|
|
109
|
+
|
|
110
|
+
Attributes:
|
|
111
|
+
name (str): The name of the requirement.
|
|
112
|
+
description (str): A description of the requirement.
|
|
113
|
+
"""
|
|
114
|
+
|
|
115
|
+
name = models.CharField(max_length=100, unique=True)
|
|
116
|
+
description = models.TextField(blank=True, null=True)
|
|
117
|
+
|
|
118
|
+
numeric_value = models.FloatField(
|
|
119
|
+
blank=True,
|
|
120
|
+
null=True,
|
|
121
|
+
help_text="Numeric value for the requirement. If not set, the requirement is not used in calculations.",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
numeric_value_min = models.FloatField(
|
|
125
|
+
blank=True,
|
|
126
|
+
null=True,
|
|
127
|
+
help_text="Minimum numeric value for the requirement. If not set, the requirement is not used in calculations.",
|
|
128
|
+
)
|
|
129
|
+
numeric_value_max = models.FloatField(
|
|
130
|
+
blank=True,
|
|
131
|
+
null=True,
|
|
132
|
+
help_text="Maximum numeric value for the requirement. If not set, the requirement is not used in calculations.",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
string_value = models.CharField(
|
|
136
|
+
max_length=100,
|
|
137
|
+
blank=True,
|
|
138
|
+
null=True,
|
|
139
|
+
help_text="String value for the requirement. If not set, the requirement is not used in calculations.",
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
string_values = models.TextField(
|
|
143
|
+
blank=True,
|
|
144
|
+
null=True,
|
|
145
|
+
help_text=" ','-separated list of string values for the requirement.If not set, the requirement is not used in calculations.",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
objects = RequirementManager()
|
|
149
|
+
|
|
150
|
+
requirement_types = models.ManyToManyField( # type: ignore[assignment]
|
|
151
|
+
"RequirementType",
|
|
152
|
+
blank=True,
|
|
153
|
+
related_name="linked_requirements",
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
operators = models.ManyToManyField( # type: ignore[assignment]
|
|
157
|
+
"RequirementOperator",
|
|
158
|
+
blank=True,
|
|
159
|
+
related_name="required_in",
|
|
160
|
+
)
|
|
161
|
+
|
|
162
|
+
unit = models.ForeignKey(
|
|
163
|
+
"Unit",
|
|
164
|
+
on_delete=models.CASCADE,
|
|
165
|
+
related_name="required_in",
|
|
166
|
+
blank=True,
|
|
167
|
+
null=True,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
examinations = models.ManyToManyField( # type: ignore[assignment]
|
|
171
|
+
"Examination",
|
|
172
|
+
blank=True,
|
|
173
|
+
related_name="required_in",
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
examination_indications = models.ManyToManyField( # type: ignore[assignment]
|
|
177
|
+
"ExaminationIndication",
|
|
178
|
+
blank=True,
|
|
179
|
+
related_name="required_in",
|
|
180
|
+
)
|
|
181
|
+
|
|
182
|
+
diseases = models.ManyToManyField( # type: ignore[assignment]
|
|
183
|
+
"Disease",
|
|
184
|
+
blank=True,
|
|
185
|
+
related_name="required_in",
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
disease_classification_choices = models.ManyToManyField( # type: ignore[assignment]
|
|
189
|
+
"DiseaseClassificationChoice",
|
|
190
|
+
blank=True,
|
|
191
|
+
related_name="required_in",
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
events = models.ManyToManyField( # type: ignore[assignment]
|
|
195
|
+
"Event",
|
|
196
|
+
blank=True,
|
|
197
|
+
related_name="required_in",
|
|
198
|
+
)
|
|
199
|
+
|
|
200
|
+
lab_values = models.ManyToManyField( # type: ignore[assignment]
|
|
201
|
+
"LabValue",
|
|
202
|
+
blank=True,
|
|
203
|
+
related_name="required_in",
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
findings = models.ManyToManyField( # type: ignore[assignment]
|
|
207
|
+
"Finding",
|
|
208
|
+
blank=True,
|
|
209
|
+
related_name="required_in",
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
finding_classifications = models.ManyToManyField( # type: ignore[assignment]
|
|
213
|
+
"FindingClassification",
|
|
214
|
+
blank=True,
|
|
215
|
+
related_name="required_in",
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
finding_classification_choices = models.ManyToManyField( # type: ignore[assignment]
|
|
219
|
+
"FindingClassificationChoice",
|
|
220
|
+
blank=True,
|
|
221
|
+
related_name="required_in",
|
|
222
|
+
)
|
|
223
|
+
|
|
224
|
+
finding_interventions = models.ManyToManyField( # type: ignore[assignment]
|
|
225
|
+
"FindingIntervention",
|
|
226
|
+
blank=True,
|
|
227
|
+
related_name="required_in",
|
|
228
|
+
)
|
|
229
|
+
|
|
230
|
+
medications = models.ManyToManyField( # type: ignore[assignment]
|
|
231
|
+
"Medication",
|
|
232
|
+
blank=True,
|
|
233
|
+
related_name="required_in",
|
|
234
|
+
)
|
|
235
|
+
|
|
236
|
+
medication_indications = models.ManyToManyField( # type: ignore[assignment]
|
|
237
|
+
"MedicationIndication",
|
|
238
|
+
blank=True,
|
|
239
|
+
related_name="required_in",
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
medication_intake_times = models.ManyToManyField( # type: ignore[assignment]
|
|
243
|
+
"MedicationIntakeTime",
|
|
244
|
+
blank=True,
|
|
245
|
+
related_name="required_in",
|
|
246
|
+
)
|
|
247
|
+
|
|
248
|
+
medication_schedules = models.ManyToManyField( # type: ignore[assignment]
|
|
249
|
+
"MedicationSchedule",
|
|
250
|
+
blank=True,
|
|
251
|
+
related_name="required_in",
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
genders = models.ManyToManyField( # type: ignore[assignment]
|
|
255
|
+
"Gender",
|
|
256
|
+
blank=True,
|
|
257
|
+
related_name="required_in",
|
|
258
|
+
)
|
|
259
|
+
|
|
260
|
+
if TYPE_CHECKING:
|
|
261
|
+
requirement_types: models.QuerySet[RequirementType]
|
|
262
|
+
operators: models.QuerySet[RequirementOperator]
|
|
263
|
+
requirement_sets: models.QuerySet[RequirementSet]
|
|
264
|
+
examinations: models.QuerySet[Examination]
|
|
265
|
+
examination_indications: models.QuerySet[ExaminationIndication]
|
|
266
|
+
lab_values: models.QuerySet[LabValue]
|
|
267
|
+
diseases: models.QuerySet[Disease]
|
|
268
|
+
disease_classification_choices: models.QuerySet[DiseaseClassificationChoice]
|
|
269
|
+
events: models.QuerySet[Event]
|
|
270
|
+
findings: models.QuerySet[Finding]
|
|
271
|
+
finding_classifications: models.QuerySet[FindingClassification]
|
|
272
|
+
finding_classification_choices: models.QuerySet[FindingClassificationChoice]
|
|
273
|
+
finding_interventions: models.QuerySet[FindingIntervention]
|
|
274
|
+
medications: models.QuerySet[Medication]
|
|
275
|
+
medication_indications: models.QuerySet[MedicationIndication]
|
|
276
|
+
medication_intake_times: models.QuerySet[MedicationIntakeTime] # Added type hint
|
|
277
|
+
medication_schedules: models.QuerySet[MedicationSchedule]
|
|
278
|
+
genders: models.QuerySet[Gender]
|
|
279
|
+
|
|
280
|
+
def natural_key(self):
|
|
281
|
+
"""
|
|
282
|
+
Returns a tuple containing the instance's name as its natural key.
|
|
283
|
+
|
|
284
|
+
This tuple provides a unique identifier for serialization purposes.
|
|
285
|
+
"""
|
|
286
|
+
return (self.name,)
|
|
287
|
+
|
|
288
|
+
def __str__(self):
|
|
289
|
+
"""Returns the name of the requirement as its string representation."""
|
|
290
|
+
return str(self.name)
|
|
291
|
+
|
|
292
|
+
@property
|
|
293
|
+
def expected_models(self) -> List[Union[
|
|
294
|
+
"Disease",
|
|
295
|
+
"DiseaseClassificationChoice",
|
|
296
|
+
"Event",
|
|
297
|
+
"EventClassification",
|
|
298
|
+
"EventClassificationChoice",
|
|
299
|
+
"Examination",
|
|
300
|
+
"ExaminationIndication",
|
|
301
|
+
"Finding",
|
|
302
|
+
"FindingIntervention",
|
|
303
|
+
"FindingClassification",
|
|
304
|
+
"FindingClassificationChoice",
|
|
305
|
+
"FindingClassificationType",
|
|
306
|
+
"LabValue",
|
|
307
|
+
"Medication",
|
|
308
|
+
"MedicationIndication",
|
|
309
|
+
"MedicationIntakeTime", # Added MedicationIntakeTime
|
|
310
|
+
"PatientDisease",
|
|
311
|
+
"PatientEvent",
|
|
312
|
+
"PatientExamination",
|
|
313
|
+
"PatientFinding",
|
|
314
|
+
"PatientFindingIntervention",
|
|
315
|
+
"PatientFindingClassification",
|
|
316
|
+
"PatientLabValue",
|
|
317
|
+
"PatientMedicationSchedule", # Added PatientMedicationSchedule
|
|
318
|
+
]]:
|
|
319
|
+
"""
|
|
320
|
+
Return the list of model classes that are expected as input for evaluating this requirement.
|
|
321
|
+
|
|
322
|
+
The returned models correspond to the requirement types linked to this requirement, mapped via the internal data model dictionary.
|
|
323
|
+
"""
|
|
324
|
+
req_types = self.requirement_types.all()
|
|
325
|
+
req_type_names = [_.name for _ in req_types]
|
|
326
|
+
|
|
327
|
+
expected_models = [self.data_model_dict[_] for _ in req_type_names]
|
|
328
|
+
# e.g. [PatientExamination, PatientFinding]
|
|
329
|
+
return expected_models
|
|
330
|
+
|
|
331
|
+
@property
|
|
332
|
+
def links(self) -> "RequirementLinks":
|
|
333
|
+
"""
|
|
334
|
+
Return a RequirementLinks object containing all non-null related model instances for this requirement.
|
|
335
|
+
|
|
336
|
+
The returned object provides structured access to all associated entities, such as examinations, diseases, findings, classifications, interventions, medications, and related choices, aggregated from the requirement's many-to-many fields.
|
|
337
|
+
"""
|
|
338
|
+
# requirement_sets is not part of RequirementLinks (avoids circular import); collect other related models
|
|
339
|
+
models_dict = RequirementLinks(
|
|
340
|
+
examinations=[_ for _ in self.examinations.all() if _ is not None],
|
|
341
|
+
examination_indications=[_ for _ in self.examination_indications.all() if _ is not None],
|
|
342
|
+
lab_values=[_ for _ in self.lab_values.all() if _ is not None],
|
|
343
|
+
diseases=[_ for _ in self.diseases.all() if _ is not None],
|
|
344
|
+
disease_classification_choices=[_ for _ in self.disease_classification_choices.all() if _ is not None],
|
|
345
|
+
events=[_ for _ in self.events.all() if _ is not None],
|
|
346
|
+
findings=[_ for _ in self.findings.all() if _ is not None],
|
|
347
|
+
finding_classifications=[_ for _ in self.finding_classifications.all() if _ is not None],
|
|
348
|
+
finding_classification_choices=[_ for _ in self.finding_classification_choices.all() if _ is not None],
|
|
349
|
+
finding_interventions=[_ for _ in self.finding_interventions.all() if _ is not None],
|
|
350
|
+
medications=[_ for _ in self.medications.all() if _ is not None],
|
|
351
|
+
medication_indications=[_ for _ in self.medication_indications.all() if _ is not None],
|
|
352
|
+
medication_intake_times=[_ for _ in self.medication_intake_times.all() if _ is not None],
|
|
353
|
+
)
|
|
354
|
+
return models_dict
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def data_model_dict(self) -> dict:
|
|
358
|
+
"""
|
|
359
|
+
Provides a mapping from requirement type names to their corresponding model classes.
|
|
360
|
+
|
|
361
|
+
Returns:
|
|
362
|
+
A dictionary where keys are requirement type names and values are model classes used for requirement evaluation.
|
|
363
|
+
"""
|
|
364
|
+
from .requirement_evaluation.requirement_type_parser import data_model_dict
|
|
365
|
+
return data_model_dict
|
|
366
|
+
|
|
367
|
+
@property
|
|
368
|
+
def active_links(self) -> Dict[str, List]:
|
|
369
|
+
"""Returns a dictionary of linked models containing only non-empty entries.
|
|
370
|
+
|
|
371
|
+
The returned dictionary includes only those related model lists that have at least one linked instance.
|
|
372
|
+
"""
|
|
373
|
+
return self.links.active()
|
|
374
|
+
|
|
375
|
+
|
|
376
|
+
def evaluate(self, *args, mode:str, **kwargs):
|
|
377
|
+
"""
|
|
378
|
+
Evaluates whether the requirement is satisfied for the given input models using linked operators and gender constraints.
|
|
379
|
+
|
|
380
|
+
Args:
|
|
381
|
+
*args: Instances or QuerySets of expected model classes to be evaluated. Each must have a `.links` property returning a `RequirementLinks` object.
|
|
382
|
+
mode: Evaluation mode; "strict" requires all operators to pass, "loose" requires any operator to pass.
|
|
383
|
+
**kwargs: Additional keyword arguments passed to operator evaluations.
|
|
384
|
+
|
|
385
|
+
Returns:
|
|
386
|
+
True if the requirement is satisfied according to the specified mode, linked operators, and gender restrictions; otherwise, False.
|
|
387
|
+
|
|
388
|
+
Raises:
|
|
389
|
+
ValueError: If an invalid mode is provided.
|
|
390
|
+
TypeError: If an input is not an instance or QuerySet of expected models, or lacks a valid `.links` attribute.
|
|
391
|
+
|
|
392
|
+
If the requirement specifies genders, only input containing a patient with a matching gender will be considered valid for evaluation.
|
|
393
|
+
"""
|
|
394
|
+
#TODO Review, Optimize or remove
|
|
395
|
+
if mode not in ["strict", "loose"]:
|
|
396
|
+
raise ValueError(f"Invalid mode: {mode}. Use 'strict' or 'loose'.")
|
|
397
|
+
|
|
398
|
+
evaluate_result_list_func = all if mode == "strict" else any
|
|
399
|
+
|
|
400
|
+
requirement_req_links = self.links
|
|
401
|
+
expected_models = self.expected_models
|
|
402
|
+
|
|
403
|
+
# helpers to avoid passing a complex tuple to isinstance/issubclass which confuses type checkers
|
|
404
|
+
def _is_expected_instance(obj) -> bool:
|
|
405
|
+
for cls in expected_models:
|
|
406
|
+
if isinstance(cls, type):
|
|
407
|
+
try:
|
|
408
|
+
if isinstance(obj, cls):
|
|
409
|
+
return True
|
|
410
|
+
except Exception:
|
|
411
|
+
# cls might not be a runtime type
|
|
412
|
+
continue
|
|
413
|
+
return False
|
|
414
|
+
|
|
415
|
+
def _is_queryset_of_expected(qs) -> bool:
|
|
416
|
+
if not isinstance(qs, models.QuerySet) or not hasattr(qs, 'model'):
|
|
417
|
+
return False
|
|
418
|
+
for cls in expected_models:
|
|
419
|
+
if isinstance(cls, type):
|
|
420
|
+
try:
|
|
421
|
+
if issubclass(qs.model, cls):
|
|
422
|
+
return True
|
|
423
|
+
except Exception:
|
|
424
|
+
continue
|
|
425
|
+
return False
|
|
426
|
+
|
|
427
|
+
# Aggregate RequirementLinks from all input arguments
|
|
428
|
+
aggregated_input_links_data = {}
|
|
429
|
+
processed_inputs_count = 0
|
|
430
|
+
|
|
431
|
+
for _input in args:
|
|
432
|
+
# Check if the input is an instance of any of the expected model types
|
|
433
|
+
if not _is_expected_instance(_input):
|
|
434
|
+
# Allow QuerySets of expected models
|
|
435
|
+
if _is_queryset_of_expected(_input):
|
|
436
|
+
# For QuerySets, evaluate each item individually and return True if any matches
|
|
437
|
+
if not _input.exists(): # Skip empty querysets
|
|
438
|
+
continue
|
|
439
|
+
|
|
440
|
+
queryset_results = []
|
|
441
|
+
for item in _input:
|
|
442
|
+
if not hasattr(item, 'links') or not isinstance(item.links, RequirementLinks):
|
|
443
|
+
raise TypeError(
|
|
444
|
+
f"Item {item} of type {type(item)} in QuerySet does not have a valid .links attribute of type RequirementLinks."
|
|
445
|
+
)
|
|
446
|
+
|
|
447
|
+
# Evaluate this single item against the requirement
|
|
448
|
+
item_input_links = RequirementLinks(**item.links.active())
|
|
449
|
+
|
|
450
|
+
# Evaluate all operators for this single item
|
|
451
|
+
item_operator_results = []
|
|
452
|
+
for operator in self.operators.all():
|
|
453
|
+
try:
|
|
454
|
+
operator_result = operator.evaluate(
|
|
455
|
+
requirement_links=requirement_req_links,
|
|
456
|
+
input_links=item_input_links,
|
|
457
|
+
requirement=self,
|
|
458
|
+
original_input_args=args,
|
|
459
|
+
**kwargs
|
|
460
|
+
)
|
|
461
|
+
item_operator_results.append(operator_result)
|
|
462
|
+
except Exception as e:
|
|
463
|
+
logger.debug(f"Operator {operator.name} evaluation failed for item {item}: {e}")
|
|
464
|
+
item_operator_results.append(False)
|
|
465
|
+
|
|
466
|
+
# Apply evaluation mode for this single item
|
|
467
|
+
item_result = evaluate_result_list_func(item_operator_results) if item_operator_results else True
|
|
468
|
+
queryset_results.append(item_result)
|
|
469
|
+
processed_inputs_count += 1
|
|
470
|
+
|
|
471
|
+
# If any item in the QuerySet matches, return True for the whole QuerySet evaluation
|
|
472
|
+
if any(queryset_results):
|
|
473
|
+
return True
|
|
474
|
+
continue # Move to the next arg after processing queryset
|
|
475
|
+
else:
|
|
476
|
+
raise TypeError(
|
|
477
|
+
f"Input type {type(_input)} is not among expected models: {self.expected_models} "
|
|
478
|
+
f"nor a QuerySet of expected models."
|
|
479
|
+
)
|
|
480
|
+
|
|
481
|
+
# Process single model instance
|
|
482
|
+
if not hasattr(_input, 'links') or not isinstance(_input.links, RequirementLinks):
|
|
483
|
+
raise TypeError(
|
|
484
|
+
f"Input {_input} of type {type(_input)} does not have a valid .links attribute of type RequirementLinks."
|
|
485
|
+
)
|
|
486
|
+
|
|
487
|
+
active_input_links = _input.links.active() # Get dict of non-empty lists
|
|
488
|
+
for link_key, link_list in active_input_links.items():
|
|
489
|
+
if link_key not in aggregated_input_links_data:
|
|
490
|
+
aggregated_input_links_data[link_key] = []
|
|
491
|
+
aggregated_input_links_data[link_key].extend(link_list)
|
|
492
|
+
processed_inputs_count += 1
|
|
493
|
+
|
|
494
|
+
if not processed_inputs_count and args: # If args were provided but none were processable (e.g. all empty querysets)
|
|
495
|
+
# This situation implies no relevant data was provided for evaluation against the requirement.
|
|
496
|
+
# Depending on operator logic (e.g., "requires at least one matching item"), this might lead to False.
|
|
497
|
+
# For "models_match_any", an empty input_links will likely result in False if requirement_req_links is not empty.
|
|
498
|
+
pass
|
|
499
|
+
|
|
500
|
+
|
|
501
|
+
# Deduplicate items within each list after aggregation
|
|
502
|
+
for key in aggregated_input_links_data:
|
|
503
|
+
try:
|
|
504
|
+
# Using dict.fromkeys to preserve order and remove duplicates for hashable items
|
|
505
|
+
aggregated_input_links_data[key] = list(dict.fromkeys(aggregated_input_links_data[key]))
|
|
506
|
+
except TypeError:
|
|
507
|
+
# Fallback for non-hashable items (though Django models are hashable)
|
|
508
|
+
temp_list = []
|
|
509
|
+
for item in aggregated_input_links_data[key]:
|
|
510
|
+
if item not in temp_list:
|
|
511
|
+
temp_list.append(item)
|
|
512
|
+
aggregated_input_links_data[key] = temp_list
|
|
513
|
+
|
|
514
|
+
final_input_links = RequirementLinks(**aggregated_input_links_data)
|
|
515
|
+
|
|
516
|
+
# Gender strict check: if this requirement has genders, only pass if patient.gender is in the set
|
|
517
|
+
genders_exist = self.genders.exists()
|
|
518
|
+
if genders_exist:
|
|
519
|
+
# Import here to avoid circular import
|
|
520
|
+
from endoreg_db.models.administration.person.patient import Patient
|
|
521
|
+
patient = None
|
|
522
|
+
for arg in args:
|
|
523
|
+
if isinstance(arg, Patient):
|
|
524
|
+
patient = arg
|
|
525
|
+
break
|
|
526
|
+
if patient is None or patient.gender is None:
|
|
527
|
+
return False
|
|
528
|
+
if not self.genders.filter(pk=patient.gender.pk).exists():
|
|
529
|
+
return False
|
|
530
|
+
|
|
531
|
+
operators = self.operators.all()
|
|
532
|
+
if not operators.exists(): # If a requirement has no operators, its evaluation is ambiguous.
|
|
533
|
+
# Consider if this should be True, False, or an error.
|
|
534
|
+
# For now, if no operators, and mode is strict, it's vacuously true. If loose, vacuously false.
|
|
535
|
+
# However, typically a requirement implies some condition.
|
|
536
|
+
# Let's assume if no operators, it cannot be satisfied unless it also has no specific links.
|
|
537
|
+
# This behavior might need further refinement based on business logic.
|
|
538
|
+
if not requirement_req_links.active(): # No conditions in requirement
|
|
539
|
+
return True # Vacuously true if requirement itself is empty
|
|
540
|
+
return False # Cannot be satisfied if requirement has conditions but no operators to check them
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
operator_results = []
|
|
544
|
+
for operator in operators:
|
|
545
|
+
# Prepare kwargs for the operator, including the current Requirement instance
|
|
546
|
+
op_kwargs = kwargs.copy() # Start with kwargs passed to Requirement.evaluate
|
|
547
|
+
op_kwargs['requirement'] = self # Add the Requirement instance itself
|
|
548
|
+
op_kwargs['original_input_args'] = args # Add the original input arguments for operators that need them (e.g., age operators)
|
|
549
|
+
operator_results.append(operator.evaluate(
|
|
550
|
+
requirement_links=requirement_req_links,
|
|
551
|
+
input_links=final_input_links,
|
|
552
|
+
**op_kwargs
|
|
553
|
+
))
|
|
554
|
+
|
|
555
|
+
is_valid = evaluate_result_list_func(operator_results)
|
|
556
|
+
|
|
557
|
+
return is_valid
|
|
558
|
+
|
|
559
|
+
def evaluate_with_details(self, *args, mode:str, **kwargs):
|
|
560
|
+
"""
|
|
561
|
+
Evaluates whether the requirement is satisfied for the given input models using linked operators and gender constraints.
|
|
562
|
+
|
|
563
|
+
Args:
|
|
564
|
+
*args: Instances or QuerySets of expected model classes to be evaluated. Each must have a `.links` property returning a `RequirementLinks` object.
|
|
565
|
+
mode: Evaluation mode; "strict" requires all operators to pass, "loose" requires any operator to pass.
|
|
566
|
+
**kwargs: Additional keyword arguments passed to operator evaluations.
|
|
567
|
+
|
|
568
|
+
Returns:
|
|
569
|
+
True if the requirement is satisfied according to the specified mode, linked operators, and gender restrictions; otherwise, False.
|
|
570
|
+
|
|
571
|
+
Raises:
|
|
572
|
+
ValueError: If an invalid mode is provided.
|
|
573
|
+
TypeError: If an input is not an instance or QuerySet of expected models, or lacks a valid `.links` attribute.
|
|
574
|
+
|
|
575
|
+
If the requirement specifies genders, only input containing a patient with a matching gender will be considered valid for evaluation.
|
|
576
|
+
"""
|
|
577
|
+
#TODO Review, Optimize or remove
|
|
578
|
+
if mode not in ["strict", "loose"]:
|
|
579
|
+
raise ValueError(f"Invalid mode: {mode}. Use 'strict' or 'loose'.")
|
|
580
|
+
|
|
581
|
+
evaluate_result_list_func = all if mode == "strict" else any
|
|
582
|
+
|
|
583
|
+
requirement_req_links = self.links
|
|
584
|
+
expected_models = self.expected_models
|
|
585
|
+
|
|
586
|
+
# helpers to avoid passing a complex tuple to isinstance/issubclass which confuses type checkers
|
|
587
|
+
def _is_expected_instance(obj) -> bool:
|
|
588
|
+
for cls in expected_models:
|
|
589
|
+
if isinstance(cls, type):
|
|
590
|
+
try:
|
|
591
|
+
if isinstance(obj, cls):
|
|
592
|
+
return True
|
|
593
|
+
except Exception:
|
|
594
|
+
# cls might not be a runtime type
|
|
595
|
+
continue
|
|
596
|
+
return False
|
|
597
|
+
|
|
598
|
+
def _is_queryset_of_expected(qs) -> bool:
|
|
599
|
+
if not isinstance(qs, models.QuerySet) or not hasattr(qs, 'model'):
|
|
600
|
+
return False
|
|
601
|
+
for cls in expected_models:
|
|
602
|
+
if isinstance(cls, type):
|
|
603
|
+
try:
|
|
604
|
+
if issubclass(qs.model, cls):
|
|
605
|
+
return True
|
|
606
|
+
except Exception:
|
|
607
|
+
continue
|
|
608
|
+
return False
|
|
609
|
+
|
|
610
|
+
# Aggregate RequirementLinks from all input arguments
|
|
611
|
+
aggregated_input_links_data = {}
|
|
612
|
+
processed_inputs_count = 0
|
|
613
|
+
|
|
614
|
+
for _input in args:
|
|
615
|
+
# Check if the input is an instance of any of the expected model types
|
|
616
|
+
if not _is_expected_instance(_input):
|
|
617
|
+
# Allow QuerySets of expected models
|
|
618
|
+
if _is_queryset_of_expected(_input):
|
|
619
|
+
# For QuerySets, evaluate each item individually and return True if any matches
|
|
620
|
+
if not _input.exists(): # Skip empty querysets
|
|
621
|
+
continue
|
|
622
|
+
|
|
623
|
+
queryset_results = []
|
|
624
|
+
for item in _input:
|
|
625
|
+
if not hasattr(item, 'links') or not isinstance(item.links, RequirementLinks):
|
|
626
|
+
raise TypeError(
|
|
627
|
+
f"Item {item} of type {type(item)} in QuerySet does not have a valid .links attribute of type RequirementLinks."
|
|
628
|
+
)
|
|
629
|
+
|
|
630
|
+
# Evaluate this single item against the requirement
|
|
631
|
+
item_input_links = RequirementLinks(**item.links.active())
|
|
632
|
+
|
|
633
|
+
# Evaluate all operators for this single item
|
|
634
|
+
item_operator_results = []
|
|
635
|
+
for operator in self.operators.all():
|
|
636
|
+
try:
|
|
637
|
+
operator_result = operator.evaluate(
|
|
638
|
+
requirement_links=requirement_req_links,
|
|
639
|
+
input_links=item_input_links,
|
|
640
|
+
requirement=self,
|
|
641
|
+
original_input_args=args,
|
|
642
|
+
**kwargs
|
|
643
|
+
)
|
|
644
|
+
item_operator_results.append(operator_result)
|
|
645
|
+
except Exception as e:
|
|
646
|
+
logger.debug(f"Operator {operator.name} evaluation failed for item {item}: {e}")
|
|
647
|
+
item_operator_results.append(False)
|
|
648
|
+
|
|
649
|
+
# Apply evaluation mode for this single item
|
|
650
|
+
item_result = evaluate_result_list_func(item_operator_results) if item_operator_results else True
|
|
651
|
+
queryset_results.append(item_result)
|
|
652
|
+
processed_inputs_count += 1
|
|
653
|
+
|
|
654
|
+
# If any item in the QuerySet matches, return True for the whole QuerySet evaluation
|
|
655
|
+
if any(queryset_results):
|
|
656
|
+
return True
|
|
657
|
+
continue # Move to the next arg after processing queryset
|
|
658
|
+
else:
|
|
659
|
+
raise TypeError(
|
|
660
|
+
f"Input type {type(_input)} is not among expected models: {self.expected_models} "
|
|
661
|
+
f"nor a QuerySet of expected models."
|
|
662
|
+
)
|
|
663
|
+
|
|
664
|
+
# Process single model instance
|
|
665
|
+
if not hasattr(_input, 'links') or not isinstance(_input.links, RequirementLinks):
|
|
666
|
+
raise TypeError(
|
|
667
|
+
f"Input {_input} of type {type(_input)} does not have a valid .links attribute of type RequirementLinks."
|
|
668
|
+
)
|
|
669
|
+
|
|
670
|
+
active_input_links = _input.links.active() # Get dict of non-empty lists
|
|
671
|
+
for link_key, link_list in active_input_links.items():
|
|
672
|
+
if link_key not in aggregated_input_links_data:
|
|
673
|
+
aggregated_input_links_data[link_key] = []
|
|
674
|
+
aggregated_input_links_data[link_key].extend(link_list)
|
|
675
|
+
processed_inputs_count += 1
|
|
676
|
+
|
|
677
|
+
if not processed_inputs_count and args: # If args were provided but none were processable (e.g. all empty querysets)
|
|
678
|
+
# This situation implies no relevant data was provided for evaluation against the requirement.
|
|
679
|
+
# Depending on operator logic (e.g., "requires at least one matching item"), this might lead to False.
|
|
680
|
+
# For "models_match_any", an empty input_links will likely result in False if requirement_req_links is not empty.
|
|
681
|
+
pass
|
|
682
|
+
|
|
683
|
+
|
|
684
|
+
# Deduplicate items within each list after aggregation
|
|
685
|
+
for key in aggregated_input_links_data:
|
|
686
|
+
try:
|
|
687
|
+
# Using dict.fromkeys to preserve order and remove duplicates for hashable items
|
|
688
|
+
aggregated_input_links_data[key] = list(dict.fromkeys(aggregated_input_links_data[key]))
|
|
689
|
+
except TypeError:
|
|
690
|
+
# Fallback for non-hashable items (though Django models are hashable)
|
|
691
|
+
temp_list = []
|
|
692
|
+
for item in aggregated_input_links_data[key]:
|
|
693
|
+
if item not in temp_list:
|
|
694
|
+
temp_list.append(item)
|
|
695
|
+
aggregated_input_links_data[key] = temp_list
|
|
696
|
+
|
|
697
|
+
final_input_links = RequirementLinks(**aggregated_input_links_data)
|
|
698
|
+
|
|
699
|
+
# Gender strict check: if this requirement has genders, only pass if patient.gender is in the set
|
|
700
|
+
genders_exist = self.genders.exists()
|
|
701
|
+
if genders_exist:
|
|
702
|
+
# Import here to avoid circular import
|
|
703
|
+
from endoreg_db.models.administration.person.patient import Patient
|
|
704
|
+
patient = None
|
|
705
|
+
for arg in args:
|
|
706
|
+
if isinstance(arg, Patient):
|
|
707
|
+
patient = arg
|
|
708
|
+
break
|
|
709
|
+
if patient is None or patient.gender is None:
|
|
710
|
+
return False
|
|
711
|
+
if not self.genders.filter(pk=patient.gender.pk).exists():
|
|
712
|
+
return False
|
|
713
|
+
|
|
714
|
+
operators = self.operators.all()
|
|
715
|
+
if not operators.exists(): # If a requirement has no operators, its evaluation is ambiguous.
|
|
716
|
+
# Consider if this should be True, False, or an error.
|
|
717
|
+
# For now, if no operators, and mode is strict, it's vacuously true. If loose, vacuously false.
|
|
718
|
+
# However, typically a requirement implies some condition.
|
|
719
|
+
# Let's assume if no operators, it cannot be satisfied unless it also has no specific links.
|
|
720
|
+
# This behavior might need further refinement based on business logic.
|
|
721
|
+
if not requirement_req_links.active(): # No conditions in requirement
|
|
722
|
+
return True # Vacuously true if requirement itself is empty
|
|
723
|
+
return False # Cannot be satisfied if requirement has conditions but no operators to check them
|
|
724
|
+
|
|
725
|
+
|
|
726
|
+
operator_results = []
|
|
727
|
+
operator_details = []
|
|
728
|
+
for operator in operators:
|
|
729
|
+
# Prepare kwargs for the operator, including the current Requirement instance
|
|
730
|
+
op_kwargs = kwargs.copy() # Start with kwargs passed to Requirement.evaluate
|
|
731
|
+
op_kwargs['requirement'] = self # Add the Requirement instance itself
|
|
732
|
+
op_kwargs['original_input_args'] = args # Add the original input arguments for operators that need them (e.g., age operators)
|
|
733
|
+
try:
|
|
734
|
+
operator_result = operator.evaluate(
|
|
735
|
+
requirement_links=requirement_req_links,
|
|
736
|
+
input_links=final_input_links,
|
|
737
|
+
**op_kwargs
|
|
738
|
+
)
|
|
739
|
+
operator_results.append(operator_result)
|
|
740
|
+
operator_details.append(f"{operator.name}: {'Passed' if operator_result else 'Failed'}")
|
|
741
|
+
except Exception as e:
|
|
742
|
+
operator_results.append(False)
|
|
743
|
+
operator_details.append(f"{operator.name}: {str(e)}")
|
|
744
|
+
|
|
745
|
+
is_valid = evaluate_result_list_func(operator_results)
|
|
746
|
+
|
|
747
|
+
# Create detailed feedback
|
|
748
|
+
if not operator_results:
|
|
749
|
+
details = "Keine Operatoren für die Bewertung verfügbar"
|
|
750
|
+
elif len(operator_results) == 1:
|
|
751
|
+
details = operator_details[0]
|
|
752
|
+
else:
|
|
753
|
+
failed_details = [detail for detail, result in zip(operator_details, operator_results) if not result]
|
|
754
|
+
if failed_details:
|
|
755
|
+
details = "; ".join(failed_details)
|
|
756
|
+
else:
|
|
757
|
+
details = "Alle Operatoren erfolgreich"
|
|
758
|
+
|
|
759
|
+
# Append working directory for debugging convenience
|
|
760
|
+
try:
|
|
761
|
+
cwd = run("pwd", capture_output=True, text=True).stdout.strip()
|
|
762
|
+
details = f"{details}\ncwd: {cwd}"
|
|
763
|
+
except Exception:
|
|
764
|
+
# non-fatal: ignore if subprocess fails
|
|
765
|
+
pass
|
|
766
|
+
|
|
767
|
+
return is_valid, details
|