endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.9__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/authz/auth.py +74 -0
- endoreg_db/authz/backends.py +168 -0
- endoreg_db/authz/management/commands/list_routes.py +18 -0
- endoreg_db/authz/middleware.py +83 -0
- endoreg_db/authz/permissions.py +127 -0
- endoreg_db/authz/policy.py +218 -0
- endoreg_db/authz/views_auth.py +66 -0
- endoreg_db/config/env.py +13 -8
- endoreg_db/data/__init__.py +2 -11
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +3 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/examination/examinations/data.yaml +114 -14
- endoreg_db/data/examination/time-type/data.yaml +0 -3
- endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
- endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
- endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
- endoreg_db/data/finding/00_generic.yaml +35 -0
- endoreg_db/data/finding/00_generic_complication.yaml +9 -0
- endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
- endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
- endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
- endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
- endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
- endoreg_db/data/finding_classification/00_generic.yaml +44 -0
- endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
- endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
- endoreg_db/data/finding_classification/02_colonoscopy_baseline.yaml +83 -0
- endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
- endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
- endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
- endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
- endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
- endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
- endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
- endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
- endoreg_db/data/finding_classification_choice/{colonoscopy_not_complete_reason.yaml → 02_colonoscopy_generic.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{colonoscopy_location.yaml → 02_colonoscopy_location.yaml} +23 -4
- endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
- endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
- endoreg_db/data/finding_classification_choice/{colon_lesion_paris.yaml → 02_colonoscopy_polyp_morphology.yaml} +26 -8
- endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
- endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
- endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
- endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
- endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
- endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
- endoreg_db/data/finding_type/data.yaml +8 -12
- endoreg_db/data/requirement/01_patient_data.yaml +93 -0
- endoreg_db/data/requirement/old/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/old/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +29 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/{endoscopy_bleeding_risk.yaml → 02_endoscopy_bleeding_risk.yaml} +0 -6
- endoreg_db/data/requirement_set/90_coloreg.yaml +190 -0
- endoreg_db/data/requirement_set/_old_ +109 -0
- endoreg_db/data/requirement_set_type/data.yaml +21 -0
- endoreg_db/data/setup_config.yaml +4 -4
- endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
- endoreg_db/exceptions.py +4 -2
- endoreg_db/forms/examination_form.py +1 -1
- endoreg_db/helpers/data_loader.py +125 -53
- endoreg_db/helpers/default_objects.py +116 -81
- endoreg_db/import_files/__init__.py +27 -0
- endoreg_db/import_files/context/__init__.py +7 -0
- endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
- endoreg_db/import_files/context/ensure_center.py +17 -0
- endoreg_db/import_files/context/file_lock.py +66 -0
- endoreg_db/import_files/context/import_context.py +43 -0
- endoreg_db/import_files/context/validate_directories.py +56 -0
- endoreg_db/import_files/file_storage/__init__.py +15 -0
- endoreg_db/import_files/file_storage/create_report_file.py +76 -0
- endoreg_db/import_files/file_storage/create_video_file.py +75 -0
- endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
- endoreg_db/import_files/file_storage/state_management.py +400 -0
- endoreg_db/import_files/file_storage/storage.py +36 -0
- endoreg_db/import_files/import_service.md +26 -0
- endoreg_db/import_files/processing/__init__.py +11 -0
- endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
- endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
- endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
- endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
- endoreg_db/import_files/pseudonymization/fake.py +52 -0
- endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
- endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
- endoreg_db/import_files/report_import_service.py +141 -0
- endoreg_db/import_files/video_import_service.py +150 -0
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
- endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
- endoreg_db/management/commands/import_report.py +130 -65
- endoreg_db/management/commands/import_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +2 -2
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_ai_model_data.py +5 -5
- endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
- endoreg_db/management/commands/load_base_db_data.py +5 -134
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_contraindication_data.py +14 -16
- endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
- endoreg_db/management/commands/load_disease_classification_data.py +15 -18
- endoreg_db/management/commands/load_disease_data.py +25 -28
- endoreg_db/management/commands/load_endoscope_data.py +20 -27
- endoreg_db/management/commands/load_event_data.py +14 -16
- endoreg_db/management/commands/load_examination_data.py +31 -44
- endoreg_db/management/commands/load_examination_indication_data.py +20 -21
- endoreg_db/management/commands/load_finding_data.py +52 -80
- endoreg_db/management/commands/load_information_source.py +21 -23
- endoreg_db/management/commands/load_lab_value_data.py +17 -26
- endoreg_db/management/commands/load_medication_data.py +13 -12
- endoreg_db/management/commands/load_organ_data.py +15 -19
- endoreg_db/management/commands/load_pdf_type_data.py +19 -18
- endoreg_db/management/commands/load_profession_data.py +14 -17
- endoreg_db/management/commands/load_qualification_data.py +20 -23
- endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
- endoreg_db/management/commands/load_requirement_data.py +62 -39
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/load_risk_data.py +7 -6
- endoreg_db/management/commands/load_shift_data.py +20 -23
- endoreg_db/management/commands/load_tag_data.py +8 -11
- endoreg_db/management/commands/load_unit_data.py +17 -19
- endoreg_db/management/commands/setup_endoreg_db.py +3 -3
- endoreg_db/management/commands/start_filewatcher.py +46 -37
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/management/commands/validate_video_files.py +1 -5
- endoreg_db/migrations/0001_initial.py +297 -250
- endoreg_db/models/__init__.py +78 -123
- endoreg_db/models/administration/__init__.py +21 -42
- endoreg_db/models/administration/ai/active_model.py +2 -2
- endoreg_db/models/administration/ai/ai_model.py +7 -6
- endoreg_db/models/administration/case/__init__.py +1 -15
- endoreg_db/models/administration/case/case.py +3 -3
- endoreg_db/models/administration/case/case_template/__init__.py +2 -14
- endoreg_db/models/administration/case/case_template/case_template.py +2 -124
- endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
- endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
- endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
- endoreg_db/models/administration/center/center.py +33 -19
- endoreg_db/models/administration/center/center_product.py +12 -9
- endoreg_db/models/administration/center/center_resource.py +25 -19
- endoreg_db/models/administration/center/center_shift.py +21 -17
- endoreg_db/models/administration/center/center_waste.py +16 -8
- endoreg_db/models/administration/person/__init__.py +2 -0
- endoreg_db/models/administration/person/employee/employee.py +10 -5
- endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
- endoreg_db/models/administration/person/employee/employee_type.py +12 -6
- endoreg_db/models/administration/person/examiner/examiner.py +13 -11
- endoreg_db/models/administration/person/patient/__init__.py +2 -0
- endoreg_db/models/administration/person/patient/patient.py +129 -100
- endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
- endoreg_db/models/administration/person/person.py +4 -0
- endoreg_db/models/administration/person/profession/__init__.py +8 -4
- endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
- endoreg_db/models/administration/product/product.py +20 -15
- endoreg_db/models/administration/product/product_material.py +17 -18
- endoreg_db/models/administration/product/product_weight.py +12 -8
- endoreg_db/models/administration/product/reference_product.py +23 -55
- endoreg_db/models/administration/qualification/qualification.py +7 -3
- endoreg_db/models/administration/qualification/qualification_type.py +7 -3
- endoreg_db/models/administration/shift/scheduled_days.py +8 -5
- endoreg_db/models/administration/shift/shift.py +16 -12
- endoreg_db/models/administration/shift/shift_type.py +23 -31
- endoreg_db/models/label/__init__.py +8 -9
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +23 -28
- endoreg_db/models/label/label.py +15 -15
- endoreg_db/models/label/label_set.py +19 -6
- endoreg_db/models/label/label_type.py +1 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
- endoreg_db/models/label/label_video_segment/label_video_segment.py +98 -102
- endoreg_db/models/label/video_segmentation_label.py +4 -0
- endoreg_db/models/label/video_segmentation_labelset.py +4 -3
- endoreg_db/models/media/frame/frame.py +22 -22
- endoreg_db/models/media/pdf/raw_pdf.py +194 -194
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +55 -47
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/processing_history/__init__.py +5 -0
- endoreg_db/models/media/processing_history/processing_history.py +96 -0
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +139 -77
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +174 -112
- endoreg_db/models/media/video/video_file_ai.py +288 -74
- endoreg_db/models/media/video/video_file_anonymize.py +38 -38
- endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
- endoreg_db/models/media/video/video_file_io.py +113 -61
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
- endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
- endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
- endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
- endoreg_db/models/media/video/video_file_segments.py +24 -17
- endoreg_db/models/media/video/video_metadata.py +19 -35
- endoreg_db/models/media/video/video_processing.py +96 -95
- endoreg_db/models/medical/contraindication/README.md +1 -0
- endoreg_db/models/medical/contraindication/__init__.py +13 -3
- endoreg_db/models/medical/disease.py +22 -16
- endoreg_db/models/medical/event.py +31 -18
- endoreg_db/models/medical/examination/__init__.py +13 -6
- endoreg_db/models/medical/examination/examination.py +39 -20
- endoreg_db/models/medical/examination/examination_indication.py +30 -95
- endoreg_db/models/medical/examination/examination_time.py +23 -8
- endoreg_db/models/medical/examination/examination_time_type.py +9 -6
- endoreg_db/models/medical/examination/examination_type.py +3 -4
- endoreg_db/models/medical/finding/finding.py +32 -40
- endoreg_db/models/medical/finding/finding_classification.py +42 -72
- endoreg_db/models/medical/finding/finding_intervention.py +25 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +26 -26
- endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
- endoreg_db/models/medical/laboratory/lab_value.py +62 -91
- endoreg_db/models/medical/medication/medication.py +22 -10
- endoreg_db/models/medical/medication/medication_indication.py +29 -3
- endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
- endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
- endoreg_db/models/medical/medication/medication_schedule.py +27 -16
- endoreg_db/models/medical/organ/__init__.py +15 -12
- endoreg_db/models/medical/patient/medication_examples.py +6 -6
- endoreg_db/models/medical/patient/patient_disease.py +20 -23
- endoreg_db/models/medical/patient/patient_event.py +19 -22
- endoreg_db/models/medical/patient/patient_examination.py +48 -54
- endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
- endoreg_db/models/medical/patient/patient_finding.py +122 -139
- endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
- endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
- endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
- endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
- endoreg_db/models/medical/patient/patient_medication.py +27 -38
- endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
- endoreg_db/models/medical/risk/risk.py +7 -6
- endoreg_db/models/medical/risk/risk_type.py +8 -5
- endoreg_db/models/metadata/model_meta.py +60 -29
- endoreg_db/models/metadata/model_meta_logic.py +125 -18
- endoreg_db/models/metadata/pdf_meta.py +31 -24
- endoreg_db/models/metadata/sensitive_meta.py +105 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +198 -103
- endoreg_db/models/metadata/video_meta.py +51 -31
- endoreg_db/models/metadata/video_prediction_logic.py +16 -23
- endoreg_db/models/metadata/video_prediction_meta.py +29 -33
- endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
- endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
- endoreg_db/models/other/emission/emission_factor.py +18 -8
- endoreg_db/models/other/gender.py +10 -5
- endoreg_db/models/other/information_source.py +50 -29
- endoreg_db/models/other/material.py +9 -5
- endoreg_db/models/other/resource.py +6 -4
- endoreg_db/models/other/tag.py +10 -5
- endoreg_db/models/other/transport_route.py +13 -8
- endoreg_db/models/other/unit.py +10 -6
- endoreg_db/models/other/waste.py +6 -5
- endoreg_db/models/report/report.py +6 -0
- endoreg_db/models/requirement/requirement.py +329 -361
- endoreg_db/models/requirement/requirement_error.py +85 -0
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
- endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
- endoreg_db/models/requirement/requirement_operator.py +103 -112
- endoreg_db/models/requirement/requirement_set.py +74 -57
- endoreg_db/models/state/__init__.py +4 -4
- endoreg_db/models/state/abstract.py +2 -2
- endoreg_db/models/state/anonymization.py +12 -0
- endoreg_db/models/state/audit_ledger.py +49 -51
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +101 -68
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +110 -90
- endoreg_db/models/upload_job.py +35 -34
- endoreg_db/models/utils.py +28 -25
- endoreg_db/queries/__init__.py +3 -1
- endoreg_db/root_urls.py +21 -2
- endoreg_db/schemas/examination_evaluation.py +1 -1
- endoreg_db/serializers/__init__.py +2 -10
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
- endoreg_db/serializers/meta/__init__.py +1 -6
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/file_overview.py +11 -99
- endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
- endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
- endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_file_list.py +65 -34
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/services/__old/pdf_import.py +1487 -0
- endoreg_db/services/__old/video_import.py +1306 -0
- endoreg_db/services/anonymization.py +128 -89
- endoreg_db/services/lookup_service.py +65 -52
- endoreg_db/services/lookup_store.py +2 -2
- endoreg_db/services/pdf_import.py +0 -1382
- endoreg_db/services/report_import.py +10 -0
- endoreg_db/services/video_import.py +6 -1255
- endoreg_db/tasks/upload_tasks.py +79 -70
- endoreg_db/tasks/video_ingest.py +8 -4
- endoreg_db/urls/__init__.py +5 -32
- endoreg_db/urls/ai.py +32 -0
- endoreg_db/urls/media.py +121 -83
- endoreg_db/urls/root_urls.py +29 -0
- endoreg_db/utils/__init__.py +15 -5
- endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
- endoreg_db/utils/case_generator/__init__.py +3 -0
- endoreg_db/utils/dataloader.py +142 -40
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/paths.py +110 -46
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/Readme.md +1 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +655 -0
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
- endoreg_db/utils/setup_config.py +8 -5
- endoreg_db/utils/storage.py +115 -0
- endoreg_db/utils/validate_endo_roi.py +8 -2
- endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
- endoreg_db/views/__init__.py +85 -183
- endoreg_db/views/ai/__init__.py +8 -0
- endoreg_db/views/ai/label.py +155 -0
- endoreg_db/views/anonymization/media_management.py +202 -166
- endoreg_db/views/anonymization/overview.py +99 -67
- endoreg_db/views/anonymization/validate.py +182 -44
- endoreg_db/views/media/__init__.py +7 -20
- endoreg_db/views/media/pdf_media.py +197 -174
- endoreg_db/views/media/sensitive_metadata.py +193 -138
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/meta/__init__.py +0 -8
- endoreg_db/views/misc/__init__.py +1 -7
- endoreg_db/views/misc/upload_views.py +94 -93
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/report/__init__.py +5 -7
- endoreg_db/views/{pdf → report}/reimport.py +22 -22
- endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +46 -39
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +17 -3
- endoreg_db/views/requirement/lookup_store.py +22 -90
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +23 -24
- endoreg_db/views/video/correction.py +201 -172
- endoreg_db/views/video/reimport.py +1 -1
- endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +77 -40
- endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
- endoreg_db/views/video/video_stream.py +7 -8
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +391 -413
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
- endoreg_db/data/finding/anatomy_colon.yaml +0 -128
- endoreg_db/data/finding/colonoscopy.yaml +0 -40
- endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
- endoreg_db/data/finding/complication.yaml +0 -16
- endoreg_db/data/finding/data.yaml +0 -105
- endoreg_db/data/finding/examination_setting.yaml +0 -16
- endoreg_db/data/finding/medication_related.yaml +0 -18
- endoreg_db/data/finding/outcome.yaml +0 -12
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +0 -95
- endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
- endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
- endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -68
- endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -80
- endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
- endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
- endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
- endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
- endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
- endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
- endoreg_db/data/finding_classification/histology_colo.yaml +0 -51
- endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
- endoreg_db/data/finding_classification/medication_related.yaml +0 -23
- endoreg_db/data/finding_classification/visualized.yaml +0 -33
- endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
- endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
- endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
- endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
- endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
- endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
- endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
- endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
- endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
- endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
- endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
- endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
- endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
- endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
- endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
- endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
- endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
- endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
- endoreg_db/data/requirement/age.yaml +0 -26
- endoreg_db/data/requirement/gender.yaml +0 -25
- endoreg_db/management/commands/init_default_ai_model.py +0 -112
- endoreg_db/management/commands/reset_celery_schedule.py +0 -9
- endoreg_db/management/commands/validate_video.py +0 -204
- endoreg_db/migrations/0002_add_video_correction_models.py +0 -52
- endoreg_db/migrations/0003_add_center_display_name.py +0 -30
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/rule/__init__.py +0 -13
- endoreg_db/models/rule/rule.py +0 -27
- endoreg_db/models/rule/rule_applicator.py +0 -224
- endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
- endoreg_db/models/rule/rule_type.py +0 -20
- endoreg_db/models/rule/ruleset.py +0 -17
- endoreg_db/renames.yml +0 -8
- endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
- endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
- endoreg_db/serializers/_old/video.py +0 -71
- endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
- endoreg_db/serializers/meta/report_meta.py +0 -53
- endoreg_db/serializers/report/__init__.py +0 -9
- endoreg_db/serializers/report/mixins.py +0 -45
- endoreg_db/serializers/report/report.py +0 -105
- endoreg_db/serializers/report/report_list.py +0 -22
- endoreg_db/serializers/report/secure_file_url.py +0 -26
- endoreg_db/serializers/video/video_metadata.py +0 -105
- endoreg_db/services/requirements_object.py +0 -147
- endoreg_db/services/storage_aware_video_processor.py +0 -344
- endoreg_db/urls/files.py +0 -6
- endoreg_db/urls/label_video_segment_validate.py +0 -33
- endoreg_db/urls/label_video_segments.py +0 -46
- endoreg_db/urls/report.py +0 -48
- endoreg_db/urls/video.py +0 -61
- endoreg_db/utils/case_generator/case_generator.py +0 -159
- endoreg_db/utils/case_generator/utils.py +0 -30
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +0 -368
- endoreg_db/views/label/__init__.py +0 -5
- endoreg_db/views/label/label.py +0 -15
- endoreg_db/views/label_video_segment/__init__.py +0 -16
- endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
- endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
- endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
- endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
- endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
- endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
- endoreg_db/views/label_video_segment/validate.py +0 -226
- endoreg_db/views/media/segments.py +0 -71
- endoreg_db/views/meta/available_files_list.py +0 -146
- endoreg_db/views/meta/report_meta.py +0 -53
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -148
- endoreg_db/views/misc/secure_file_serving_view.py +0 -80
- endoreg_db/views/misc/secure_file_url_view.py +0 -84
- endoreg_db/views/misc/secure_url_validate.py +0 -79
- endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
- endoreg_db/views/patient_finding_location/__init__.py +0 -5
- endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
- endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
- endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
- endoreg_db/views/pdf/__init__.py +0 -8
- endoreg_db/views/report/report_list.py +0 -112
- endoreg_db/views/report/report_with_secure_url.py +0 -28
- endoreg_db/views/report/start_examination.py +0 -7
- endoreg_db/views/video/segmentation.py +0 -274
- endoreg_db/views/video/task_status.py +0 -49
- endoreg_db/views/video/timeline.py +0 -46
- endoreg_db/views/video/video_analyze.py +0 -52
- endoreg_db/views.py +0 -0
- /endoreg_db/data/requirement/{colonoscopy_baseline_austria.yaml → old/colonoscopy_baseline_austria.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_cardiovascular.yaml → old/disease_cardiovascular.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_classification_choice_cardiovascular.yaml → old/disease_classification_choice_cardiovascular.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_hepatology.yaml → old/disease_hepatology.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_misc.yaml → old/disease_misc.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_renal.yaml → old/disease_renal.yaml} +0 -0
- /endoreg_db/data/requirement/{endoscopy_bleeding_risk.yaml → old/endoscopy_bleeding_risk.yaml} +0 -0
- /endoreg_db/data/requirement/{event_cardiology.yaml → old/event_cardiology.yaml} +0 -0
- /endoreg_db/data/requirement/{event_requirements.yaml → old/event_requirements.yaml} +0 -0
- /endoreg_db/data/requirement/{finding_colon_polyp.yaml → old/finding_colon_polyp.yaml} +0 -0
- /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
- /endoreg_db/data/requirement/{lab_value.yaml → old/lab_value.yaml} +0 -0
- /endoreg_db/data/requirement/{medication.yaml → old/medication.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{age.yaml → _old/age.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{lab_operators.yaml → _old/lab_operators.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{model_operators.yaml → _old/model_operators.yaml} +0 -0
- /endoreg_db/{models/media/video/refactor_plan.md → import_files/pseudonymization/__init__.py} +0 -0
- /endoreg_db/{models/media/video/video_file_frames.py → import_files/pseudonymization/pseudonymize.py} +0 -0
- /endoreg_db/models/{metadata/frame_ocr_result.py → report/__init__.py} +0 -0
- /endoreg_db/{urls/sensitive_meta.py → models/report/images.py} +0 -0
- /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -3,7 +3,10 @@ import os
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
5
|
from django.http import FileResponse, Http404, StreamingHttpResponse
|
|
6
|
-
from django.views.decorators.clickjacking import
|
|
6
|
+
from django.views.decorators.clickjacking import (
|
|
7
|
+
xframe_options_exempt,
|
|
8
|
+
xframe_options_sameorigin,
|
|
9
|
+
)
|
|
7
10
|
from rest_framework.views import APIView
|
|
8
11
|
|
|
9
12
|
from endoreg_db.models import RawPdfFile
|
|
@@ -36,34 +39,34 @@ class ClosingFileWrapper:
|
|
|
36
39
|
self.file_handle.close()
|
|
37
40
|
|
|
38
41
|
|
|
39
|
-
class
|
|
42
|
+
class ReportStreamView(APIView):
|
|
40
43
|
"""
|
|
41
|
-
Streams a
|
|
44
|
+
Streams a report file with correct HTTP range support and proper file handle management.
|
|
42
45
|
|
|
43
|
-
Supports streaming both raw (original) and
|
|
46
|
+
Supports streaming both raw (original) and processed report files.
|
|
44
47
|
|
|
45
48
|
Query Parameters:
|
|
46
|
-
type: 'raw' (default) or '
|
|
49
|
+
type: 'raw' (default) or 'processed' - Selects which report file to stream
|
|
47
50
|
|
|
48
51
|
Examples:
|
|
49
|
-
GET /api/media/pdf/1/?type=raw - Stream original raw
|
|
50
|
-
GET /api/media/pdf/1/?type=
|
|
52
|
+
GET /api/media/pdf/1/?type=raw - Stream original raw report
|
|
53
|
+
GET /api/media/pdf/1/?type=processed - Stream processed report
|
|
51
54
|
"""
|
|
52
55
|
|
|
53
56
|
permission_classes = [EnvironmentAwarePermission]
|
|
54
57
|
|
|
55
|
-
@
|
|
56
|
-
def get(self, request,
|
|
58
|
+
@xframe_options_exempt
|
|
59
|
+
def get(self, request, pk: int, *args, **kwargs):
|
|
57
60
|
file_type = "raw" # Initialize for error logging
|
|
58
61
|
try:
|
|
59
|
-
pdf_obj = RawPdfFile.objects.filter(pk=
|
|
62
|
+
pdf_obj = RawPdfFile.objects.filter(pk=pk).first()
|
|
60
63
|
if not pdf_obj:
|
|
61
|
-
logger.warning(f"
|
|
62
|
-
raise Http404("
|
|
64
|
+
logger.warning(f"report not found: ID {pk}")
|
|
65
|
+
raise Http404("report not found")
|
|
63
66
|
|
|
64
67
|
# Parse query parameters to determine which file to stream
|
|
65
68
|
file_type = request.query_params.get("type", "raw").lower()
|
|
66
|
-
if file_type not in ["raw", "
|
|
69
|
+
if file_type not in ["raw", "processed"]:
|
|
67
70
|
logger.warning(f"Invalid file_type '{file_type}', defaulting to 'raw'")
|
|
68
71
|
file_type = "raw"
|
|
69
72
|
|
|
@@ -71,43 +74,45 @@ class PdfStreamView(APIView):
|
|
|
71
74
|
if file_type == "raw":
|
|
72
75
|
file_field = pdf_obj.file
|
|
73
76
|
if not file_field:
|
|
74
|
-
logger.warning(f"No raw
|
|
75
|
-
raise Http404("Raw
|
|
77
|
+
logger.warning(f"No raw report file available for report ID {pk}")
|
|
78
|
+
raise Http404("Raw report file not available")
|
|
76
79
|
else: # anonymized
|
|
77
|
-
file_field = pdf_obj.
|
|
80
|
+
file_field = pdf_obj.processed_file
|
|
78
81
|
if not file_field:
|
|
79
82
|
logger.warning(
|
|
80
|
-
f"No
|
|
83
|
+
f"No processed report file available for report ID {pk}"
|
|
81
84
|
)
|
|
82
|
-
raise Http404("
|
|
85
|
+
raise Http404("Processed report file not available")
|
|
83
86
|
|
|
84
87
|
# Check if file exists on filesystem
|
|
85
88
|
try:
|
|
86
89
|
file_path = file_field.path
|
|
87
90
|
if not os.path.exists(file_path):
|
|
88
|
-
logger.error(
|
|
91
|
+
logger.error(
|
|
92
|
+
f"report file does not exist on filesystem: {file_path}"
|
|
93
|
+
)
|
|
89
94
|
raise Http404(
|
|
90
|
-
f"{file_type.capitalize()}
|
|
95
|
+
f"{file_type.capitalize()} report file not found on filesystem"
|
|
91
96
|
)
|
|
92
97
|
|
|
93
98
|
file_size = os.path.getsize(file_path)
|
|
94
99
|
except (OSError, IOError, AttributeError) as e:
|
|
95
|
-
logger.error(f"Error accessing {file_type}
|
|
96
|
-
raise Http404(f"{file_type.capitalize()}
|
|
100
|
+
logger.error(f"Error accessing {file_type} report file {pk}: {e}")
|
|
101
|
+
raise Http404(f"{file_type.capitalize()} report file not accessible")
|
|
97
102
|
|
|
98
103
|
# Generate safe filename
|
|
99
104
|
base_filename = (
|
|
100
105
|
os.path.basename(file_field.name)
|
|
101
106
|
if file_field.name
|
|
102
|
-
else f"document_{
|
|
107
|
+
else f"document_{pk}.pdf"
|
|
103
108
|
)
|
|
104
109
|
if not base_filename.endswith(".pdf"):
|
|
105
110
|
base_filename += ".pdf"
|
|
106
111
|
|
|
107
112
|
# Add type indicator to filename for clarity
|
|
108
|
-
if file_type == "
|
|
113
|
+
if file_type == "processed":
|
|
109
114
|
name_parts = base_filename.rsplit(".", 1)
|
|
110
|
-
safe_filename = f"{name_parts[0]}
|
|
115
|
+
safe_filename = f"{name_parts[0]}_processed.{name_parts[1]}"
|
|
111
116
|
else:
|
|
112
117
|
safe_filename = base_filename
|
|
113
118
|
|
|
@@ -115,7 +120,7 @@ class PdfStreamView(APIView):
|
|
|
115
120
|
range_header = request.headers.get("Range")
|
|
116
121
|
if range_header:
|
|
117
122
|
logger.debug(
|
|
118
|
-
f"Range request for {file_type}
|
|
123
|
+
f"Range request for {file_type} report {pk}: {range_header}"
|
|
119
124
|
)
|
|
120
125
|
match = _RANGE_RE.match(range_header)
|
|
121
126
|
if match:
|
|
@@ -139,7 +144,7 @@ class PdfStreamView(APIView):
|
|
|
139
144
|
file_handle.seek(start)
|
|
140
145
|
|
|
141
146
|
logger.debug(
|
|
142
|
-
f"Serving {file_type}
|
|
147
|
+
f"Serving {file_type} report {pk} range {start}-{end}/{file_size}"
|
|
143
148
|
)
|
|
144
149
|
|
|
145
150
|
response = StreamingHttpResponse(
|
|
@@ -157,31 +162,33 @@ class PdfStreamView(APIView):
|
|
|
157
162
|
return response
|
|
158
163
|
except (OSError, IOError) as e:
|
|
159
164
|
logger.error(
|
|
160
|
-
f"Error opening {file_type}
|
|
165
|
+
f"Error opening {file_type} report file for range request: {e}"
|
|
161
166
|
)
|
|
162
|
-
raise Http404(f"Error accessing {file_type}
|
|
167
|
+
raise Http404(f"Error accessing {file_type} report file")
|
|
163
168
|
else:
|
|
164
169
|
logger.warning(f"Invalid Range header format: {range_header}")
|
|
165
170
|
|
|
166
171
|
# Serve entire file using FileResponse (automatically handles file closing)
|
|
167
|
-
logger.debug(f"Serving full {file_type}
|
|
172
|
+
logger.debug(f"Serving full {file_type} report {pk} ({file_size} bytes)")
|
|
168
173
|
|
|
169
174
|
try:
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
+
with open(file_path, "rb") as file_handle:
|
|
176
|
+
response = FileResponse(file_handle, content_type="application/pdf")
|
|
177
|
+
response["Content-Length"] = str(file_size)
|
|
178
|
+
response["Accept-Ranges"] = "bytes"
|
|
179
|
+
response["Content-Disposition"] = (
|
|
180
|
+
f'inline; filename="{safe_filename}"'
|
|
181
|
+
)
|
|
175
182
|
|
|
176
183
|
# FileResponse will take ownership of file_handle and close it after response
|
|
177
184
|
return response
|
|
178
185
|
except (OSError, IOError) as e:
|
|
179
|
-
logger.error(f"Error opening {file_type}
|
|
180
|
-
raise Http404(f"Error accessing {file_type}
|
|
186
|
+
logger.error(f"Error opening {file_type} report file: {e}")
|
|
187
|
+
raise Http404(f"Error accessing {file_type} report file")
|
|
181
188
|
|
|
182
189
|
except Exception as e:
|
|
183
190
|
logger.error(
|
|
184
|
-
f"Unexpected error streaming {file_type if 'file_type' in locals() else '
|
|
191
|
+
f"Unexpected error streaming {file_type if 'file_type' in locals() else 'report'} {pk}: {e}",
|
|
185
192
|
exc_info=True,
|
|
186
193
|
)
|
|
187
|
-
raise Http404("Error streaming
|
|
194
|
+
raise Http404("Error streaming report")
|
|
@@ -1,52 +1,31 @@
|
|
|
1
1
|
from endoreg_db.models.requirement.requirement import Requirement
|
|
2
|
+
from endoreg_db.views.requirement.requirement_utils import safe_evaluate_requirement
|
|
2
3
|
from endoreg_db.models.requirement.requirement_set import RequirementSet
|
|
4
|
+
from endoreg_db.models.requirement.requirement_evaluation.evaluate_with_dependencies import (
|
|
5
|
+
evaluate_requirement_sets_with_dependencies,
|
|
6
|
+
RequirementStatus, # if you export it there, otherwise re-declare in view
|
|
7
|
+
)
|
|
3
8
|
from endoreg_db.models.medical.patient.patient_examination import PatientExamination
|
|
4
|
-
|
|
5
|
-
|
|
6
9
|
from rest_framework import status
|
|
7
10
|
from rest_framework.decorators import api_view
|
|
8
11
|
from rest_framework.response import Response
|
|
12
|
+
import json
|
|
9
13
|
import logging
|
|
10
14
|
|
|
11
15
|
logger = logging.getLogger(__name__)
|
|
12
|
-
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
@api_view(["POST"])
|
|
13
19
|
def evaluate_requirements(request):
|
|
14
20
|
"""
|
|
15
|
-
Evaluate requirements and always return 200 with structured results.
|
|
16
|
-
Payload:
|
|
17
|
-
{
|
|
18
|
-
"requirement_set_ids": [<int>, ...], // optional; evaluates all if omitted
|
|
19
|
-
"patient_examination_id": <int> // required
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
Response (HTTP 200 always):
|
|
23
|
-
{
|
|
24
|
-
"ok": <bool>, // false if any errors occurred
|
|
25
|
-
"errors": [<str>, ...], // high-level problems (e.g. invalid input, missing PE)
|
|
26
|
-
"meta": {
|
|
27
|
-
"patientExaminationId": <int|null>,
|
|
28
|
-
"setsEvaluated": <int>,
|
|
29
|
-
"requirementsEvaluated": <int>,
|
|
30
|
-
"status": "ok" | "partial" | "failed"
|
|
31
|
-
},
|
|
32
|
-
"results": [
|
|
33
|
-
{
|
|
34
|
-
"requirement_set_id": <int>,
|
|
35
|
-
"requirement_set_name": <str>,
|
|
36
|
-
"requirement_name": <str>,
|
|
37
|
-
"met": <bool>,
|
|
38
|
-
"details": <str>, // always a string for UI display
|
|
39
|
-
"error": <str|null> // per-item error if evaluation failed
|
|
40
|
-
}
|
|
41
|
-
]
|
|
42
|
-
}
|
|
21
|
+
Evaluate requirements (all selected sets) and always return 200 with structured results.
|
|
43
22
|
"""
|
|
44
23
|
payload = request.data or {}
|
|
45
24
|
req_set_ids = payload.get("requirement_set_ids")
|
|
46
25
|
pe_id = payload.get("patient_examination_id")
|
|
47
26
|
|
|
48
|
-
results = []
|
|
49
|
-
errors = []
|
|
27
|
+
results: list[dict] = []
|
|
28
|
+
errors: list[str] = []
|
|
50
29
|
sets_evaluated = 0
|
|
51
30
|
requirements_evaluated = 0
|
|
52
31
|
|
|
@@ -55,53 +34,63 @@ def evaluate_requirements(request):
|
|
|
55
34
|
msg = "patient_examination_id is required"
|
|
56
35
|
errors.append(msg)
|
|
57
36
|
logger.warning("evaluate_requirements: %s; payload=%s", msg, payload)
|
|
58
|
-
return Response(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
"
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
37
|
+
return Response(
|
|
38
|
+
{
|
|
39
|
+
"ok": False,
|
|
40
|
+
"errors": errors,
|
|
41
|
+
"meta": {
|
|
42
|
+
"patientExaminationId": None,
|
|
43
|
+
"setsEvaluated": 0,
|
|
44
|
+
"requirementsEvaluated": 0,
|
|
45
|
+
"status": "failed",
|
|
46
|
+
},
|
|
47
|
+
"results": [],
|
|
66
48
|
},
|
|
67
|
-
|
|
68
|
-
|
|
49
|
+
status=status.HTTP_200_OK,
|
|
50
|
+
)
|
|
69
51
|
|
|
70
52
|
# ---- fetch PatientExamination
|
|
71
53
|
try:
|
|
72
|
-
pe = (
|
|
73
|
-
|
|
74
|
-
|
|
54
|
+
pe = (
|
|
55
|
+
PatientExamination.objects.select_related("patient")
|
|
56
|
+
.get(id=pe_id)
|
|
57
|
+
)
|
|
75
58
|
except PatientExamination.DoesNotExist:
|
|
76
59
|
msg = f"PatientExamination with id {pe_id} does not exist"
|
|
77
60
|
errors.append(msg)
|
|
78
61
|
logger.warning("evaluate_requirements: %s", msg)
|
|
79
|
-
return Response(
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
"
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
62
|
+
return Response(
|
|
63
|
+
{
|
|
64
|
+
"ok": False,
|
|
65
|
+
"errors": errors,
|
|
66
|
+
"meta": {
|
|
67
|
+
"patientExaminationId": pe_id,
|
|
68
|
+
"setsEvaluated": 0,
|
|
69
|
+
"requirementsEvaluated": 0,
|
|
70
|
+
"status": "failed",
|
|
71
|
+
},
|
|
72
|
+
"results": [],
|
|
87
73
|
},
|
|
88
|
-
|
|
89
|
-
|
|
74
|
+
status=status.HTTP_200_OK,
|
|
75
|
+
)
|
|
90
76
|
except Exception as e:
|
|
91
77
|
msg = f"Unexpected error retrieving PatientExamination {pe_id}: {e}"
|
|
92
78
|
errors.append(msg)
|
|
93
79
|
logger.exception("evaluate_requirements: %s", msg)
|
|
94
|
-
return Response(
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
"
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
80
|
+
return Response(
|
|
81
|
+
{
|
|
82
|
+
"ok": False,
|
|
83
|
+
"errors": errors,
|
|
84
|
+
"meta": {
|
|
85
|
+
"patientExaminationId": pe_id,
|
|
86
|
+
"setsEvaluated": 0,
|
|
87
|
+
"requirementsEvaluated": 0,
|
|
88
|
+
"status": "failed",
|
|
89
|
+
},
|
|
90
|
+
"results": [],
|
|
102
91
|
},
|
|
103
|
-
|
|
104
|
-
|
|
92
|
+
status=status.HTTP_200_OK,
|
|
93
|
+
)
|
|
105
94
|
|
|
106
95
|
# ---- determine requirement sets
|
|
107
96
|
try:
|
|
@@ -120,75 +109,152 @@ def evaluate_requirements(request):
|
|
|
120
109
|
msg = f"Error loading RequirementSets: {e}"
|
|
121
110
|
errors.append(msg)
|
|
122
111
|
logger.exception("evaluate_requirements: %s", msg)
|
|
123
|
-
return Response(
|
|
124
|
-
|
|
112
|
+
return Response(
|
|
113
|
+
{
|
|
114
|
+
"ok": False,
|
|
115
|
+
"errors": errors,
|
|
116
|
+
"meta": {
|
|
117
|
+
"patientExaminationId": pe_id,
|
|
118
|
+
"setsEvaluated": 0,
|
|
119
|
+
"requirementsEvaluated": 0,
|
|
120
|
+
"status": "failed",
|
|
121
|
+
},
|
|
122
|
+
"results": [],
|
|
123
|
+
},
|
|
124
|
+
status=status.HTTP_200_OK,
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# nothing to evaluate → still return 200
|
|
128
|
+
if not requirement_sets:
|
|
129
|
+
response_payload = {
|
|
130
|
+
"ok": len(errors) == 0,
|
|
125
131
|
"errors": errors,
|
|
126
132
|
"meta": {
|
|
127
133
|
"patientExaminationId": pe_id,
|
|
128
134
|
"setsEvaluated": 0,
|
|
129
135
|
"requirementsEvaluated": 0,
|
|
130
|
-
"status": "failed",
|
|
136
|
+
"status": "failed" if errors else "ok",
|
|
131
137
|
},
|
|
132
|
-
"results": []
|
|
133
|
-
}
|
|
138
|
+
"results": [],
|
|
139
|
+
}
|
|
140
|
+
return Response(response_payload, status=status.HTTP_200_OK)
|
|
141
|
+
|
|
142
|
+
# mapping from IDs to objects for later lookup
|
|
143
|
+
sets_by_id: dict[int, RequirementSet] = {s.id: s for s in requirement_sets}
|
|
144
|
+
|
|
145
|
+
# ---- main evaluation with set dependencies
|
|
146
|
+
try:
|
|
147
|
+
# returns: { set_id: { req_id: (status, details) } }
|
|
148
|
+
set_results = evaluate_requirement_sets_with_dependencies(
|
|
149
|
+
requirement_sets,
|
|
150
|
+
pe.patient,
|
|
151
|
+
mode="strict",
|
|
152
|
+
)
|
|
153
|
+
|
|
154
|
+
for set_id, req_dict in set_results.items():
|
|
155
|
+
req_set = sets_by_id.get(set_id)
|
|
156
|
+
set_name = getattr(
|
|
157
|
+
req_set, "name", str(set_id)
|
|
158
|
+
) if req_set is not None else str(set_id)
|
|
134
159
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
# fall back to the prefetched instance
|
|
143
|
-
requirement_obj = req
|
|
160
|
+
for req_id, (status_value, details) in req_dict.items():
|
|
161
|
+
try:
|
|
162
|
+
requirement_obj = Requirement.objects.get(id=req_id)
|
|
163
|
+
req_name = getattr(requirement_obj, "name", f"#{req_id}")
|
|
164
|
+
except Requirement.DoesNotExist:
|
|
165
|
+
requirement_obj = None
|
|
166
|
+
req_name = f"#{req_id}"
|
|
144
167
|
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
168
|
+
# map RequirementStatus → met + error
|
|
169
|
+
if status_value == "PASSED":
|
|
170
|
+
met = True
|
|
171
|
+
error_str = None
|
|
172
|
+
elif status_value in ("FAILED", "BLOCKED"):
|
|
173
|
+
met = False
|
|
174
|
+
error_str = None
|
|
175
|
+
else: # "ERROR"
|
|
176
|
+
met = False
|
|
177
|
+
error_str = "Technischer Fehler bei der Auswertung"
|
|
178
|
+
|
|
179
|
+
# normalize details to string
|
|
148
180
|
if isinstance(details, str):
|
|
149
181
|
details_str = details
|
|
150
182
|
else:
|
|
151
183
|
try:
|
|
152
|
-
details_str = json.dumps(
|
|
184
|
+
details_str = json.dumps(
|
|
185
|
+
details, ensure_ascii=False, default=str
|
|
186
|
+
)
|
|
153
187
|
except Exception:
|
|
154
188
|
details_str = str(details)
|
|
155
189
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
+
# default fallback text if details are empty
|
|
191
|
+
if not details_str:
|
|
192
|
+
details_str = (
|
|
193
|
+
"Voraussetzung erfüllt"
|
|
194
|
+
if met
|
|
195
|
+
else "Voraussetzung nicht erfüllt"
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
if status_value == "ERROR":
|
|
199
|
+
# add a high-level error for meta if there was an internal error
|
|
200
|
+
msg = (
|
|
201
|
+
f"Technischer Fehler bei der Auswertung von "
|
|
202
|
+
f"Voraussetzung '{req_name}' in Set '{set_name}'."
|
|
203
|
+
)
|
|
204
|
+
errors.append(msg)
|
|
205
|
+
|
|
206
|
+
results.append(
|
|
207
|
+
{
|
|
208
|
+
"requirement_set_id": set_id,
|
|
209
|
+
"requirement_set_name": set_name,
|
|
210
|
+
"requirement_name": req_name,
|
|
211
|
+
"met": bool(met),
|
|
212
|
+
"details": details_str,
|
|
213
|
+
"error": error_str,
|
|
214
|
+
"status": status_value,
|
|
215
|
+
}
|
|
216
|
+
)
|
|
217
|
+
requirements_evaluated += 1
|
|
218
|
+
|
|
219
|
+
except Exception as e:
|
|
220
|
+
# hard failure of the orchestrator → log and fall back to per-requirement evaluation
|
|
221
|
+
msg = f"Unerwarteter Fehler bei der gruppenbasierten Bewertung: {e}"
|
|
222
|
+
errors.append(msg)
|
|
223
|
+
logger.exception("evaluate_requirements: %s", msg)
|
|
224
|
+
|
|
225
|
+
for req_set in requirement_sets:
|
|
226
|
+
for req in req_set.requirements.all():
|
|
227
|
+
met, details, error = safe_evaluate_requirement(
|
|
228
|
+
req, pe.patient, mode="strict"
|
|
229
|
+
)
|
|
230
|
+
# normalize details to string
|
|
231
|
+
if not isinstance(details, str):
|
|
232
|
+
try:
|
|
233
|
+
details = json.dumps(details, ensure_ascii=False, default=str)
|
|
234
|
+
except Exception:
|
|
235
|
+
details = str(details)
|
|
236
|
+
|
|
237
|
+
if not details:
|
|
238
|
+
details = (
|
|
239
|
+
"Voraussetzung erfüllt"
|
|
240
|
+
if met
|
|
241
|
+
else "Voraussetzung nicht erfüllt"
|
|
242
|
+
)
|
|
190
243
|
|
|
191
|
-
|
|
244
|
+
results.append(
|
|
245
|
+
{
|
|
246
|
+
"requirement_set_id": req_set.id,
|
|
247
|
+
"requirement_set_name": getattr(
|
|
248
|
+
req_set, "name", str(req_set.id)
|
|
249
|
+
),
|
|
250
|
+
"requirement_name": getattr(req, "name", "unknown"),
|
|
251
|
+
"met": bool(met),
|
|
252
|
+
"details": details,
|
|
253
|
+
"error": error,
|
|
254
|
+
"status": "PASSED" if met else "FAILED",
|
|
255
|
+
}
|
|
256
|
+
)
|
|
257
|
+
requirements_evaluated += 1
|
|
192
258
|
|
|
193
259
|
# ---- response meta & status summary
|
|
194
260
|
any_errors = len(errors) > 0
|
|
@@ -201,79 +267,14 @@ def evaluate_requirements(request):
|
|
|
201
267
|
|
|
202
268
|
response_payload = {
|
|
203
269
|
"ok": not any_errors,
|
|
204
|
-
"errors": errors,
|
|
270
|
+
"errors": errors,
|
|
205
271
|
"meta": {
|
|
206
272
|
"patientExaminationId": pe_id,
|
|
207
273
|
"setsEvaluated": sets_evaluated,
|
|
208
274
|
"requirementsEvaluated": requirements_evaluated,
|
|
209
|
-
"status": status_label
|
|
275
|
+
"status": status_label,
|
|
210
276
|
},
|
|
211
|
-
"results": results
|
|
277
|
+
"results": results,
|
|
212
278
|
}
|
|
213
279
|
|
|
214
280
|
return Response(response_payload, status=status.HTTP_200_OK)
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
def evaluate_requirement_set(request) -> Response:
|
|
218
|
-
"""
|
|
219
|
-
Evaluate a specific RequirementSet based on provided RequirementLinks data.
|
|
220
|
-
|
|
221
|
-
Expects a JSON payload with the following structure:
|
|
222
|
-
{
|
|
223
|
-
"requirement_set_ids": [<requirement_set_ids>],
|
|
224
|
-
"patient_examination_id": <patient_examination_id>
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
Returns a JSON response with the evaluation results for the specified RequirementSet:
|
|
228
|
-
{
|
|
229
|
-
"results": [
|
|
230
|
-
{
|
|
231
|
-
"requirement_name": <requirement_name>,
|
|
232
|
-
"met": <true/false>,
|
|
233
|
-
"details": <additional_details>
|
|
234
|
-
},
|
|
235
|
-
...
|
|
236
|
-
]
|
|
237
|
-
}
|
|
238
|
-
"""
|
|
239
|
-
try:
|
|
240
|
-
data = request.data
|
|
241
|
-
requirement_set_ids = data.get("requirement_set_ids", None)
|
|
242
|
-
patient_examination_id = data.get("patient_examination_id")
|
|
243
|
-
|
|
244
|
-
if not requirement_set_ids:
|
|
245
|
-
return Response({"error": "requirement_set_ids is required"}, status=status.HTTP_400_BAD_REQUEST)
|
|
246
|
-
|
|
247
|
-
if not patient_examination_id:
|
|
248
|
-
return Response({"error": "patient_examination_id is required"}, status=status.HTTP_400_BAD_REQUEST)
|
|
249
|
-
|
|
250
|
-
# Get the patient examination
|
|
251
|
-
try:
|
|
252
|
-
patient_examination = PatientExamination.objects.get(id=patient_examination_id)
|
|
253
|
-
except PatientExamination.DoesNotExist:
|
|
254
|
-
return Response({"error": f"PatientExamination with id {patient_examination_id} does not exist"}, status=status.HTTP_404_NOT_FOUND)
|
|
255
|
-
|
|
256
|
-
results = []
|
|
257
|
-
|
|
258
|
-
# Construct RequirementLinks from the provided data
|
|
259
|
-
for requirement_set_id in requirement_set_ids:
|
|
260
|
-
# Fetch the specified RequirementSet
|
|
261
|
-
try:
|
|
262
|
-
req_set = RequirementSet.objects.get(id=requirement_set_id)
|
|
263
|
-
except RequirementSet.DoesNotExist:
|
|
264
|
-
return Response({"error": f"RequirementSet with id {requirement_set_id} does not exist"}, status=status.HTTP_404_NOT_FOUND)
|
|
265
|
-
|
|
266
|
-
for requirement in req_set.requirements.all():
|
|
267
|
-
met, details = requirement.evaluate(patient_examination.patient, mode="strict")
|
|
268
|
-
results.append({
|
|
269
|
-
"requirement_name": requirement.name,
|
|
270
|
-
"met": met,
|
|
271
|
-
"details": details
|
|
272
|
-
})
|
|
273
|
-
|
|
274
|
-
return Response({
|
|
275
|
-
"results": results
|
|
276
|
-
}, status=status.HTTP_200_OK)
|
|
277
|
-
|
|
278
|
-
except Exception as e:
|
|
279
|
-
return Response({"results": str(e), details: "Fehler bei der Bewertung der Voraussetzung"}, status=status.HTTP_400_BAD_REQUEST)
|