endoreg-db 0.8.9.2__py3-none-any.whl → 0.8.9.10__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 +10 -5
- endoreg_db/apps.py +4 -7
- endoreg_db/authz/auth.py +1 -0
- endoreg_db/authz/backends.py +1 -1
- endoreg_db/authz/management/commands/list_routes.py +2 -0
- endoreg_db/authz/middleware.py +8 -7
- endoreg_db/authz/permissions.py +21 -10
- endoreg_db/authz/policy.py +14 -19
- endoreg_db/authz/views_auth.py +14 -10
- endoreg_db/codemods/rename_datetime_fields.py +8 -1
- endoreg_db/exceptions.py +5 -2
- endoreg_db/forms/__init__.py +0 -1
- endoreg_db/forms/examination_form.py +4 -3
- endoreg_db/forms/patient_finding_intervention_form.py +30 -8
- endoreg_db/forms/patient_form.py +9 -13
- endoreg_db/forms/questionnaires/__init__.py +1 -1
- endoreg_db/forms/settings/__init__.py +4 -1
- endoreg_db/forms/unit.py +2 -1
- endoreg_db/helpers/count_db.py +17 -14
- endoreg_db/helpers/default_objects.py +2 -1
- endoreg_db/helpers/download_segmentation_model.py +4 -3
- endoreg_db/helpers/interact.py +0 -5
- endoreg_db/helpers/test_video_helper.py +33 -25
- endoreg_db/import_files/__init__.py +1 -1
- endoreg_db/import_files/context/__init__.py +1 -1
- endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
- endoreg_db/import_files/context/ensure_center.py +4 -4
- endoreg_db/import_files/context/file_lock.py +3 -3
- endoreg_db/import_files/context/import_context.py +11 -12
- endoreg_db/import_files/context/validate_directories.py +1 -0
- endoreg_db/import_files/file_storage/create_report_file.py +57 -34
- endoreg_db/import_files/file_storage/create_video_file.py +64 -35
- endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
- endoreg_db/import_files/file_storage/state_management.py +89 -122
- endoreg_db/import_files/file_storage/storage.py +5 -1
- endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
- endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
- endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
- endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
- endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
- endoreg_db/import_files/report_import_service.py +36 -30
- endoreg_db/import_files/video_import_service.py +27 -23
- endoreg_db/logger_conf.py +56 -40
- endoreg_db/management/__init__.py +1 -1
- endoreg_db/management/commands/__init__.py +1 -1
- endoreg_db/management/commands/check_auth.py +45 -38
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
- endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
- endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
- endoreg_db/management/commands/fix_video_paths.py +75 -54
- endoreg_db/management/commands/import_report.py +1 -3
- endoreg_db/management/commands/list_routes.py +2 -0
- endoreg_db/management/commands/load_ai_model_data.py +8 -2
- endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
- endoreg_db/management/commands/load_center_data.py +3 -3
- endoreg_db/management/commands/load_distribution_data.py +35 -38
- endoreg_db/management/commands/load_endoscope_data.py +0 -3
- endoreg_db/management/commands/load_examination_data.py +20 -4
- endoreg_db/management/commands/load_finding_data.py +18 -3
- endoreg_db/management/commands/load_gender_data.py +17 -24
- endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
- endoreg_db/management/commands/load_information_source.py +0 -3
- endoreg_db/management/commands/load_lab_value_data.py +14 -3
- endoreg_db/management/commands/load_legacy_data.py +303 -0
- endoreg_db/management/commands/load_name_data.py +1 -2
- endoreg_db/management/commands/load_pdf_type_data.py +4 -8
- endoreg_db/management/commands/load_profession_data.py +0 -1
- endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
- endoreg_db/management/commands/load_requirement_data.py +6 -2
- endoreg_db/management/commands/load_unit_data.py +0 -4
- endoreg_db/management/commands/load_user_groups.py +5 -7
- endoreg_db/management/commands/model_input.py +169 -0
- endoreg_db/management/commands/register_ai_model.py +22 -16
- endoreg_db/management/commands/setup_endoreg_db.py +110 -32
- endoreg_db/management/commands/storage_management.py +14 -8
- endoreg_db/management/commands/summarize_db_content.py +154 -63
- endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
- endoreg_db/management/commands/validate_video_files.py +82 -50
- endoreg_db/management/commands/video_validation.py +4 -6
- endoreg_db/migrations/0001_initial.py +112 -63
- endoreg_db/models/__init__.py +8 -0
- endoreg_db/models/administration/ai/active_model.py +5 -5
- endoreg_db/models/administration/ai/ai_model.py +41 -18
- endoreg_db/models/administration/ai/model_type.py +1 -0
- endoreg_db/models/administration/case/case.py +22 -22
- endoreg_db/models/administration/center/__init__.py +5 -5
- endoreg_db/models/administration/center/center.py +6 -2
- endoreg_db/models/administration/center/center_resource.py +18 -4
- endoreg_db/models/administration/center/center_shift.py +3 -1
- endoreg_db/models/administration/center/center_waste.py +6 -2
- endoreg_db/models/administration/person/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/employee_type.py +3 -1
- endoreg_db/models/administration/person/examiner/__init__.py +1 -1
- endoreg_db/models/administration/person/examiner/examiner.py +10 -2
- endoreg_db/models/administration/person/names/first_name.py +6 -4
- endoreg_db/models/administration/person/names/last_name.py +4 -3
- endoreg_db/models/administration/person/patient/__init__.py +1 -1
- endoreg_db/models/administration/person/patient/patient.py +0 -1
- endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
- endoreg_db/models/administration/person/person.py +1 -1
- endoreg_db/models/administration/product/__init__.py +7 -6
- endoreg_db/models/administration/product/product.py +6 -2
- endoreg_db/models/administration/product/product_group.py +9 -7
- endoreg_db/models/administration/product/product_material.py +9 -2
- endoreg_db/models/administration/product/reference_product.py +64 -15
- endoreg_db/models/administration/qualification/qualification.py +3 -1
- endoreg_db/models/administration/shift/shift.py +3 -1
- endoreg_db/models/administration/shift/shift_type.py +12 -4
- endoreg_db/models/aidataset/__init__.py +5 -0
- endoreg_db/models/aidataset/aidataset.py +193 -0
- endoreg_db/models/label/__init__.py +1 -1
- endoreg_db/models/label/label.py +10 -2
- endoreg_db/models/label/label_set.py +3 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
- endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
- endoreg_db/models/media/__init__.py +12 -5
- endoreg_db/models/media/frame/__init__.py +1 -1
- endoreg_db/models/media/frame/frame.py +34 -8
- endoreg_db/models/media/pdf/__init__.py +2 -1
- endoreg_db/models/media/pdf/raw_pdf.py +11 -4
- endoreg_db/models/media/pdf/report_file.py +6 -2
- endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
- endoreg_db/models/media/video/create_from_file.py +20 -41
- endoreg_db/models/media/video/pipe_1.py +75 -30
- endoreg_db/models/media/video/pipe_2.py +37 -12
- endoreg_db/models/media/video/video_file.py +36 -24
- endoreg_db/models/media/video/video_file_ai.py +235 -70
- endoreg_db/models/media/video/video_file_anonymize.py +240 -65
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
- endoreg_db/models/media/video/video_file_io.py +85 -33
- endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
- endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
- endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
- endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
- endoreg_db/models/media/video/video_file_segments.py +118 -27
- endoreg_db/models/media/video/video_metadata.py +25 -6
- endoreg_db/models/media/video/video_processing.py +54 -15
- endoreg_db/models/medical/__init__.py +3 -13
- endoreg_db/models/medical/contraindication/__init__.py +3 -1
- endoreg_db/models/medical/disease.py +18 -6
- endoreg_db/models/medical/event.py +6 -2
- endoreg_db/models/medical/examination/__init__.py +5 -1
- endoreg_db/models/medical/examination/examination.py +22 -6
- endoreg_db/models/medical/examination/examination_indication.py +23 -7
- endoreg_db/models/medical/examination/examination_time.py +6 -2
- endoreg_db/models/medical/finding/__init__.py +3 -1
- endoreg_db/models/medical/finding/finding.py +37 -12
- endoreg_db/models/medical/finding/finding_classification.py +27 -8
- endoreg_db/models/medical/finding/finding_intervention.py +19 -6
- endoreg_db/models/medical/finding/finding_type.py +3 -1
- endoreg_db/models/medical/hardware/__init__.py +1 -1
- endoreg_db/models/medical/hardware/endoscope.py +14 -2
- endoreg_db/models/medical/laboratory/__init__.py +1 -1
- endoreg_db/models/medical/laboratory/lab_value.py +139 -39
- endoreg_db/models/medical/medication/__init__.py +7 -3
- endoreg_db/models/medical/medication/medication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
- endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
- endoreg_db/models/medical/medication/medication_schedule.py +3 -1
- endoreg_db/models/medical/patient/__init__.py +2 -10
- endoreg_db/models/medical/patient/medication_examples.py +3 -14
- endoreg_db/models/medical/patient/patient_disease.py +17 -5
- endoreg_db/models/medical/patient/patient_event.py +12 -4
- endoreg_db/models/medical/patient/patient_examination.py +52 -15
- endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
- endoreg_db/models/medical/patient/patient_finding.py +105 -29
- endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
- endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
- endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
- endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
- endoreg_db/models/medical/patient/patient_medication.py +25 -7
- endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
- endoreg_db/models/metadata/model_meta.py +40 -12
- endoreg_db/models/metadata/model_meta_logic.py +51 -16
- endoreg_db/models/metadata/sensitive_meta.py +65 -28
- endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
- endoreg_db/models/metadata/video_meta.py +146 -39
- endoreg_db/models/metadata/video_prediction_logic.py +70 -21
- endoreg_db/models/metadata/video_prediction_meta.py +80 -27
- endoreg_db/models/operation_log.py +63 -0
- endoreg_db/models/other/__init__.py +10 -10
- endoreg_db/models/other/distribution/__init__.py +9 -7
- endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
- endoreg_db/models/other/emission/__init__.py +1 -1
- endoreg_db/models/other/emission/emission_factor.py +9 -3
- endoreg_db/models/other/information_source.py +15 -5
- endoreg_db/models/other/material.py +3 -1
- endoreg_db/models/other/transport_route.py +3 -1
- endoreg_db/models/other/unit.py +6 -2
- endoreg_db/models/report/report.py +0 -1
- endoreg_db/models/requirement/requirement.py +84 -27
- endoreg_db/models/requirement/requirement_error.py +5 -6
- endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
- endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
- endoreg_db/models/requirement/requirement_operator.py +28 -8
- endoreg_db/models/requirement/requirement_set.py +34 -11
- endoreg_db/models/state/__init__.py +1 -0
- endoreg_db/models/state/audit_ledger.py +9 -2
- endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
- endoreg_db/models/state/processing_history/processing_history.py +136 -0
- endoreg_db/models/state/raw_pdf.py +0 -1
- endoreg_db/models/state/video.py +2 -4
- endoreg_db/models/utils.py +4 -2
- endoreg_db/queries/__init__.py +2 -6
- endoreg_db/queries/annotations/__init__.py +1 -3
- endoreg_db/queries/annotations/legacy.py +37 -26
- endoreg_db/root_urls.py +3 -4
- endoreg_db/schemas/examination_evaluation.py +3 -0
- endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
- endoreg_db/serializers/__init__.py +2 -8
- endoreg_db/serializers/administration/__init__.py +1 -2
- endoreg_db/serializers/administration/ai/__init__.py +0 -1
- endoreg_db/serializers/administration/ai/active_model.py +3 -1
- endoreg_db/serializers/administration/ai/ai_model.py +5 -3
- endoreg_db/serializers/administration/ai/model_type.py +3 -1
- endoreg_db/serializers/administration/center.py +7 -2
- endoreg_db/serializers/administration/gender.py +4 -2
- endoreg_db/serializers/anonymization.py +13 -13
- endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
- endoreg_db/serializers/examination/__init__.py +1 -1
- endoreg_db/serializers/examination/base.py +12 -13
- endoreg_db/serializers/examination/dropdown.py +6 -7
- endoreg_db/serializers/examination_serializer.py +3 -6
- endoreg_db/serializers/finding/__init__.py +1 -1
- endoreg_db/serializers/finding/finding.py +14 -7
- endoreg_db/serializers/finding_classification/__init__.py +3 -3
- endoreg_db/serializers/finding_classification/choice.py +3 -3
- endoreg_db/serializers/finding_classification/classification.py +2 -4
- endoreg_db/serializers/label_video_segment/__init__.py +5 -3
- endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
- endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
- endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
- endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
- endoreg_db/serializers/meta/__init__.py +1 -2
- endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
- endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
- endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
- endoreg_db/serializers/misc/__init__.py +2 -2
- endoreg_db/serializers/misc/file_overview.py +11 -7
- endoreg_db/serializers/misc/stats.py +10 -8
- endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
- endoreg_db/serializers/misc/upload_job.py +32 -29
- endoreg_db/serializers/patient/__init__.py +2 -1
- endoreg_db/serializers/patient/patient.py +32 -15
- endoreg_db/serializers/patient/patient_dropdown.py +11 -3
- endoreg_db/serializers/patient_examination/__init__.py +1 -1
- endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
- endoreg_db/serializers/patient_finding/__init__.py +1 -1
- endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
- endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
- endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
- endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
- endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
- endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
- endoreg_db/serializers/pdf/__init__.py +1 -3
- endoreg_db/serializers/requirements/requirement_schema.py +1 -6
- endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
- endoreg_db/serializers/video/__init__.py +2 -2
- endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
- endoreg_db/serializers/video/video_file_brief.py +6 -2
- endoreg_db/serializers/video/video_file_detail.py +36 -23
- endoreg_db/serializers/video/video_file_list.py +4 -2
- endoreg_db/serializers/video/video_processing_history.py +54 -50
- endoreg_db/services/__init__.py +1 -1
- endoreg_db/services/anonymization.py +2 -2
- endoreg_db/services/examination_evaluation.py +40 -17
- endoreg_db/services/model_meta_from_hf.py +76 -0
- endoreg_db/services/polling_coordinator.py +101 -70
- endoreg_db/services/pseudonym_service.py +27 -22
- endoreg_db/services/report_import.py +6 -3
- endoreg_db/services/segment_sync.py +75 -59
- endoreg_db/services/video_import.py +6 -7
- endoreg_db/urls/__init__.py +2 -2
- endoreg_db/urls/ai.py +7 -25
- endoreg_db/urls/anonymization.py +61 -15
- endoreg_db/urls/auth.py +4 -4
- endoreg_db/urls/classification.py +4 -9
- endoreg_db/urls/examination.py +27 -18
- endoreg_db/urls/media.py +27 -34
- endoreg_db/urls/patient.py +11 -7
- endoreg_db/urls/requirements.py +3 -1
- endoreg_db/urls/root_urls.py +2 -3
- endoreg_db/urls/stats.py +24 -16
- endoreg_db/urls/upload.py +3 -11
- endoreg_db/utils/__init__.py +14 -15
- endoreg_db/utils/ai/__init__.py +1 -1
- endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
- endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
- endoreg_db/utils/ai/get.py +2 -1
- endoreg_db/utils/ai/inference_dataset.py +14 -15
- endoreg_db/utils/ai/model_training/config.py +117 -0
- endoreg_db/utils/ai/model_training/dataset.py +74 -0
- endoreg_db/utils/ai/model_training/losses.py +68 -0
- endoreg_db/utils/ai/model_training/metrics.py +78 -0
- endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
- endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
- endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
- endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
- endoreg_db/utils/ai/predict.py +4 -4
- endoreg_db/utils/ai/preprocess.py +19 -11
- endoreg_db/utils/calc_duration_seconds.py +4 -4
- endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
- endoreg_db/utils/check_video_files.py +74 -47
- endoreg_db/utils/cropping.py +10 -9
- endoreg_db/utils/dataloader.py +11 -3
- endoreg_db/utils/dates.py +3 -4
- endoreg_db/utils/defaults/set_default_center.py +7 -6
- endoreg_db/utils/env.py +6 -2
- endoreg_db/utils/extract_specific_frames.py +24 -9
- endoreg_db/utils/file_operations.py +30 -18
- endoreg_db/utils/fix_video_path_direct.py +57 -41
- endoreg_db/utils/frame_anonymization_utils.py +157 -157
- endoreg_db/utils/hashs.py +3 -18
- endoreg_db/utils/links/requirement_link.py +96 -52
- endoreg_db/utils/ocr.py +30 -25
- endoreg_db/utils/operation_log.py +61 -0
- endoreg_db/utils/parse_and_generate_yaml.py +12 -13
- endoreg_db/utils/paths.py +6 -6
- endoreg_db/utils/permissions.py +40 -24
- endoreg_db/utils/pipelines/process_video_dir.py +50 -26
- endoreg_db/utils/product/sum_emissions.py +5 -3
- endoreg_db/utils/product/sum_weights.py +4 -2
- endoreg_db/utils/pydantic_models/__init__.py +3 -4
- endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
- endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
- endoreg_db/utils/setup_config.py +21 -5
- endoreg_db/utils/storage.py +3 -1
- endoreg_db/utils/translation.py +19 -15
- endoreg_db/utils/uuid.py +1 -0
- endoreg_db/utils/validate_endo_roi.py +12 -4
- endoreg_db/utils/validate_subcategory_dict.py +26 -24
- endoreg_db/utils/validate_video_detailed.py +207 -149
- endoreg_db/utils/video/__init__.py +7 -3
- endoreg_db/utils/video/extract_frames.py +30 -18
- endoreg_db/utils/video/names.py +11 -6
- endoreg_db/utils/video/streaming_processor.py +175 -101
- endoreg_db/utils/video/video_splitter.py +30 -19
- endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
- endoreg_db/views/__init__.py +0 -20
- endoreg_db/views/anonymization/__init__.py +6 -2
- endoreg_db/views/anonymization/media_management.py +2 -6
- endoreg_db/views/anonymization/overview.py +34 -1
- endoreg_db/views/anonymization/validate.py +79 -18
- endoreg_db/views/auth/__init__.py +1 -1
- endoreg_db/views/auth/keycloak.py +16 -14
- endoreg_db/views/examination/__init__.py +12 -15
- endoreg_db/views/examination/examination.py +5 -5
- endoreg_db/views/examination/examination_manifest_cache.py +5 -5
- endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
- endoreg_db/views/examination/get_finding_classifications.py +9 -7
- endoreg_db/views/examination/get_findings.py +8 -10
- endoreg_db/views/examination/get_instruments.py +3 -2
- endoreg_db/views/examination/get_interventions.py +1 -1
- endoreg_db/views/finding/__init__.py +2 -2
- endoreg_db/views/finding/finding.py +58 -54
- endoreg_db/views/finding/get_classifications.py +1 -1
- endoreg_db/views/finding/get_interventions.py +1 -1
- endoreg_db/views/finding_classification/__init__.py +5 -5
- endoreg_db/views/finding_classification/finding_classification.py +5 -6
- endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
- endoreg_db/views/media/__init__.py +13 -13
- endoreg_db/views/media/pdf_media.py +9 -9
- endoreg_db/views/media/sensitive_metadata.py +10 -7
- endoreg_db/views/media/video_media.py +4 -4
- endoreg_db/views/meta/__init__.py +1 -1
- endoreg_db/views/meta/sensitive_meta_list.py +20 -22
- endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
- endoreg_db/views/misc/__init__.py +6 -34
- endoreg_db/views/misc/center.py +2 -1
- endoreg_db/views/misc/csrf.py +2 -1
- endoreg_db/views/misc/gender.py +2 -1
- endoreg_db/views/misc/stats.py +141 -106
- endoreg_db/views/patient/__init__.py +1 -3
- endoreg_db/views/patient/patient.py +141 -99
- endoreg_db/views/patient_examination/__init__.py +5 -5
- endoreg_db/views/patient_examination/patient_examination.py +43 -42
- endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
- endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
- endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
- endoreg_db/views/patient_examination/video.py +114 -80
- endoreg_db/views/patient_finding/__init__.py +1 -1
- endoreg_db/views/patient_finding/patient_finding.py +17 -10
- endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
- endoreg_db/views/patient_finding_classification/__init__.py +1 -1
- endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
- endoreg_db/views/report/reimport.py +1 -1
- endoreg_db/views/report/report_stream.py +5 -8
- endoreg_db/views/requirement/__init__.py +2 -1
- endoreg_db/views/requirement/evaluate.py +7 -9
- endoreg_db/views/requirement/lookup.py +2 -3
- endoreg_db/views/requirement/lookup_store.py +0 -1
- endoreg_db/views/requirement/requirement_utils.py +2 -4
- endoreg_db/views/stats/__init__.py +4 -4
- endoreg_db/views/stats/stats_views.py +152 -115
- endoreg_db/views/video/__init__.py +18 -27
- endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
- endoreg_db/views/{ai → video/ai}/label.py +20 -16
- endoreg_db/views/video/correction.py +5 -6
- endoreg_db/views/video/reimport.py +134 -99
- endoreg_db/views/video/segments_crud.py +134 -44
- endoreg_db/views/video/video_apply_mask.py +13 -12
- endoreg_db/views/video/video_correction.py +2 -1
- endoreg_db/views/video/video_download_processed.py +15 -15
- endoreg_db/views/video/video_meta_stats.py +7 -6
- endoreg_db/views/video/video_processing_history.py +3 -2
- endoreg_db/views/video/video_remove_frames.py +13 -12
- endoreg_db/views/video/video_stream.py +110 -82
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +434 -431
- endoreg_db/management/commands/import_fallback_video.py +0 -203
- endoreg_db/management/commands/import_video.py +0 -422
- endoreg_db/management/commands/import_video_with_classification.py +0 -367
- endoreg_db/models/media/processing_history/processing_history.py +0 -96
- endoreg_db/serializers/label/__init__.py +0 -7
- endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
- endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
- endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
- endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
- endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
- endoreg_db/services/__old/pdf_import.py +0 -1487
- endoreg_db/services/__old/video_import.py +0 -1306
- endoreg_db/tasks/upload_tasks.py +0 -216
- endoreg_db/tasks/video_ingest.py +0 -161
- endoreg_db/tasks/video_processing_tasks.py +0 -327
- endoreg_db/views/misc/translation.py +0 -182
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -55,7 +55,9 @@ def _add_months(base_date: datetime.date, months: int) -> datetime.date:
|
|
|
55
55
|
return base_date.replace(year=year, month=month, day=day)
|
|
56
56
|
|
|
57
57
|
|
|
58
|
-
def _shift_date_by_unit(
|
|
58
|
+
def _shift_date_by_unit(
|
|
59
|
+
base_date: datetime.date, value: int, unit: str
|
|
60
|
+
) -> datetime.date:
|
|
59
61
|
"""Returns ``base_date`` shifted by ``value`` units using the supported unit set.
|
|
60
62
|
|
|
61
63
|
Hour-level offsets are not supported because ``base_date`` is a ``date``
|
|
@@ -65,7 +67,9 @@ def _shift_date_by_unit(base_date: datetime.date, value: int, unit: str) -> date
|
|
|
65
67
|
if unit == "days":
|
|
66
68
|
return base_date + timedelta(days=value)
|
|
67
69
|
if unit == "hours":
|
|
68
|
-
raise NotImplementedError(
|
|
70
|
+
raise NotImplementedError(
|
|
71
|
+
"Hour-level timeframe comparisons require datetime-aware inputs; _shift_date_by_unit only operates on date objects."
|
|
72
|
+
)
|
|
69
73
|
if unit == "weeks":
|
|
70
74
|
return base_date + timedelta(weeks=value)
|
|
71
75
|
if unit == "months":
|
|
@@ -75,7 +79,9 @@ def _shift_date_by_unit(base_date: datetime.date, value: int, unit: str) -> date
|
|
|
75
79
|
raise NotImplementedError(f"Timeframe unit '{unit}' is not supported.")
|
|
76
80
|
|
|
77
81
|
|
|
78
|
-
def _is_date_in_timeframe(
|
|
82
|
+
def _is_date_in_timeframe(
|
|
83
|
+
date_to_check: datetime.date | None, requirement: "Requirement"
|
|
84
|
+
) -> bool:
|
|
79
85
|
"""
|
|
80
86
|
Checks if a given date falls within the timeframe specified by a Requirement.
|
|
81
87
|
|
|
@@ -107,7 +113,9 @@ def _is_date_in_timeframe(date_to_check: datetime.date | None, requirement: "Req
|
|
|
107
113
|
|
|
108
114
|
unit = _normalize_timeframe_unit(requirement)
|
|
109
115
|
if not unit:
|
|
110
|
-
raise NotImplementedError(
|
|
116
|
+
raise NotImplementedError(
|
|
117
|
+
"Timeframe unit could not be resolved from requirement's Unit name/abbreviation."
|
|
118
|
+
)
|
|
111
119
|
|
|
112
120
|
today = datetime.date.today()
|
|
113
121
|
timeframe_start_delta = int(requirement.numeric_value_min)
|
|
@@ -122,7 +130,9 @@ def _is_date_in_timeframe(date_to_check: datetime.date | None, requirement: "Req
|
|
|
122
130
|
return start_date_bound <= date_to_check <= end_date_bound
|
|
123
131
|
|
|
124
132
|
|
|
125
|
-
def _evaluate_models_match_any(
|
|
133
|
+
def _evaluate_models_match_any(
|
|
134
|
+
requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs
|
|
135
|
+
) -> bool:
|
|
126
136
|
"""
|
|
127
137
|
Checks if the requirement_links matches any of the input_links.
|
|
128
138
|
|
|
@@ -161,7 +171,9 @@ def _evaluate_models_match_any_in_timeframe(
|
|
|
161
171
|
# --- Handle PatientEvents ---
|
|
162
172
|
# Check if the requirement is concerned with events
|
|
163
173
|
if requirement_links.events: # This list contains Event model instances
|
|
164
|
-
required_event_models = set(
|
|
174
|
+
required_event_models = set(
|
|
175
|
+
requirement_links.events
|
|
176
|
+
) # Target Event models from the Requirement
|
|
165
177
|
|
|
166
178
|
# input_links.patient_events contains PatientEvent instances provided as input
|
|
167
179
|
for patient_event_instance in input_links.patient_events:
|
|
@@ -191,7 +203,10 @@ def _evaluate_models_match_any_in_timeframe(
|
|
|
191
203
|
|
|
192
204
|
|
|
193
205
|
def _evaluate_models_match_all_in_timeframe(
|
|
194
|
-
requirement_links: "RequirementLinks",
|
|
206
|
+
requirement_links: "RequirementLinks",
|
|
207
|
+
input_links: "RequirementLinks",
|
|
208
|
+
requirement: "Requirement",
|
|
209
|
+
**kwargs,
|
|
195
210
|
) -> bool:
|
|
196
211
|
if not _evaluate_models_match_all(requirement_links, input_links, **kwargs):
|
|
197
212
|
return False
|
|
@@ -211,7 +226,9 @@ def _evaluate_models_match_all_in_timeframe(
|
|
|
211
226
|
found_in_timeframe = False
|
|
212
227
|
for patient_event in patient_events:
|
|
213
228
|
if getattr(patient_event, "event", None) == event:
|
|
214
|
-
if _is_date_in_timeframe(
|
|
229
|
+
if _is_date_in_timeframe(
|
|
230
|
+
getattr(patient_event, "date", None), requirement
|
|
231
|
+
):
|
|
215
232
|
found_in_timeframe = True
|
|
216
233
|
break
|
|
217
234
|
if not found_in_timeframe:
|
|
@@ -223,7 +240,10 @@ def _evaluate_models_match_all_in_timeframe(
|
|
|
223
240
|
|
|
224
241
|
|
|
225
242
|
def _evaluate_models_match_none_in_timeframe(
|
|
226
|
-
requirement_links: "RequirementLinks",
|
|
243
|
+
requirement_links: "RequirementLinks",
|
|
244
|
+
input_links: "RequirementLinks",
|
|
245
|
+
requirement: "Requirement",
|
|
246
|
+
**kwargs,
|
|
227
247
|
) -> bool:
|
|
228
248
|
if not requirement_links.events:
|
|
229
249
|
return True
|
|
@@ -239,7 +259,9 @@ def _evaluate_models_match_none_in_timeframe(
|
|
|
239
259
|
return True
|
|
240
260
|
|
|
241
261
|
|
|
242
|
-
def _evaluate_models_match_all(
|
|
262
|
+
def _evaluate_models_match_all(
|
|
263
|
+
requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs
|
|
264
|
+
) -> bool:
|
|
243
265
|
"""
|
|
244
266
|
Evaluates if all active links in requirement_links are present in input_links.
|
|
245
267
|
|
|
@@ -256,7 +278,9 @@ def _evaluate_models_match_all(requirement_links: "RequirementLinks", input_link
|
|
|
256
278
|
True if all specified items in requirement_links are found in input_links,
|
|
257
279
|
False otherwise.
|
|
258
280
|
"""
|
|
259
|
-
active_req_links =
|
|
281
|
+
active_req_links = (
|
|
282
|
+
requirement_links.active()
|
|
283
|
+
) # Get dict of non-empty lists from requirement
|
|
260
284
|
|
|
261
285
|
if not active_req_links: # If the requirement specifies no actual items to link
|
|
262
286
|
return True # Vacuously true, as there are no conditions to fail
|
|
@@ -279,7 +303,9 @@ def _evaluate_models_match_all(requirement_links: "RequirementLinks", input_link
|
|
|
279
303
|
return True
|
|
280
304
|
|
|
281
305
|
|
|
282
|
-
def _evaluate_models_match_none(
|
|
306
|
+
def _evaluate_models_match_none(
|
|
307
|
+
requirement_links: "RequirementLinks", input_links: "RequirementLinks", **kwargs
|
|
308
|
+
) -> bool:
|
|
283
309
|
"""Returns True when no required models are present in the input links."""
|
|
284
310
|
active_req_links = requirement_links.active()
|
|
285
311
|
if not active_req_links:
|
|
@@ -366,28 +392,48 @@ def _count_matching_events_in_timeframe(
|
|
|
366
392
|
return matched_events
|
|
367
393
|
|
|
368
394
|
|
|
369
|
-
def _evaluate_models_match_n(
|
|
395
|
+
def _evaluate_models_match_n(
|
|
396
|
+
requirement_links: "RequirementLinks",
|
|
397
|
+
input_links: "RequirementLinks",
|
|
398
|
+
requirement: "Requirement",
|
|
399
|
+
**kwargs,
|
|
400
|
+
) -> bool:
|
|
370
401
|
expected = _resolve_expected_count(requirement)
|
|
371
402
|
if expected is None:
|
|
372
403
|
return False
|
|
373
404
|
return _count_matching_models(requirement_links, input_links) == expected
|
|
374
405
|
|
|
375
406
|
|
|
376
|
-
def _evaluate_models_match_n_or_more(
|
|
407
|
+
def _evaluate_models_match_n_or_more(
|
|
408
|
+
requirement_links: "RequirementLinks",
|
|
409
|
+
input_links: "RequirementLinks",
|
|
410
|
+
requirement: "Requirement",
|
|
411
|
+
**kwargs,
|
|
412
|
+
) -> bool:
|
|
377
413
|
threshold = _resolve_expected_count(requirement)
|
|
378
414
|
if threshold is None:
|
|
379
415
|
return False
|
|
380
416
|
return _count_matching_models(requirement_links, input_links) >= max(threshold, 0)
|
|
381
417
|
|
|
382
418
|
|
|
383
|
-
def _evaluate_models_match_n_or_less(
|
|
419
|
+
def _evaluate_models_match_n_or_less(
|
|
420
|
+
requirement_links: "RequirementLinks",
|
|
421
|
+
input_links: "RequirementLinks",
|
|
422
|
+
requirement: "Requirement",
|
|
423
|
+
**kwargs,
|
|
424
|
+
) -> bool:
|
|
384
425
|
limit = _resolve_expected_count(requirement)
|
|
385
426
|
if limit is None:
|
|
386
427
|
return False
|
|
387
428
|
return _count_matching_models(requirement_links, input_links) <= limit
|
|
388
429
|
|
|
389
430
|
|
|
390
|
-
def _evaluate_models_match_count_in_range(
|
|
431
|
+
def _evaluate_models_match_count_in_range(
|
|
432
|
+
requirement_links: "RequirementLinks",
|
|
433
|
+
input_links: "RequirementLinks",
|
|
434
|
+
requirement: "Requirement",
|
|
435
|
+
**kwargs,
|
|
436
|
+
) -> bool:
|
|
391
437
|
if requirement.numeric_value_min is None or requirement.numeric_value_max is None:
|
|
392
438
|
return False
|
|
393
439
|
|
|
@@ -400,7 +446,12 @@ def _evaluate_models_match_count_in_range(requirement_links: "RequirementLinks",
|
|
|
400
446
|
return lower <= match_count <= upper
|
|
401
447
|
|
|
402
448
|
|
|
403
|
-
def _evaluate_models_match_n_in_timeframe(
|
|
449
|
+
def _evaluate_models_match_n_in_timeframe(
|
|
450
|
+
requirement_links: "RequirementLinks",
|
|
451
|
+
input_links: "RequirementLinks",
|
|
452
|
+
requirement: "Requirement",
|
|
453
|
+
**kwargs,
|
|
454
|
+
) -> bool:
|
|
404
455
|
if not _has_timeframe_configuration(requirement):
|
|
405
456
|
return False
|
|
406
457
|
|
|
@@ -408,11 +459,17 @@ def _evaluate_models_match_n_in_timeframe(requirement_links: "RequirementLinks",
|
|
|
408
459
|
if expected is None:
|
|
409
460
|
return False
|
|
410
461
|
|
|
411
|
-
return
|
|
462
|
+
return (
|
|
463
|
+
_count_matching_events_in_timeframe(requirement_links, input_links, requirement)
|
|
464
|
+
== expected
|
|
465
|
+
)
|
|
412
466
|
|
|
413
467
|
|
|
414
468
|
def _evaluate_models_match_n_or_more_in_timeframe(
|
|
415
|
-
requirement_links: "RequirementLinks",
|
|
469
|
+
requirement_links: "RequirementLinks",
|
|
470
|
+
input_links: "RequirementLinks",
|
|
471
|
+
requirement: "Requirement",
|
|
472
|
+
**kwargs,
|
|
416
473
|
) -> bool:
|
|
417
474
|
if not _has_timeframe_configuration(requirement):
|
|
418
475
|
return False
|
|
@@ -421,11 +478,16 @@ def _evaluate_models_match_n_or_more_in_timeframe(
|
|
|
421
478
|
if threshold is None:
|
|
422
479
|
return False
|
|
423
480
|
|
|
424
|
-
return _count_matching_events_in_timeframe(
|
|
481
|
+
return _count_matching_events_in_timeframe(
|
|
482
|
+
requirement_links, input_links, requirement
|
|
483
|
+
) >= max(threshold, 0)
|
|
425
484
|
|
|
426
485
|
|
|
427
486
|
def _evaluate_models_match_n_or_less_in_timeframe(
|
|
428
|
-
requirement_links: "RequirementLinks",
|
|
487
|
+
requirement_links: "RequirementLinks",
|
|
488
|
+
input_links: "RequirementLinks",
|
|
489
|
+
requirement: "Requirement",
|
|
490
|
+
**kwargs,
|
|
429
491
|
) -> bool:
|
|
430
492
|
if not _has_timeframe_configuration(requirement):
|
|
431
493
|
return False
|
|
@@ -434,10 +496,18 @@ def _evaluate_models_match_n_or_less_in_timeframe(
|
|
|
434
496
|
if limit is None:
|
|
435
497
|
return False
|
|
436
498
|
|
|
437
|
-
return
|
|
499
|
+
return (
|
|
500
|
+
_count_matching_events_in_timeframe(requirement_links, input_links, requirement)
|
|
501
|
+
<= limit
|
|
502
|
+
)
|
|
438
503
|
|
|
439
504
|
|
|
440
|
-
def _evaluate_age_gte(
|
|
505
|
+
def _evaluate_age_gte(
|
|
506
|
+
requirement_links: "RequirementLinks",
|
|
507
|
+
input_links: "RequirementLinks",
|
|
508
|
+
requirement: "Requirement",
|
|
509
|
+
**kwargs,
|
|
510
|
+
) -> bool:
|
|
441
511
|
"""
|
|
442
512
|
Checks if any patient in the input has an age greater than or equal to the requirement's numeric_value.
|
|
443
513
|
|
|
@@ -465,35 +535,54 @@ def _evaluate_age_gte(requirement_links: "RequirementLinks", input_links: "Requi
|
|
|
465
535
|
|
|
466
536
|
# Check if we have Patient instances in the original_input_args
|
|
467
537
|
original_args = kwargs.get("original_input_args", [])
|
|
468
|
-
logger.debug(
|
|
538
|
+
logger.debug(
|
|
539
|
+
f"age_gte: Found {len(original_args)} original input arguments: {[type(arg).__name__ for arg in original_args]}"
|
|
540
|
+
)
|
|
469
541
|
|
|
470
542
|
for i, arg in enumerate(original_args):
|
|
471
543
|
logger.debug(f"age_gte: Checking argument {i}: {type(arg).__name__}")
|
|
472
544
|
if isinstance(arg, Patient):
|
|
473
545
|
patient_age = arg.age()
|
|
474
|
-
logger.debug(
|
|
546
|
+
logger.debug(
|
|
547
|
+
f"age_gte: Patient {arg} has age {patient_age}, comparing with min_age {min_age}"
|
|
548
|
+
)
|
|
475
549
|
if patient_age is not None and patient_age >= min_age:
|
|
476
|
-
logger.debug(
|
|
550
|
+
logger.debug(
|
|
551
|
+
f"age_gte: Patient age {patient_age} >= {min_age}, returning True"
|
|
552
|
+
)
|
|
477
553
|
return True
|
|
478
554
|
else:
|
|
479
|
-
logger.debug(
|
|
555
|
+
logger.debug(
|
|
556
|
+
f"age_gte: Patient age {patient_age} < {min_age} or is None"
|
|
557
|
+
)
|
|
480
558
|
# Handle QuerySets of patients
|
|
481
559
|
elif hasattr(arg, "model") and issubclass(arg.model, Patient):
|
|
482
560
|
logger.debug(f"age_gte: Found Patient QuerySet with {arg.count()} patients")
|
|
483
561
|
for patient in arg:
|
|
484
562
|
patient_age = patient.age()
|
|
485
|
-
logger.debug(
|
|
563
|
+
logger.debug(
|
|
564
|
+
f"age_gte: Patient {patient} has age {patient_age}, comparing with min_age {min_age}"
|
|
565
|
+
)
|
|
486
566
|
if patient_age is not None and patient_age >= min_age:
|
|
487
|
-
logger.debug(
|
|
567
|
+
logger.debug(
|
|
568
|
+
f"age_gte: Patient age {patient_age} >= {min_age}, returning True"
|
|
569
|
+
)
|
|
488
570
|
return True
|
|
489
571
|
else:
|
|
490
|
-
logger.debug(
|
|
572
|
+
logger.debug(
|
|
573
|
+
f"age_gte: Argument {i} is not a Patient or Patient QuerySet: {type(arg)}"
|
|
574
|
+
)
|
|
491
575
|
|
|
492
576
|
logger.debug(f"age_gte: No patient found with age >= {min_age}, returning False")
|
|
493
577
|
return False
|
|
494
578
|
|
|
495
579
|
|
|
496
|
-
def _evaluate_age_lte(
|
|
580
|
+
def _evaluate_age_lte(
|
|
581
|
+
requirement_links: "RequirementLinks",
|
|
582
|
+
input_links: "RequirementLinks",
|
|
583
|
+
requirement: "Requirement",
|
|
584
|
+
**kwargs,
|
|
585
|
+
) -> bool:
|
|
497
586
|
"""
|
|
498
587
|
Checks if any patient in the input has an age less than or equal to the requirement's numeric_value.
|
|
499
588
|
|
|
@@ -530,7 +619,12 @@ def _evaluate_age_lte(requirement_links: "RequirementLinks", input_links: "Requi
|
|
|
530
619
|
return False
|
|
531
620
|
|
|
532
621
|
|
|
533
|
-
def dispatch_operator_evaluation(
|
|
622
|
+
def dispatch_operator_evaluation(
|
|
623
|
+
operator_name: str,
|
|
624
|
+
requirement_links: "RequirementLinks",
|
|
625
|
+
input_links: "RequirementLinks",
|
|
626
|
+
**kwargs,
|
|
627
|
+
) -> bool:
|
|
534
628
|
"""
|
|
535
629
|
Dispatches the evaluation to the appropriate function based on the operator name.
|
|
536
630
|
|
|
@@ -547,29 +641,43 @@ def dispatch_operator_evaluation(operator_name: str, requirement_links: "Require
|
|
|
547
641
|
Raises:
|
|
548
642
|
NotImplementedError: If the evaluation logic for the operator's name is not implemented.
|
|
549
643
|
"""
|
|
550
|
-
from endoreg_db.models.requirement.requirement import
|
|
644
|
+
from endoreg_db.models.requirement.requirement import (
|
|
645
|
+
Requirement,
|
|
646
|
+
) # Runtime import for isinstance
|
|
551
647
|
|
|
552
648
|
from .lab_value_operators import LAB_VALUE_OPERATOR_FUNCTIONS
|
|
553
649
|
|
|
554
650
|
eval_func = None
|
|
555
|
-
requirement = kwargs.get(
|
|
651
|
+
requirement = kwargs.get(
|
|
652
|
+
"requirement"
|
|
653
|
+
) # Get requirement for operators that need it
|
|
556
654
|
|
|
557
655
|
def _kwargs_without_requirement() -> dict:
|
|
558
656
|
return {k: v for k, v in kwargs.items() if k != "requirement"}
|
|
559
657
|
|
|
560
658
|
if operator_name == "models_match_any":
|
|
561
659
|
eval_func = _evaluate_models_match_any
|
|
562
|
-
return eval_func(
|
|
660
|
+
return eval_func(
|
|
661
|
+
requirement_links=requirement_links, input_links=input_links, **kwargs
|
|
662
|
+
)
|
|
563
663
|
elif operator_name == "models_match_all":
|
|
564
664
|
eval_func = _evaluate_models_match_all
|
|
565
|
-
return eval_func(
|
|
665
|
+
return eval_func(
|
|
666
|
+
requirement_links=requirement_links, input_links=input_links, **kwargs
|
|
667
|
+
)
|
|
566
668
|
elif operator_name == "models_match_none":
|
|
567
669
|
eval_func = _evaluate_models_match_none
|
|
568
|
-
return eval_func(
|
|
670
|
+
return eval_func(
|
|
671
|
+
requirement_links=requirement_links, input_links=input_links, **kwargs
|
|
672
|
+
)
|
|
569
673
|
elif operator_name == "models_match_any_in_timeframe":
|
|
570
674
|
# 'requirement' is already extracted from kwargs via requirement = kwargs.get("requirement")
|
|
571
|
-
if not isinstance(
|
|
572
|
-
|
|
675
|
+
if not isinstance(
|
|
676
|
+
requirement, Requirement
|
|
677
|
+
): # Ensure requirement is present and correct type
|
|
678
|
+
raise ValueError(
|
|
679
|
+
"models_match_any_in_timeframe operator requires a valid 'requirement' instance in kwargs."
|
|
680
|
+
)
|
|
573
681
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
574
682
|
eval_func = _evaluate_models_match_any_in_timeframe
|
|
575
683
|
return eval_func(
|
|
@@ -580,76 +688,155 @@ def dispatch_operator_evaluation(operator_name: str, requirement_links: "Require
|
|
|
580
688
|
)
|
|
581
689
|
elif operator_name == "models_match_all_in_timeframe":
|
|
582
690
|
if not isinstance(requirement, Requirement):
|
|
583
|
-
raise ValueError(
|
|
691
|
+
raise ValueError(
|
|
692
|
+
"models_match_all_in_timeframe operator requires a valid 'requirement' instance in kwargs."
|
|
693
|
+
)
|
|
584
694
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
585
|
-
return _evaluate_models_match_all_in_timeframe(
|
|
695
|
+
return _evaluate_models_match_all_in_timeframe(
|
|
696
|
+
requirement_links=requirement_links,
|
|
697
|
+
input_links=input_links,
|
|
698
|
+
requirement=requirement,
|
|
699
|
+
**kwargs_for_eval,
|
|
700
|
+
)
|
|
586
701
|
elif operator_name == "models_match_none_in_timeframe":
|
|
587
702
|
if not isinstance(requirement, Requirement):
|
|
588
|
-
raise ValueError(
|
|
703
|
+
raise ValueError(
|
|
704
|
+
"models_match_none_in_timeframe operator requires a valid 'requirement' instance in kwargs."
|
|
705
|
+
)
|
|
589
706
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
590
707
|
return _evaluate_models_match_none_in_timeframe(
|
|
591
|
-
requirement_links=requirement_links,
|
|
708
|
+
requirement_links=requirement_links,
|
|
709
|
+
input_links=input_links,
|
|
710
|
+
requirement=requirement,
|
|
711
|
+
**kwargs_for_eval,
|
|
592
712
|
)
|
|
593
713
|
elif operator_name == "models_match_n":
|
|
594
714
|
if not isinstance(requirement, Requirement):
|
|
595
|
-
raise ValueError(
|
|
715
|
+
raise ValueError(
|
|
716
|
+
"models_match_n operator requires a valid 'requirement' instance in kwargs."
|
|
717
|
+
)
|
|
596
718
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
597
|
-
return _evaluate_models_match_n(
|
|
719
|
+
return _evaluate_models_match_n(
|
|
720
|
+
requirement_links=requirement_links,
|
|
721
|
+
input_links=input_links,
|
|
722
|
+
requirement=requirement,
|
|
723
|
+
**kwargs_for_eval,
|
|
724
|
+
)
|
|
598
725
|
elif operator_name == "models_match_n_or_more":
|
|
599
726
|
if not isinstance(requirement, Requirement):
|
|
600
|
-
raise ValueError(
|
|
727
|
+
raise ValueError(
|
|
728
|
+
"models_match_n_or_more operator requires a valid 'requirement' instance in kwargs."
|
|
729
|
+
)
|
|
601
730
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
602
|
-
return _evaluate_models_match_n_or_more(
|
|
731
|
+
return _evaluate_models_match_n_or_more(
|
|
732
|
+
requirement_links=requirement_links,
|
|
733
|
+
input_links=input_links,
|
|
734
|
+
requirement=requirement,
|
|
735
|
+
**kwargs_for_eval,
|
|
736
|
+
)
|
|
603
737
|
elif operator_name == "models_match_n_or_less":
|
|
604
738
|
if not isinstance(requirement, Requirement):
|
|
605
|
-
raise ValueError(
|
|
739
|
+
raise ValueError(
|
|
740
|
+
"models_match_n_or_less operator requires a valid 'requirement' instance in kwargs."
|
|
741
|
+
)
|
|
606
742
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
607
|
-
return _evaluate_models_match_n_or_less(
|
|
743
|
+
return _evaluate_models_match_n_or_less(
|
|
744
|
+
requirement_links=requirement_links,
|
|
745
|
+
input_links=input_links,
|
|
746
|
+
requirement=requirement,
|
|
747
|
+
**kwargs_for_eval,
|
|
748
|
+
)
|
|
608
749
|
elif operator_name == "models_match_count_in_range":
|
|
609
750
|
if not isinstance(requirement, Requirement):
|
|
610
|
-
raise ValueError(
|
|
751
|
+
raise ValueError(
|
|
752
|
+
"models_match_count_in_range operator requires a valid 'requirement' instance in kwargs."
|
|
753
|
+
)
|
|
611
754
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
612
|
-
return _evaluate_models_match_count_in_range(
|
|
755
|
+
return _evaluate_models_match_count_in_range(
|
|
756
|
+
requirement_links=requirement_links,
|
|
757
|
+
input_links=input_links,
|
|
758
|
+
requirement=requirement,
|
|
759
|
+
**kwargs_for_eval,
|
|
760
|
+
)
|
|
613
761
|
elif operator_name == "models_match_n_in_timeframe":
|
|
614
762
|
if not isinstance(requirement, Requirement):
|
|
615
|
-
raise ValueError(
|
|
763
|
+
raise ValueError(
|
|
764
|
+
"models_match_n_in_timeframe operator requires a valid 'requirement' instance in kwargs."
|
|
765
|
+
)
|
|
616
766
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
617
|
-
return _evaluate_models_match_n_in_timeframe(
|
|
767
|
+
return _evaluate_models_match_n_in_timeframe(
|
|
768
|
+
requirement_links=requirement_links,
|
|
769
|
+
input_links=input_links,
|
|
770
|
+
requirement=requirement,
|
|
771
|
+
**kwargs_for_eval,
|
|
772
|
+
)
|
|
618
773
|
elif operator_name == "models_match_n_or_more_in_timeframe":
|
|
619
774
|
if not isinstance(requirement, Requirement):
|
|
620
|
-
raise ValueError(
|
|
775
|
+
raise ValueError(
|
|
776
|
+
"models_match_n_or_more_in_timeframe operator requires a valid 'requirement' instance in kwargs."
|
|
777
|
+
)
|
|
621
778
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
622
779
|
return _evaluate_models_match_n_or_more_in_timeframe(
|
|
623
|
-
requirement_links=requirement_links,
|
|
780
|
+
requirement_links=requirement_links,
|
|
781
|
+
input_links=input_links,
|
|
782
|
+
requirement=requirement,
|
|
783
|
+
**kwargs_for_eval,
|
|
624
784
|
)
|
|
625
785
|
elif operator_name == "models_match_n_or_less_in_timeframe":
|
|
626
786
|
if not isinstance(requirement, Requirement):
|
|
627
|
-
raise ValueError(
|
|
787
|
+
raise ValueError(
|
|
788
|
+
"models_match_n_or_less_in_timeframe operator requires a valid 'requirement' instance in kwargs."
|
|
789
|
+
)
|
|
628
790
|
kwargs_for_eval = _kwargs_without_requirement()
|
|
629
791
|
return _evaluate_models_match_n_or_less_in_timeframe(
|
|
630
|
-
requirement_links=requirement_links,
|
|
792
|
+
requirement_links=requirement_links,
|
|
793
|
+
input_links=input_links,
|
|
794
|
+
requirement=requirement,
|
|
795
|
+
**kwargs_for_eval,
|
|
631
796
|
)
|
|
632
797
|
elif operator_name in LAB_VALUE_OPERATOR_FUNCTIONS:
|
|
633
|
-
if not isinstance(
|
|
634
|
-
|
|
798
|
+
if not isinstance(
|
|
799
|
+
requirement, Requirement
|
|
800
|
+
): # Ensure requirement is present and correct type
|
|
801
|
+
raise ValueError(
|
|
802
|
+
f"Lab value operator '{operator_name}' requires a valid 'requirement' instance in kwargs."
|
|
803
|
+
)
|
|
635
804
|
|
|
636
805
|
eval_func = LAB_VALUE_OPERATOR_FUNCTIONS[operator_name]
|
|
637
|
-
return eval_func(
|
|
806
|
+
return eval_func(
|
|
807
|
+
input_links=input_links, requirement=requirement, operator_kwargs=kwargs
|
|
808
|
+
)
|
|
638
809
|
elif operator_name == "age_gte":
|
|
639
810
|
if not isinstance(requirement, Requirement):
|
|
640
|
-
raise ValueError(
|
|
811
|
+
raise ValueError(
|
|
812
|
+
"age_gte operator requires a valid 'requirement' instance in kwargs."
|
|
813
|
+
)
|
|
641
814
|
|
|
642
815
|
# Create a new kwargs dict for the call, excluding 'requirement' to avoid passing it twice
|
|
643
816
|
kwargs_for_eval = {k: v for k, v in kwargs.items() if k != "requirement"}
|
|
644
817
|
|
|
645
|
-
return _evaluate_age_gte(
|
|
818
|
+
return _evaluate_age_gte(
|
|
819
|
+
requirement_links=requirement_links,
|
|
820
|
+
input_links=input_links,
|
|
821
|
+
requirement=requirement,
|
|
822
|
+
**kwargs_for_eval,
|
|
823
|
+
)
|
|
646
824
|
elif operator_name == "age_lte":
|
|
647
825
|
if not isinstance(requirement, Requirement):
|
|
648
|
-
raise ValueError(
|
|
826
|
+
raise ValueError(
|
|
827
|
+
"age_lte operator requires a valid 'requirement' instance in kwargs."
|
|
828
|
+
)
|
|
649
829
|
|
|
650
830
|
# Create a new kwargs dict for the call, excluding 'requirement' to avoid passing it twice
|
|
651
831
|
kwargs_for_eval = {k: v for k, v in kwargs.items() if k != "requirement"}
|
|
652
832
|
|
|
653
|
-
return _evaluate_age_lte(
|
|
833
|
+
return _evaluate_age_lte(
|
|
834
|
+
requirement_links=requirement_links,
|
|
835
|
+
input_links=input_links,
|
|
836
|
+
requirement=requirement,
|
|
837
|
+
**kwargs_for_eval,
|
|
838
|
+
)
|
|
654
839
|
else:
|
|
655
|
-
raise NotImplementedError(
|
|
840
|
+
raise NotImplementedError(
|
|
841
|
+
f"Evaluation logic for operator '{operator_name}' is not implemented."
|
|
842
|
+
)
|
|
@@ -1,11 +1,14 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Any,
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Tuple, Union
|
|
2
2
|
|
|
3
3
|
if TYPE_CHECKING:
|
|
4
4
|
from endoreg_db.models import Patient, PatientExamination, Requirement
|
|
5
|
-
from endoreg_db.models.requirement.requirement_operator import OperatorInstructions
|
|
5
|
+
from endoreg_db.models.requirement.requirement_operator import OperatorInstructions
|
|
6
6
|
|
|
7
7
|
|
|
8
|
-
def fetch_input_target(
|
|
8
|
+
def fetch_input_target(
|
|
9
|
+
input_object: Union["Patient", "PatientExamination"],
|
|
10
|
+
operator_instructions: "OperatorInstructions",
|
|
11
|
+
) -> Tuple[str, Any]:
|
|
9
12
|
input_target_names = operator_instructions.input_targets
|
|
10
13
|
|
|
11
14
|
# Iterate over targets and stop with first successful fetch
|
|
@@ -20,7 +23,9 @@ def fetch_input_target(input_object: Union["Patient", "PatientExamination"], ope
|
|
|
20
23
|
except AttributeError:
|
|
21
24
|
continue # Try next input target
|
|
22
25
|
|
|
23
|
-
raise AttributeError(
|
|
26
|
+
raise AttributeError(
|
|
27
|
+
f"None of the input targets {input_target_names} could be resolved on the input object."
|
|
28
|
+
)
|
|
24
29
|
|
|
25
30
|
|
|
26
31
|
def fetch_requirement_targets(
|
|
@@ -35,7 +40,9 @@ def fetch_requirement_targets(
|
|
|
35
40
|
target_value = getattr(requirement, target_name)
|
|
36
41
|
target_values[target_name] = target_value
|
|
37
42
|
except AttributeError:
|
|
38
|
-
raise AttributeError(
|
|
43
|
+
raise AttributeError(
|
|
44
|
+
f"Requirement does not have attribute '{target_name}'."
|
|
45
|
+
)
|
|
39
46
|
target_values[target_name] = target_value
|
|
40
47
|
|
|
41
48
|
return target_values
|
|
@@ -46,7 +53,9 @@ def model_attribute_set_any(
|
|
|
46
53
|
operator_instructions: "OperatorInstructions",
|
|
47
54
|
requirement: "Requirement",
|
|
48
55
|
) -> bool:
|
|
49
|
-
input_target_name, input_value = fetch_input_target(
|
|
56
|
+
input_target_name, input_value = fetch_input_target(
|
|
57
|
+
input_object, operator_instructions
|
|
58
|
+
)
|
|
50
59
|
|
|
51
60
|
if not input_value:
|
|
52
61
|
return False
|
|
@@ -59,18 +68,24 @@ def model_attribute_numeric_in_range(
|
|
|
59
68
|
operator_instructions: "OperatorInstructions",
|
|
60
69
|
requirement: "Requirement",
|
|
61
70
|
) -> bool:
|
|
62
|
-
input_target_name, input_value = fetch_input_target(
|
|
71
|
+
input_target_name, input_value = fetch_input_target(
|
|
72
|
+
input_object, operator_instructions
|
|
73
|
+
)
|
|
63
74
|
|
|
64
75
|
# make sure, input_value is numeric
|
|
65
76
|
try:
|
|
66
77
|
numeric_value = float(input_value)
|
|
67
78
|
except (TypeError, ValueError):
|
|
68
|
-
raise ValueError(
|
|
79
|
+
raise ValueError(
|
|
80
|
+
f"Input value for target '{input_target_name}' is not numeric: {input_value}"
|
|
81
|
+
)
|
|
69
82
|
|
|
70
83
|
_min = requirement.numeric_value_min
|
|
71
84
|
_max = requirement.numeric_value_max
|
|
72
85
|
|
|
73
|
-
assert _min is not None and _max is not None,
|
|
86
|
+
assert _min is not None and _max is not None, (
|
|
87
|
+
"Numeric range requires both min and max values to be set."
|
|
88
|
+
)
|
|
74
89
|
|
|
75
90
|
return_value = _min <= numeric_value <= _max
|
|
76
91
|
return return_value
|
|
@@ -81,7 +96,9 @@ def model_attribute_is_among_values(
|
|
|
81
96
|
operator_instructions: "OperatorInstructions",
|
|
82
97
|
requirement: "Requirement",
|
|
83
98
|
) -> bool:
|
|
84
|
-
input_target_name, input_value = fetch_input_target(
|
|
99
|
+
input_target_name, input_value = fetch_input_target(
|
|
100
|
+
input_object, operator_instructions
|
|
101
|
+
)
|
|
85
102
|
target_values = fetch_requirement_targets(requirement, operator_instructions)
|
|
86
103
|
|
|
87
104
|
for target_name, target_values in target_values.items():
|