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
|
@@ -3,7 +3,6 @@ from subprocess import run
|
|
|
3
3
|
from typing import TYPE_CHECKING, Dict, List, Tuple, Union, cast
|
|
4
4
|
|
|
5
5
|
from django.db import models
|
|
6
|
-
from pydantic import BaseModel
|
|
7
6
|
|
|
8
7
|
from endoreg_db.utils.links.requirement_link import RequirementLinks
|
|
9
8
|
|
|
@@ -124,9 +123,13 @@ class Requirement(models.Model):
|
|
|
124
123
|
|
|
125
124
|
@property
|
|
126
125
|
def operator_instructions_parsed(self):
|
|
127
|
-
from endoreg_db.models.requirement.requirement_operator import
|
|
126
|
+
from endoreg_db.models.requirement.requirement_operator import (
|
|
127
|
+
RequirementOperator,
|
|
128
|
+
)
|
|
128
129
|
|
|
129
|
-
instructions = RequirementOperator.parse_instructions(
|
|
130
|
+
instructions = RequirementOperator.parse_instructions(
|
|
131
|
+
self.operator_instructions
|
|
132
|
+
)
|
|
130
133
|
return instructions
|
|
131
134
|
|
|
132
135
|
numeric_value = models.FloatField(
|
|
@@ -268,7 +271,9 @@ class Requirement(models.Model):
|
|
|
268
271
|
)
|
|
269
272
|
|
|
270
273
|
if TYPE_CHECKING:
|
|
271
|
-
requirement_types = cast(
|
|
274
|
+
requirement_types = cast(
|
|
275
|
+
models.manager.RelatedManager["RequirementType"], requirement_types
|
|
276
|
+
)
|
|
272
277
|
operator = models.ForeignKey["RequirementOperator"]
|
|
273
278
|
# requirement_sets = cast(models.manager.RelatedManager["RequirementSet"], requirement_sets)
|
|
274
279
|
examinations = cast(models.manager.RelatedManager["Examination"], examinations)
|
|
@@ -292,7 +297,9 @@ class Requirement(models.Model):
|
|
|
292
297
|
models.manager.RelatedManager["FindingClassificationChoice"],
|
|
293
298
|
finding_classification_choices,
|
|
294
299
|
)
|
|
295
|
-
finding_interventions = cast(
|
|
300
|
+
finding_interventions = cast(
|
|
301
|
+
models.manager.RelatedManager["FindingIntervention"], finding_interventions
|
|
302
|
+
)
|
|
296
303
|
medications = cast(models.manager.RelatedManager["Medication"], medications)
|
|
297
304
|
medication_indications = cast(
|
|
298
305
|
models.manager.RelatedManager["MedicationIndication"],
|
|
@@ -302,7 +309,9 @@ class Requirement(models.Model):
|
|
|
302
309
|
models.manager.RelatedManager["MedicationIntakeTime"],
|
|
303
310
|
medication_intake_times,
|
|
304
311
|
)
|
|
305
|
-
medication_schedules = cast(
|
|
312
|
+
medication_schedules = cast(
|
|
313
|
+
models.manager.RelatedManager["MedicationSchedule"], medication_schedules
|
|
314
|
+
)
|
|
306
315
|
genders = cast(models.manager.RelatedManager["Gender"], genders)
|
|
307
316
|
|
|
308
317
|
def natural_key(self):
|
|
@@ -370,18 +379,32 @@ class Requirement(models.Model):
|
|
|
370
379
|
# requirement_sets is not part of RequirementLinks (avoids circular import); collect other related models
|
|
371
380
|
models_dict = RequirementLinks(
|
|
372
381
|
examinations=[_ for _ in self.examinations.all() if _ is not None],
|
|
373
|
-
examination_indications=[
|
|
382
|
+
examination_indications=[
|
|
383
|
+
_ for _ in self.examination_indications.all() if _ is not None
|
|
384
|
+
],
|
|
374
385
|
lab_values=[_ for _ in self.lab_values.all() if _ is not None],
|
|
375
386
|
diseases=[_ for _ in self.diseases.all() if _ is not None],
|
|
376
|
-
disease_classification_choices=[
|
|
387
|
+
disease_classification_choices=[
|
|
388
|
+
_ for _ in self.disease_classification_choices.all() if _ is not None
|
|
389
|
+
],
|
|
377
390
|
events=[_ for _ in self.events.all() if _ is not None],
|
|
378
391
|
findings=[_ for _ in self.findings.all() if _ is not None],
|
|
379
|
-
finding_classifications=[
|
|
380
|
-
|
|
381
|
-
|
|
392
|
+
finding_classifications=[
|
|
393
|
+
_ for _ in self.finding_classifications.all() if _ is not None
|
|
394
|
+
],
|
|
395
|
+
finding_classification_choices=[
|
|
396
|
+
_ for _ in self.finding_classification_choices.all() if _ is not None
|
|
397
|
+
],
|
|
398
|
+
finding_interventions=[
|
|
399
|
+
_ for _ in self.finding_interventions.all() if _ is not None
|
|
400
|
+
],
|
|
382
401
|
medications=[_ for _ in self.medications.all() if _ is not None],
|
|
383
|
-
medication_indications=[
|
|
384
|
-
|
|
402
|
+
medication_indications=[
|
|
403
|
+
_ for _ in self.medication_indications.all() if _ is not None
|
|
404
|
+
],
|
|
405
|
+
medication_intake_times=[
|
|
406
|
+
_ for _ in self.medication_intake_times.all() if _ is not None
|
|
407
|
+
],
|
|
385
408
|
)
|
|
386
409
|
return models_dict
|
|
387
410
|
|
|
@@ -457,7 +480,9 @@ class Requirement(models.Model):
|
|
|
457
480
|
- bei ungültigem Modus
|
|
458
481
|
- bei komplett falschen Input-Typen / fehlender .links-Struktur
|
|
459
482
|
"""
|
|
460
|
-
from endoreg_db.models.requirement.requirement_error import
|
|
483
|
+
from endoreg_db.models.requirement.requirement_error import (
|
|
484
|
+
RequirementEvaluationError,
|
|
485
|
+
)
|
|
461
486
|
|
|
462
487
|
# --- Mode validieren -------------------------------------------------
|
|
463
488
|
if mode not in ["strict", "loose"]:
|
|
@@ -520,7 +545,11 @@ class Requirement(models.Model):
|
|
|
520
545
|
"Für diese Voraussetzung müssen alle passenden Einträge vorliegen, aber es wurden keine entsprechenden Datensätze gefunden.",
|
|
521
546
|
)
|
|
522
547
|
if queryset_mode == "min_count":
|
|
523
|
-
required =
|
|
548
|
+
required = (
|
|
549
|
+
queryset_min_count
|
|
550
|
+
if queryset_min_count is not None
|
|
551
|
+
else 1
|
|
552
|
+
)
|
|
524
553
|
if required > 0:
|
|
525
554
|
return (
|
|
526
555
|
False,
|
|
@@ -534,7 +563,9 @@ class Requirement(models.Model):
|
|
|
534
563
|
queryset_item_count = 0
|
|
535
564
|
|
|
536
565
|
for item in _input:
|
|
537
|
-
if not hasattr(item, "links") or not isinstance(
|
|
566
|
+
if not hasattr(item, "links") or not isinstance(
|
|
567
|
+
item.links, RequirementLinks
|
|
568
|
+
):
|
|
538
569
|
raise RequirementEvaluationError(
|
|
539
570
|
requirement=self,
|
|
540
571
|
code="MISSING_LINKS_ATTR",
|
|
@@ -557,7 +588,9 @@ class Requirement(models.Model):
|
|
|
557
588
|
aggregated_input_links_data[link_key] = []
|
|
558
589
|
aggregated_input_links_data[link_key].extend(link_list)
|
|
559
590
|
|
|
560
|
-
per_item_args = tuple(
|
|
591
|
+
per_item_args = tuple(
|
|
592
|
+
item if arg is _input else arg for arg in args
|
|
593
|
+
)
|
|
561
594
|
op_kwargs = kwargs.copy()
|
|
562
595
|
op_kwargs["requirement"] = self
|
|
563
596
|
op_kwargs["original_input_args"] = per_item_args
|
|
@@ -580,7 +613,11 @@ class Requirement(models.Model):
|
|
|
580
613
|
exc,
|
|
581
614
|
)
|
|
582
615
|
item_operator_results.append(False)
|
|
583
|
-
item_result =
|
|
616
|
+
item_result = (
|
|
617
|
+
evaluate_result_list_func(item_operator_results)
|
|
618
|
+
if item_operator_results
|
|
619
|
+
else True
|
|
620
|
+
)
|
|
584
621
|
else:
|
|
585
622
|
# keine Operatoren -> Bedingung erfüllt, wenn Requirement selbst keine Bedingungen hat
|
|
586
623
|
item_result = not requirement_has_conditions
|
|
@@ -599,7 +636,9 @@ class Requirement(models.Model):
|
|
|
599
636
|
"Für diese Voraussetzung müssen alle relevanten Einträge die Bedingung erfüllen.",
|
|
600
637
|
)
|
|
601
638
|
elif queryset_mode == "min_count":
|
|
602
|
-
required =
|
|
639
|
+
required = (
|
|
640
|
+
queryset_min_count if queryset_min_count is not None else 1
|
|
641
|
+
)
|
|
603
642
|
if queryset_true_count < max(required, 0):
|
|
604
643
|
return (
|
|
605
644
|
False,
|
|
@@ -612,18 +651,28 @@ class Requirement(models.Model):
|
|
|
612
651
|
raise RequirementEvaluationError(
|
|
613
652
|
requirement=self,
|
|
614
653
|
code="INVALID_INPUT_TYPE",
|
|
615
|
-
technical_message=(
|
|
616
|
-
|
|
654
|
+
technical_message=(
|
|
655
|
+
f"Input type {type(_input)} is not among expected models: {self.expected_models} nor a QuerySet of expected models."
|
|
656
|
+
),
|
|
657
|
+
user_message=(
|
|
658
|
+
"Diese Voraussetzung wurde mit einem nicht passenden Datentyp aufgerufen und kann aktuell nicht korrekt geprüft werden."
|
|
659
|
+
),
|
|
617
660
|
meta={"input_type": str(type(_input))},
|
|
618
661
|
)
|
|
619
662
|
|
|
620
663
|
# Einzelinstanz erwarteten Typs
|
|
621
|
-
if not hasattr(_input, "links") or not isinstance(
|
|
664
|
+
if not hasattr(_input, "links") or not isinstance(
|
|
665
|
+
_input.links, RequirementLinks
|
|
666
|
+
):
|
|
622
667
|
raise RequirementEvaluationError(
|
|
623
668
|
requirement=self,
|
|
624
669
|
code="MISSING_LINKS_ATTR",
|
|
625
|
-
technical_message=(
|
|
626
|
-
|
|
670
|
+
technical_message=(
|
|
671
|
+
f"Input {_input} of type {type(_input)} does not have a valid .links attribute of type RequirementLinks."
|
|
672
|
+
),
|
|
673
|
+
user_message=(
|
|
674
|
+
"Für die Auswertung dieser Voraussetzung fehlen die intern benötigten Verknüpfungsinformationen."
|
|
675
|
+
),
|
|
627
676
|
meta={"input_type": str(type(_input))},
|
|
628
677
|
)
|
|
629
678
|
|
|
@@ -640,7 +689,9 @@ class Requirement(models.Model):
|
|
|
640
689
|
# Deduplizieren der aggregierten Links
|
|
641
690
|
for key in aggregated_input_links_data:
|
|
642
691
|
try:
|
|
643
|
-
aggregated_input_links_data[key] = list(
|
|
692
|
+
aggregated_input_links_data[key] = list(
|
|
693
|
+
dict.fromkeys(aggregated_input_links_data[key])
|
|
694
|
+
)
|
|
644
695
|
except TypeError:
|
|
645
696
|
# Fallback für nicht-hashbare Items
|
|
646
697
|
tmp: list = []
|
|
@@ -699,7 +750,9 @@ class Requirement(models.Model):
|
|
|
699
750
|
**op_kwargs,
|
|
700
751
|
)
|
|
701
752
|
operator_results.append(operator_result)
|
|
702
|
-
operator_details.append(
|
|
753
|
+
operator_details.append(
|
|
754
|
+
f"{operator.name}: {'erfüllt' if operator_result else 'nicht erfüllt'}"
|
|
755
|
+
)
|
|
703
756
|
except Exception as e:
|
|
704
757
|
operator_results.append(False)
|
|
705
758
|
operator_details.append(f"{operator.name}: technischer Fehler ({e})")
|
|
@@ -718,7 +771,11 @@ class Requirement(models.Model):
|
|
|
718
771
|
elif len(operator_results) == 1:
|
|
719
772
|
details = operator_details[0]
|
|
720
773
|
else:
|
|
721
|
-
failed_details = [
|
|
774
|
+
failed_details = [
|
|
775
|
+
detail
|
|
776
|
+
for detail, result in zip(operator_details, operator_results)
|
|
777
|
+
if not result
|
|
778
|
+
]
|
|
722
779
|
if failed_details:
|
|
723
780
|
details = "; ".join(failed_details)
|
|
724
781
|
else:
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from typing import Optional, Any, Mapping, TYPE_CHECKING
|
|
3
|
+
|
|
3
4
|
if TYPE_CHECKING:
|
|
4
5
|
from endoreg_db.models.requirement.requirement import Requirement
|
|
5
6
|
|
|
@@ -14,13 +15,13 @@ class RequirementEvaluationErrorContext:
|
|
|
14
15
|
- user_message: optional pre-formatted text for UI
|
|
15
16
|
- meta: optional arbitrary payload for logging / debugging
|
|
16
17
|
"""
|
|
18
|
+
|
|
17
19
|
requirement: "Requirement"
|
|
18
20
|
code: str
|
|
19
21
|
technical_message: str
|
|
20
22
|
user_message: Optional[str] = None
|
|
21
23
|
description: Optional[str] = None
|
|
22
24
|
meta: Optional[Mapping[str, Any]] = None
|
|
23
|
-
|
|
24
25
|
|
|
25
26
|
|
|
26
27
|
class RequirementEvaluationError(Exception):
|
|
@@ -36,7 +37,7 @@ class RequirementEvaluationError(Exception):
|
|
|
36
37
|
|
|
37
38
|
def __init__(
|
|
38
39
|
self,
|
|
39
|
-
requirement: "Requirement",
|
|
40
|
+
requirement: "Requirement",
|
|
40
41
|
code: str,
|
|
41
42
|
technical_message: str,
|
|
42
43
|
user_message: Optional[str] = None,
|
|
@@ -52,18 +53,16 @@ class RequirementEvaluationError(Exception):
|
|
|
52
53
|
)
|
|
53
54
|
# Base Exception message = technical message
|
|
54
55
|
super().__init__(technical_message, *args)
|
|
55
|
-
|
|
56
|
-
|
|
57
56
|
|
|
58
57
|
@property
|
|
59
|
-
def requirement(self) ->
|
|
58
|
+
def requirement(self) -> "Requirement":
|
|
60
59
|
return self.context.requirement
|
|
61
60
|
|
|
62
61
|
@property
|
|
63
62
|
def requirement_name(self) -> str:
|
|
64
63
|
# This is the DB name, *not* the internal Python repr
|
|
65
64
|
return getattr(self.context.requirement, "name", "unknown")
|
|
66
|
-
|
|
65
|
+
|
|
67
66
|
@property
|
|
68
67
|
def requirement_description(self) -> str:
|
|
69
68
|
return getattr(self.context.requirement, "description")
|
|
@@ -8,6 +8,7 @@ logger = logging.getLogger(__name__)
|
|
|
8
8
|
|
|
9
9
|
RequirementStatus = Literal["PASSED", "FAILED", "BLOCKED", "ERROR"]
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
def topologically_sort_requirement_sets(
|
|
12
13
|
sets: Iterable[RequirementSet],
|
|
13
14
|
) -> List[RequirementSet]:
|
|
@@ -44,8 +45,6 @@ def topologically_sort_requirement_sets(
|
|
|
44
45
|
return [id_map[sid] for sid in ordered_ids]
|
|
45
46
|
|
|
46
47
|
|
|
47
|
-
|
|
48
|
-
|
|
49
48
|
def evaluate_requirement_sets_with_dependencies(
|
|
50
49
|
sets: Iterable[RequirementSet],
|
|
51
50
|
*args,
|
|
@@ -145,9 +144,7 @@ def evaluate_requirement_sets_with_dependencies(
|
|
|
145
144
|
req_set.id,
|
|
146
145
|
req.id,
|
|
147
146
|
)
|
|
148
|
-
msg =
|
|
149
|
-
f"Technischer Fehler bei der Auswertung von '{req.name}': {exc}"
|
|
150
|
-
)
|
|
147
|
+
msg = f"Technischer Fehler bei der Auswertung von '{req.name}': {exc}"
|
|
151
148
|
set_results[set_id][req.id] = ("ERROR", msg)
|
|
152
149
|
|
|
153
150
|
# derive set status
|
|
@@ -161,7 +158,9 @@ def evaluate_requirement_sets_with_dependencies(
|
|
|
161
158
|
|
|
162
159
|
return set_results
|
|
163
160
|
|
|
164
|
-
|
|
161
|
+
|
|
162
|
+
# TODO Remove when sure that no per requirement evaluation will happen. Needs the depends on attribute for the reatuirements.
|
|
163
|
+
|
|
165
164
|
|
|
166
165
|
def topologically_sort_requirements(
|
|
167
166
|
requirements: Iterable[Requirement],
|
|
@@ -200,6 +199,7 @@ def topologically_sort_requirements(
|
|
|
200
199
|
|
|
201
200
|
return [id_map[rid] for rid in ordered_ids]
|
|
202
201
|
|
|
202
|
+
|
|
203
203
|
def evaluate_requirements_with_dependencies(
|
|
204
204
|
requirements: Iterable[Requirement],
|
|
205
205
|
*args,
|
|
@@ -252,7 +252,7 @@ def evaluate_requirements_with_dependencies(
|
|
|
252
252
|
met = False
|
|
253
253
|
results[req.id] = (
|
|
254
254
|
"FAILED",
|
|
255
|
-
f"Nachtragebedarf bei der Auswertung von '{req.name}': {exc}"
|
|
255
|
+
f"Nachtragebedarf bei der Auswertung von '{req.name}': {exc}",
|
|
256
256
|
)
|
|
257
257
|
except Exception as exc:
|
|
258
258
|
met = False
|
|
@@ -265,4 +265,4 @@ def evaluate_requirements_with_dependencies(
|
|
|
265
265
|
status: RequirementStatus = "PASSED" if met else "FAILED"
|
|
266
266
|
results[req.id] = (status, details)
|
|
267
267
|
|
|
268
|
-
return results
|
|
268
|
+
return results
|
|
@@ -8,6 +8,7 @@ if TYPE_CHECKING:
|
|
|
8
8
|
Requirement,
|
|
9
9
|
)
|
|
10
10
|
|
|
11
|
+
|
|
11
12
|
def get_values_from_kwargs(
|
|
12
13
|
requirement: "Requirement",
|
|
13
14
|
patient: Optional["Patient"] = None,
|
|
@@ -16,9 +17,9 @@ def get_values_from_kwargs(
|
|
|
16
17
|
) -> dict:
|
|
17
18
|
"""
|
|
18
19
|
Aggregates and validates input values required for evaluating a requirement.
|
|
19
|
-
|
|
20
|
+
|
|
20
21
|
Checks the requirement's types to ensure that necessary parameters such as `patient` or `patient_examination` are provided when required. Raises a ValueError if a required parameter is missing. Returns a dictionary containing the validated inputs and any additional keyword arguments.
|
|
21
|
-
|
|
22
|
+
|
|
22
23
|
Returns:
|
|
23
24
|
A dictionary with keys for 'patient', 'patient_examination', and any extra keyword arguments.
|
|
24
25
|
"""
|
|
@@ -37,4 +38,3 @@ def get_values_from_kwargs(
|
|
|
37
38
|
|
|
38
39
|
# Prepare and return the extracted values
|
|
39
40
|
return {"patient": patient, "patient_examination": patient_examination, **kwargs}
|
|
40
|
-
|
|
@@ -58,7 +58,9 @@ class DataModelEntry(BaseModel):
|
|
|
58
58
|
@field_validator("model")
|
|
59
59
|
@classmethod
|
|
60
60
|
def ensure_model_subclass(cls, value: type[models.Model]) -> type[models.Model]:
|
|
61
|
-
if not issubclass(
|
|
61
|
+
if not issubclass(
|
|
62
|
+
value, models.Model
|
|
63
|
+
): # Defensive: ensure provided class is a Django model
|
|
62
64
|
raise TypeError(f"{value!r} is not a Django model class")
|
|
63
65
|
return value
|
|
64
66
|
|
|
@@ -90,32 +92,46 @@ class DataModelRegistry(BaseModel):
|
|
|
90
92
|
DATA_MODEL_REGISTRY = DataModelRegistry(
|
|
91
93
|
entries=(
|
|
92
94
|
DataModelEntry(name="disease", model=Disease),
|
|
93
|
-
DataModelEntry(
|
|
95
|
+
DataModelEntry(
|
|
96
|
+
name="disease_classification_choice", model=DiseaseClassificationChoice
|
|
97
|
+
),
|
|
94
98
|
DataModelEntry(name="disease_classification", model=DiseaseClassification),
|
|
95
99
|
DataModelEntry(name="event", model=Event),
|
|
96
100
|
DataModelEntry(name="event_classification", model=EventClassification),
|
|
97
|
-
DataModelEntry(
|
|
101
|
+
DataModelEntry(
|
|
102
|
+
name="event_classification_choice", model=EventClassificationChoice
|
|
103
|
+
),
|
|
98
104
|
DataModelEntry(name="examination", model=Examination),
|
|
99
105
|
DataModelEntry(name="examination_indication", model=ExaminationIndication),
|
|
100
106
|
DataModelEntry(name="finding", model=Finding),
|
|
101
107
|
DataModelEntry(name="finding_intervention", model=FindingIntervention),
|
|
102
108
|
DataModelEntry(name="finding_classification", model=FindingClassification),
|
|
103
|
-
DataModelEntry(
|
|
109
|
+
DataModelEntry(
|
|
110
|
+
name="finding_classification_choice", model=FindingClassificationChoice
|
|
111
|
+
),
|
|
104
112
|
DataModelEntry(name="lab_value", model=LabValue),
|
|
105
113
|
DataModelEntry(name="patient_disease", model=PatientDisease),
|
|
106
114
|
DataModelEntry(name="patient_event", model=PatientEvent),
|
|
107
115
|
DataModelEntry(name="patient_examination", model=PatientExamination),
|
|
108
116
|
DataModelEntry(name="patient_finding", model=PatientFinding),
|
|
109
|
-
DataModelEntry(
|
|
110
|
-
|
|
117
|
+
DataModelEntry(
|
|
118
|
+
name="patient_finding_intervention", model=PatientFindingIntervention
|
|
119
|
+
),
|
|
120
|
+
DataModelEntry(
|
|
121
|
+
name="patient_finding_classification", model=PatientFindingClassification
|
|
122
|
+
),
|
|
111
123
|
DataModelEntry(name="patient_lab_value", model=PatientLabValue),
|
|
112
124
|
DataModelEntry(name="patient_lab_sample", model=PatientLabSample),
|
|
113
125
|
DataModelEntry(name="patient", model=Patient),
|
|
114
126
|
DataModelEntry(name="patient_medication", model=PatientMedication),
|
|
115
|
-
DataModelEntry(
|
|
127
|
+
DataModelEntry(
|
|
128
|
+
name="patient_medication_schedule", model=PatientMedicationSchedule
|
|
129
|
+
),
|
|
116
130
|
DataModelEntry(name="gender", model=Gender),
|
|
117
131
|
),
|
|
118
132
|
)
|
|
119
133
|
|
|
120
134
|
data_model_dict: Dict[str, type[models.Model]] = DATA_MODEL_REGISTRY.as_mapping()
|
|
121
|
-
data_model_dict_reverse: Dict[type[models.Model], str] =
|
|
135
|
+
data_model_dict_reverse: Dict[type[models.Model], str] = (
|
|
136
|
+
DATA_MODEL_REGISTRY.as_reverse_mapping()
|
|
137
|
+
)
|
|
@@ -4,7 +4,9 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, List, Union
|
|
|
4
4
|
from django.db import models
|
|
5
5
|
from pydantic import BaseModel
|
|
6
6
|
|
|
7
|
-
from endoreg_db.utils.requirement_operator_logic.new_operator_logic import
|
|
7
|
+
from endoreg_db.utils.requirement_operator_logic.new_operator_logic import (
|
|
8
|
+
REQUIREMENT_OPERATORS,
|
|
9
|
+
)
|
|
8
10
|
|
|
9
11
|
# see how operator evaluation function is fetched, add to docs #TODO
|
|
10
12
|
# endoreg_db/utils/requirement_operator_logic/model_evaluators.py
|
|
@@ -55,12 +57,16 @@ def _parse_operator_instructions(raw: str):
|
|
|
55
57
|
key, value = _attribute.split(":", 1)
|
|
56
58
|
kwargs[key.strip()] = value.strip()
|
|
57
59
|
else:
|
|
58
|
-
raise ValueError(
|
|
60
|
+
raise ValueError(
|
|
61
|
+
f"Invalid keyword argument format in target_attributes: '{entry}'. Expected format is '$key:value'."
|
|
62
|
+
)
|
|
59
63
|
elif _prefix == "@":
|
|
60
64
|
# Positional argument
|
|
61
65
|
args.append(_attribute)
|
|
62
66
|
else:
|
|
63
|
-
raise ValueError(
|
|
67
|
+
raise ValueError(
|
|
68
|
+
f"Invalid prefix '{_prefix}' in target_attributes entry: '{entry}'. Valid prefixes are {valid_prefixes}."
|
|
69
|
+
)
|
|
64
70
|
return OperatorInstructions(
|
|
65
71
|
input_targets=input_targets,
|
|
66
72
|
requirement_targets=input_targets,
|
|
@@ -72,7 +78,9 @@ def _parse_operator_instructions(raw: str):
|
|
|
72
78
|
def _validate_operator_instructions(instance: "Requirement") -> bool:
|
|
73
79
|
"""Ensures requirement fixtures declare at least one target attribute."""
|
|
74
80
|
if not instance.operator_instructions_parsed:
|
|
75
|
-
raise ValueError(
|
|
81
|
+
raise ValueError(
|
|
82
|
+
f"Requirement '{instance.name}' must declare at least one target attribute."
|
|
83
|
+
)
|
|
76
84
|
return True
|
|
77
85
|
|
|
78
86
|
|
|
@@ -101,7 +109,9 @@ class RequirementOperator(models.Model):
|
|
|
101
109
|
|
|
102
110
|
name = models.CharField(max_length=100, unique=True)
|
|
103
111
|
description = models.TextField(blank=True, null=True)
|
|
104
|
-
evaluation_function_name = models.CharField(
|
|
112
|
+
evaluation_function_name = models.CharField(
|
|
113
|
+
max_length=255, blank=True, null=True
|
|
114
|
+
) # Added field
|
|
105
115
|
|
|
106
116
|
objects = RequirementOperatorManager()
|
|
107
117
|
|
|
@@ -151,10 +161,18 @@ class RequirementOperator(models.Model):
|
|
|
151
161
|
def get_operator_function(self) -> Callable[..., bool]:
|
|
152
162
|
_operator_function = REQUIREMENT_OPERATORS.get(self.name)
|
|
153
163
|
if not _operator_function:
|
|
154
|
-
raise ValueError(
|
|
164
|
+
raise ValueError(
|
|
165
|
+
f"Operator function for '{self.name}' not found in REQUIREMENT_OPERATORS."
|
|
166
|
+
)
|
|
155
167
|
return _operator_function
|
|
156
168
|
|
|
157
|
-
def evaluate(
|
|
169
|
+
def evaluate(
|
|
170
|
+
self,
|
|
171
|
+
operator_instructions: "OperatorInstructions",
|
|
172
|
+
requirement: "Requirement",
|
|
173
|
+
input_obj: Any,
|
|
174
|
+
**kwargs,
|
|
175
|
+
) -> bool:
|
|
158
176
|
""" """
|
|
159
177
|
eval_result: bool = False
|
|
160
178
|
requirement_links: "RequirementLinks" = requirement.links
|
|
@@ -162,6 +180,8 @@ class RequirementOperator(models.Model):
|
|
|
162
180
|
operator_function = self.get_operator_function()
|
|
163
181
|
|
|
164
182
|
input_model = type(input_obj)
|
|
165
|
-
assert input_model in expected_input_models,
|
|
183
|
+
assert input_model in expected_input_models, (
|
|
184
|
+
f"Input model {input_model} not in expected models {expected_input_models}"
|
|
185
|
+
)
|
|
166
186
|
|
|
167
187
|
return eval_result
|
|
@@ -83,7 +83,7 @@ class RequirementSet(models.Model):
|
|
|
83
83
|
|
|
84
84
|
name = models.CharField(max_length=100, unique=True)
|
|
85
85
|
description = models.TextField(blank=True, null=True)
|
|
86
|
-
|
|
86
|
+
|
|
87
87
|
depends_on = models.ManyToManyField(
|
|
88
88
|
"self",
|
|
89
89
|
symmetrical=False,
|
|
@@ -94,7 +94,7 @@ class RequirementSet(models.Model):
|
|
|
94
94
|
"bevor dieses Set geprüft wird ('after')."
|
|
95
95
|
),
|
|
96
96
|
)
|
|
97
|
-
|
|
97
|
+
|
|
98
98
|
requirements = models.ManyToManyField(
|
|
99
99
|
"Requirement",
|
|
100
100
|
blank=True,
|
|
@@ -135,17 +135,31 @@ class RequirementSet(models.Model):
|
|
|
135
135
|
if TYPE_CHECKING:
|
|
136
136
|
from typing import Optional, cast
|
|
137
137
|
|
|
138
|
-
from endoreg_db.models import
|
|
138
|
+
from endoreg_db.models import (
|
|
139
|
+
ExaminationRequirementSet,
|
|
140
|
+
InformationSource,
|
|
141
|
+
Requirement,
|
|
142
|
+
Tag,
|
|
143
|
+
)
|
|
139
144
|
|
|
140
145
|
tags = cast(models.manager.RelatedManager["Tag"], tags)
|
|
141
146
|
requirements = cast(models.manager.RelatedManager["Requirement"], requirements)
|
|
142
|
-
links_to_sets = cast(
|
|
143
|
-
|
|
144
|
-
|
|
147
|
+
links_to_sets = cast(
|
|
148
|
+
models.manager.RelatedManager["RequirementSet"], links_to_sets
|
|
149
|
+
)
|
|
150
|
+
reqset_exam_links = cast(
|
|
151
|
+
models.manager.RelatedManager["ExaminationRequirementSet"],
|
|
152
|
+
reqset_exam_links,
|
|
153
|
+
)
|
|
154
|
+
information_sources = cast(
|
|
155
|
+
models.manager.RelatedManager["InformationSource"], information_sources
|
|
156
|
+
)
|
|
145
157
|
requirement_set_type: models.ForeignKey["RequirementSetType | None"]
|
|
146
158
|
|
|
147
159
|
@property
|
|
148
|
-
def links_from_sets(
|
|
160
|
+
def links_from_sets(
|
|
161
|
+
self,
|
|
162
|
+
) -> "models.manager.RelatedManager[RequirementSet]": ...
|
|
149
163
|
|
|
150
164
|
def natural_key(self):
|
|
151
165
|
"""Return the natural key as a tuple containing the instance's name."""
|
|
@@ -175,7 +189,9 @@ class RequirementSet(models.Model):
|
|
|
175
189
|
results = []
|
|
176
190
|
for requirement in self.requirements.all():
|
|
177
191
|
# Get the appropriate input for this specific requirement
|
|
178
|
-
evaluation_input = self._get_evaluation_input_for_requirement(
|
|
192
|
+
evaluation_input = self._get_evaluation_input_for_requirement(
|
|
193
|
+
requirement, input_object
|
|
194
|
+
)
|
|
179
195
|
result = requirement.evaluate(evaluation_input, mode=mode)
|
|
180
196
|
results.append(result)
|
|
181
197
|
return results
|
|
@@ -199,7 +215,9 @@ class RequirementSet(models.Model):
|
|
|
199
215
|
return input_object
|
|
200
216
|
|
|
201
217
|
# Import here to avoid circular imports
|
|
202
|
-
from endoreg_db.models.medical.patient.patient_examination import
|
|
218
|
+
from endoreg_db.models.medical.patient.patient_examination import (
|
|
219
|
+
PatientExamination,
|
|
220
|
+
)
|
|
203
221
|
from endoreg_db.models.medical.patient.patient_finding import PatientFinding
|
|
204
222
|
|
|
205
223
|
# Handle PatientExamination -> PatientFinding conversion
|
|
@@ -239,7 +257,10 @@ class RequirementSet(models.Model):
|
|
|
239
257
|
|
|
240
258
|
If the requirement set type is defined and matches a known type, returns the corresponding function from REQUIREMENT_SET_TYPE_FUNCTION_LOOKUP. Returns None if no matching function is found.
|
|
241
259
|
"""
|
|
242
|
-
if
|
|
260
|
+
if (
|
|
261
|
+
self.requirement_set_type
|
|
262
|
+
and self.requirement_set_type.name in REQUIREMENT_SET_TYPE_FUNCTION_LOOKUP
|
|
263
|
+
):
|
|
243
264
|
return REQUIREMENT_SET_TYPE_FUNCTION_LOOKUP[self.requirement_set_type.name]
|
|
244
265
|
return None
|
|
245
266
|
|
|
@@ -260,7 +281,9 @@ class RequirementSet(models.Model):
|
|
|
260
281
|
|
|
261
282
|
results = evaluate_r_results + evaluate_rs_results
|
|
262
283
|
|
|
263
|
-
eval_result =
|
|
284
|
+
eval_result = (
|
|
285
|
+
self.eval_function(results) if self.eval_function else all(results)
|
|
286
|
+
)
|
|
264
287
|
|
|
265
288
|
return eval_result
|
|
266
289
|
|
|
@@ -127,7 +127,12 @@ class AuditLedger(models.Model):
|
|
|
127
127
|
Returns:
|
|
128
128
|
The count of unique object primary keys matching the specified type and action.
|
|
129
129
|
"""
|
|
130
|
-
return
|
|
130
|
+
return (
|
|
131
|
+
AuditLedger.objects.filter(object_type=object_type, action=action)
|
|
132
|
+
.values("object_pk")
|
|
133
|
+
.distinct()
|
|
134
|
+
.count()
|
|
135
|
+
)
|
|
131
136
|
|
|
132
137
|
def collect_counters(self):
|
|
133
138
|
"""
|
|
@@ -140,7 +145,9 @@ class AuditLedger(models.Model):
|
|
|
140
145
|
return {
|
|
141
146
|
"totalCases": AuditLedger._distinct("VideoFile", "created"),
|
|
142
147
|
"totalVideos": AuditLedger._distinct("VideoFile", "created"),
|
|
143
|
-
"totalAnnotations": AuditLedger.objects.filter(
|
|
148
|
+
"totalAnnotations": AuditLedger.objects.filter(
|
|
149
|
+
action="annotation_added"
|
|
150
|
+
).count(),
|
|
144
151
|
"totalAnonymizations": AuditLedger._distinct("VideoFile", "anonymized"),
|
|
145
152
|
"totalImages": AuditLedger._distinct("Image", "created"),
|
|
146
153
|
"videosCompleted": AuditLedger._distinct("VideoFile", "validated"),
|