endoreg-db 0.8.4.4__py3-none-any.whl → 0.8.8.0__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 +8 -31
- endoreg_db/data/_examples/disease.yaml +55 -0
- endoreg_db/data/_examples/disease_classification.yaml +13 -0
- endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
- endoreg_db/data/_examples/event.yaml +64 -0
- endoreg_db/data/_examples/examination.yaml +72 -0
- endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
- endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
- endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
- endoreg_db/data/_examples/finding/complication.yaml +16 -0
- endoreg_db/data/_examples/finding/data.yaml +105 -0
- endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
- endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
- endoreg_db/data/_examples/finding/outcome.yaml +12 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
- endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
- endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
- endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
- endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
- endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
- endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
- endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
- endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
- endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/_examples/finding_type/data.yaml +43 -0
- endoreg_db/data/_examples/requirement/age.yaml +26 -0
- endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
- endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
- endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
- endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
- endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
- endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
- endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
- endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
- endoreg_db/data/_examples/requirement/gender.yaml +25 -0
- endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
- endoreg_db/data/_examples/requirement/medication.yaml +93 -0
- endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
- endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
- endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
- endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
- endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
- endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
- endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
- endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
- endoreg_db/data/requirement_set/90_coloreg.yaml +178 -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 +5 -2
- endoreg_db/helpers/data_loader.py +1 -1
- 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_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +1 -1
- endoreg_db/management/commands/init_default_ai_model.py +1 -1
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_ai_model_data.py +2 -1
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_requirement_data.py +60 -31
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/setup_endoreg_db.py +14 -10
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/migrations/0001_initial.py +1799 -1300
- endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
- endoreg_db/migrations/_old/0001_initial.py +1857 -0
- endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
- endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
- endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
- endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
- endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
- endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
- endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
- 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 +103 -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 +7 -8
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
- 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 +76 -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 +249 -177
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +48 -56
- endoreg_db/models/media/video/pipe_1.py +30 -33
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +359 -204
- 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 +109 -62
- 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/__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 +17 -18
- endoreg_db/models/medical/examination/examination_indication.py +26 -25
- endoreg_db/models/medical/examination/examination_time.py +16 -6
- 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 +38 -39
- endoreg_db/models/medical/finding/finding_classification.py +37 -48
- endoreg_db/models/medical/finding/finding_intervention.py +27 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +20 -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 +1 -5
- 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 +139 -18
- endoreg_db/models/metadata/pdf_meta.py +19 -24
- endoreg_db/models/metadata/sensitive_meta.py +102 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +383 -43
- 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 +25 -25
- 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/requirement/requirement.py +580 -272
- 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 +36 -33
- 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 +46 -47
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +40 -46
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +58 -53
- endoreg_db/models/upload_job.py +32 -55
- endoreg_db/models/utils.py +1 -2
- endoreg_db/root_urls.py +21 -2
- endoreg_db/serializers/__init__.py +26 -57
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/meta/report_meta.py +1 -1
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/__init__.py +1 -1
- endoreg_db/serializers/misc/file_overview.py +33 -91
- endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/serializers/video_examination.py +198 -0
- endoreg_db/services/anonymization.py +75 -73
- endoreg_db/services/lookup_service.py +256 -73
- endoreg_db/services/lookup_store.py +174 -30
- endoreg_db/services/pdf_import.py +711 -310
- endoreg_db/services/storage_aware_video_processor.py +140 -114
- endoreg_db/services/video_import.py +266 -117
- endoreg_db/urls/__init__.py +27 -27
- endoreg_db/urls/label_video_segments.py +2 -0
- endoreg_db/urls/media.py +108 -66
- 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 +88 -16
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
- 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 +5 -12
- endoreg_db/views/anonymization/media_management.py +198 -163
- endoreg_db/views/anonymization/overview.py +4 -1
- endoreg_db/views/anonymization/validate.py +174 -40
- endoreg_db/views/media/__init__.py +2 -0
- endoreg_db/views/media/pdf_media.py +131 -150
- endoreg_db/views/media/sensitive_metadata.py +46 -6
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/media/video_segments.py +187 -260
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/pdf/__init__.py +5 -8
- endoreg_db/views/pdf/pdf_stream.py +186 -0
- endoreg_db/views/pdf/pdf_stream_views.py +0 -127
- endoreg_db/views/pdf/reimport.py +86 -91
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +186 -288
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +0 -4
- endoreg_db/views/video/correction.py +2 -2
- endoreg_db/views/video/video_examination_viewset.py +202 -289
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +350 -255
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/media/video/refactor_plan.md +0 -0
- endoreg_db/models/media/video/video_file_frames.py +0 -0
- endoreg_db/models/metadata/frame_ocr_result.py +0 -0
- 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/serializers/video/video_metadata.py +0 -105
- 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/views/pdf/pdf_media.py +0 -239
- endoreg_db/views/report/__init__.py +0 -9
- 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/video_media.py +0 -158
- endoreg_db/views.py +0 -0
- /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
- /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
- /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,35 +1,82 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Lookup Service Module
|
|
3
|
+
|
|
4
|
+
This module provides server-side evaluation and lookup functionality for patient examinations.
|
|
5
|
+
It handles requirement set evaluation, finding availability, and status computation for
|
|
6
|
+
medical examination workflows.
|
|
7
|
+
|
|
8
|
+
The lookup system uses a token-based approach where client sessions are stored in Django cache,
|
|
9
|
+
allowing for efficient state management and recomputation of derived data.
|
|
10
|
+
|
|
11
|
+
Key Components:
|
|
12
|
+
- PatientExamination loading with optimized prefetching
|
|
13
|
+
- Requirement set resolution and evaluation
|
|
14
|
+
- Status computation for requirements and requirement sets
|
|
15
|
+
- Suggested actions for unsatisfied requirements
|
|
16
|
+
- Cache-based session management
|
|
17
|
+
|
|
18
|
+
Architecture:
|
|
19
|
+
1. LookupStore: Handles cache-based session storage
|
|
20
|
+
2. lookup_service: Core business logic for evaluation
|
|
21
|
+
3. LookupViewSet: Django REST API endpoints
|
|
22
|
+
"""
|
|
23
|
+
|
|
1
24
|
# services/lookup_service.py
|
|
2
25
|
from __future__ import annotations
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
from
|
|
26
|
+
|
|
27
|
+
import logging
|
|
28
|
+
from typing import Any, Dict, List, Optional
|
|
29
|
+
|
|
30
|
+
from django.db.models import Prefetch, QuerySet
|
|
31
|
+
|
|
6
32
|
from endoreg_db.models.medical.examination import ExaminationRequirementSet
|
|
33
|
+
from endoreg_db.models.medical.patient.patient_examination import PatientExamination
|
|
7
34
|
from endoreg_db.models.requirement.requirement_set import RequirementSet
|
|
35
|
+
|
|
8
36
|
from .lookup_store import LookupStore
|
|
9
37
|
|
|
10
38
|
|
|
11
39
|
def load_patient_exam_for_eval(pk: int) -> PatientExamination:
|
|
12
40
|
"""
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
Load a PatientExamination with all related data needed for evaluation.
|
|
42
|
+
|
|
43
|
+
This function performs optimized database queries to fetch a PatientExamination
|
|
44
|
+
along with all related objects required for requirement evaluation, including:
|
|
45
|
+
- Patient and examination details
|
|
46
|
+
- Patient findings
|
|
47
|
+
- Examination requirement sets and their requirements
|
|
48
|
+
- Nested requirement set relationships
|
|
49
|
+
|
|
50
|
+
The query uses select_related and prefetch_related to minimize database hits
|
|
51
|
+
and ensure all data is available for evaluation without additional queries.
|
|
52
|
+
|
|
53
|
+
Args:
|
|
54
|
+
pk: Primary key of the PatientExamination to load
|
|
55
|
+
|
|
56
|
+
Returns:
|
|
57
|
+
PatientExamination: Fully loaded instance with all related data prefetched
|
|
58
|
+
|
|
59
|
+
Raises:
|
|
60
|
+
PatientExamination.DoesNotExist: If no examination exists with the given pk
|
|
15
61
|
"""
|
|
16
62
|
return (
|
|
17
|
-
PatientExamination.objects
|
|
18
|
-
.select_related("patient", "examination")
|
|
63
|
+
PatientExamination.objects.select_related("patient", "examination")
|
|
19
64
|
.prefetch_related(
|
|
20
65
|
"patient_findings",
|
|
21
66
|
# Prefetch ERS groups on the Examination…
|
|
22
67
|
Prefetch(
|
|
23
68
|
"examination__exam_reqset_links",
|
|
24
|
-
queryset=ExaminationRequirementSet.objects.only(
|
|
69
|
+
queryset=ExaminationRequirementSet.objects.only(
|
|
70
|
+
"id", "name", "enabled_by_default"
|
|
71
|
+
),
|
|
25
72
|
),
|
|
26
73
|
# …and the RequirementSets reachable via those ERS groups.
|
|
27
74
|
Prefetch(
|
|
28
75
|
"examination__exam_reqset_links__requirement_set",
|
|
29
76
|
queryset=(
|
|
30
|
-
RequirementSet.objects
|
|
31
|
-
|
|
32
|
-
.prefetch_related(
|
|
77
|
+
RequirementSet.objects.select_related(
|
|
78
|
+
"requirement_set_type"
|
|
79
|
+
).prefetch_related(
|
|
33
80
|
"requirements",
|
|
34
81
|
"links_to_sets",
|
|
35
82
|
"links_to_sets__requirements",
|
|
@@ -42,33 +89,84 @@ def load_patient_exam_for_eval(pk: int) -> PatientExamination:
|
|
|
42
89
|
)
|
|
43
90
|
|
|
44
91
|
|
|
45
|
-
def requirement_sets_for_patient_exam(
|
|
92
|
+
def requirement_sets_for_patient_exam(
|
|
93
|
+
pe: PatientExamination, user_tags: Optional[List[str]] = None
|
|
94
|
+
) -> QuerySet:
|
|
46
95
|
"""
|
|
47
|
-
|
|
48
|
-
|
|
96
|
+
Retrieve all RequirementSets linked to a PatientExamination's examination.
|
|
97
|
+
|
|
98
|
+
Args:
|
|
99
|
+
pe: PatientExamination instance
|
|
100
|
+
user_tags: Optional list of tag names to filter requirement sets
|
|
101
|
+
|
|
102
|
+
Returns:
|
|
103
|
+
QuerySet of RequirementSet instances
|
|
49
104
|
"""
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
105
|
+
if not pe or not pe.examination:
|
|
106
|
+
from endoreg_db.models import RequirementSet
|
|
107
|
+
|
|
108
|
+
return RequirementSet.objects.none()
|
|
109
|
+
|
|
110
|
+
# Start with examination-linked requirement sets
|
|
111
|
+
req_sets = pe.examination.exam_reqset_links.select_related(
|
|
112
|
+
"requirement_set"
|
|
113
|
+
).values_list("requirement_set", flat=True)
|
|
114
|
+
|
|
115
|
+
from endoreg_db.models import RequirementSet
|
|
116
|
+
|
|
117
|
+
qs = RequirementSet.objects.filter(pk__in=req_sets)
|
|
60
118
|
|
|
61
|
-
|
|
119
|
+
# Apply tag filtering if provided
|
|
120
|
+
if user_tags:
|
|
121
|
+
qs = qs.filter(tags__name__in=user_tags).distinct()
|
|
122
|
+
|
|
123
|
+
return qs
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def build_initial_lookup(
|
|
127
|
+
pe: PatientExamination, user_tags: Optional[List[str]] = None
|
|
128
|
+
) -> Dict[str, Any]:
|
|
62
129
|
"""
|
|
63
|
-
Build the initial lookup
|
|
64
|
-
|
|
130
|
+
Build the initial lookup dictionary for a patient examination.
|
|
131
|
+
|
|
132
|
+
This function creates the base lookup data structure that will be stored in cache
|
|
133
|
+
and used by the client for requirement evaluation. It includes:
|
|
134
|
+
|
|
135
|
+
- Available findings for the examination type
|
|
136
|
+
- Required findings based on requirement defaults
|
|
137
|
+
- Requirement sets metadata
|
|
138
|
+
- Default findings and classification choices per requirement
|
|
139
|
+
- Empty placeholders for dynamic data (status, suggestions, etc.)
|
|
140
|
+
|
|
141
|
+
The returned dictionary is JSON-serializable and contains stable keys that
|
|
142
|
+
won't change between versions.
|
|
143
|
+
|
|
144
|
+
Args:
|
|
145
|
+
pe: PatientExamination instance to build lookup for
|
|
146
|
+
|
|
147
|
+
Returns:
|
|
148
|
+
Dictionary containing initial lookup data with the following keys:
|
|
149
|
+
- patient_examination_id: ID of the patient examination
|
|
150
|
+
- requirement_sets: List of available requirement sets with metadata
|
|
151
|
+
- availableFindings: List of finding IDs available for the examination
|
|
152
|
+
- requiredFindings: List of finding IDs that are required by defaults
|
|
153
|
+
- requirementDefaults: Default findings per requirement
|
|
154
|
+
- classificationChoices: Available classification choices per requirement
|
|
155
|
+
- requirementsBySet: Empty dict (populated on selection)
|
|
156
|
+
- requirementStatus: Empty dict (computed on evaluation)
|
|
157
|
+
- requirementSetStatus: Empty dict (computed on evaluation)
|
|
158
|
+
- suggestedActions: Empty dict (computed on evaluation)
|
|
65
159
|
"""
|
|
66
160
|
# Available + required findings
|
|
67
|
-
available_findings =
|
|
161
|
+
available_findings = (
|
|
162
|
+
[f.id for f in pe.examination.get_available_findings()]
|
|
163
|
+
if pe.examination
|
|
164
|
+
else []
|
|
165
|
+
)
|
|
68
166
|
required_findings: List[int] = [] # fill by scanning requirements below
|
|
69
167
|
|
|
70
168
|
# Requirement sets: ids + meta
|
|
71
|
-
rs_objs = requirement_sets_for_patient_exam(pe)
|
|
169
|
+
rs_objs = requirement_sets_for_patient_exam(pe, user_tags=user_tags)
|
|
72
170
|
requirement_sets = [
|
|
73
171
|
{
|
|
74
172
|
"id": rs.id,
|
|
@@ -90,9 +188,13 @@ def build_initial_lookup(pe: PatientExamination) -> Dict[str, Any]:
|
|
|
90
188
|
choices = getattr(req, "classification_choices", lambda pe: [])(pe)
|
|
91
189
|
if defaults:
|
|
92
190
|
req_defaults[str(req.id)] = defaults # list of {finding_id, payload...}
|
|
93
|
-
required_findings.extend(
|
|
191
|
+
required_findings.extend(
|
|
192
|
+
[d.get("finding_id") for d in defaults if "finding_id" in d]
|
|
193
|
+
)
|
|
94
194
|
if choices:
|
|
95
|
-
cls_choices[str(req.id)] =
|
|
195
|
+
cls_choices[str(req.id)] = (
|
|
196
|
+
choices # list of {classification_id, label, ...}
|
|
197
|
+
)
|
|
96
198
|
|
|
97
199
|
# De-dup required
|
|
98
200
|
required_findings = sorted(set(required_findings))
|
|
@@ -112,62 +214,129 @@ def build_initial_lookup(pe: PatientExamination) -> Dict[str, Any]:
|
|
|
112
214
|
# You can add "selectedRequirementSetIds" as the user makes choices
|
|
113
215
|
}
|
|
114
216
|
|
|
115
|
-
|
|
217
|
+
|
|
218
|
+
def create_lookup_token_for_pe(
|
|
219
|
+
pe_id: int, user_tags: Optional[List[str]] = None
|
|
220
|
+
) -> str:
|
|
221
|
+
"""
|
|
222
|
+
Create a lookup token for a patient examination.
|
|
223
|
+
|
|
224
|
+
This function initializes a new lookup session for the given patient examination
|
|
225
|
+
by building the initial lookup data and storing it in the cache via LookupStore.
|
|
226
|
+
|
|
227
|
+
Args:
|
|
228
|
+
pe_id: Primary key of the PatientExamination
|
|
229
|
+
|
|
230
|
+
Returns:
|
|
231
|
+
String token that can be used to access the lookup session
|
|
232
|
+
|
|
233
|
+
Raises:
|
|
234
|
+
PatientExamination.DoesNotExist: If examination doesn't exist
|
|
235
|
+
Exception: For any other errors during initialization
|
|
236
|
+
"""
|
|
116
237
|
pe = load_patient_exam_for_eval(pe_id)
|
|
117
|
-
token = LookupStore().init(build_initial_lookup(pe))
|
|
238
|
+
token = LookupStore().init(build_initial_lookup(pe, user_tags=user_tags))
|
|
118
239
|
return token
|
|
119
240
|
|
|
241
|
+
|
|
120
242
|
def recompute_lookup(token: str) -> Dict[str, Any]:
|
|
121
|
-
|
|
243
|
+
"""
|
|
244
|
+
Recompute derived lookup data based on current patient examination state and user selections.
|
|
245
|
+
|
|
246
|
+
This function performs the core evaluation logic for the lookup system. It:
|
|
247
|
+
|
|
248
|
+
1. Validates and recovers corrupted lookup data
|
|
249
|
+
2. Loads the current PatientExamination state from database
|
|
250
|
+
3. Evaluates requirements against the current examination state
|
|
251
|
+
4. Computes status for individual requirements and requirement sets
|
|
252
|
+
5. Generates suggested actions for unsatisfied requirements
|
|
253
|
+
6. Updates the cache with new derived data (idempotent)
|
|
254
|
+
|
|
255
|
+
The function includes reentrancy protection to prevent concurrent recomputation
|
|
256
|
+
of the same token.
|
|
257
|
+
|
|
258
|
+
Args:
|
|
259
|
+
token: Lookup session token
|
|
260
|
+
|
|
261
|
+
Returns:
|
|
262
|
+
Dictionary of updates containing:
|
|
263
|
+
- requirementsBySet: Requirements grouped by selected requirement sets
|
|
264
|
+
- requirementStatus: Boolean status for each requirement
|
|
265
|
+
- requirementSetStatus: Boolean status for each requirement set
|
|
266
|
+
- requirementDefaults: Default findings per requirement
|
|
267
|
+
- classificationChoices: Available choices per requirement
|
|
268
|
+
- suggestedActions: UI actions to satisfy unsatisfied requirements
|
|
269
|
+
|
|
270
|
+
Raises:
|
|
271
|
+
ValueError: If lookup data is invalid or patient examination not found
|
|
272
|
+
"""
|
|
122
273
|
logger = logging.getLogger(__name__)
|
|
123
|
-
|
|
274
|
+
|
|
124
275
|
store = LookupStore(token=token)
|
|
125
|
-
|
|
276
|
+
|
|
126
277
|
# Simple reentrancy guard using data
|
|
127
278
|
data = store.get_all()
|
|
128
|
-
if data.get(
|
|
279
|
+
if data.get("_recomputing"):
|
|
129
280
|
logger.warning(f"Recompute already in progress for token {token}, skipping")
|
|
130
281
|
return {}
|
|
131
|
-
|
|
132
|
-
store.set(
|
|
133
|
-
|
|
282
|
+
|
|
283
|
+
store.set("_recomputing", True)
|
|
284
|
+
|
|
134
285
|
try:
|
|
135
286
|
# First validate and attempt to recover corrupted data
|
|
136
287
|
validated_data = store.validate_and_recover_data(token)
|
|
137
288
|
if validated_data is None:
|
|
138
289
|
logger.error(f"No lookup data found for token {token}")
|
|
139
290
|
raise ValueError(f"No lookup data found for token {token}")
|
|
140
|
-
|
|
291
|
+
|
|
141
292
|
data = validated_data
|
|
142
|
-
logger.debug(
|
|
143
|
-
|
|
293
|
+
logger.debug(
|
|
294
|
+
f"Recomputing lookup for token {token}, data keys: {list(data.keys())}"
|
|
295
|
+
)
|
|
296
|
+
|
|
144
297
|
# Check if required data exists
|
|
145
298
|
if "patient_examination_id" not in data:
|
|
146
|
-
logger.error(
|
|
147
|
-
|
|
148
|
-
|
|
299
|
+
logger.error(
|
|
300
|
+
f"Invalid lookup data for token {token}: missing patient_examination_id. Data: {data}"
|
|
301
|
+
)
|
|
302
|
+
raise ValueError(
|
|
303
|
+
f"Invalid lookup data for token {token}: missing patient_examination_id"
|
|
304
|
+
)
|
|
305
|
+
|
|
149
306
|
if not data.get("patient_examination_id"):
|
|
150
|
-
logger.error(
|
|
151
|
-
|
|
307
|
+
logger.error(
|
|
308
|
+
f"Invalid lookup data for token {token}: patient_examination_id is empty. Data: {data}"
|
|
309
|
+
)
|
|
310
|
+
raise ValueError(
|
|
311
|
+
f"Invalid lookup data for token {token}: patient_examination_id is empty"
|
|
312
|
+
)
|
|
152
313
|
|
|
153
314
|
pe_id = data["patient_examination_id"]
|
|
154
315
|
logger.debug(f"Loading patient examination {pe_id} for token {token}")
|
|
155
|
-
|
|
316
|
+
|
|
156
317
|
try:
|
|
157
318
|
pe = load_patient_exam_for_eval(pe_id)
|
|
158
319
|
except Exception as e:
|
|
159
|
-
logger.error(
|
|
320
|
+
logger.error(
|
|
321
|
+
f"Failed to load patient examination {pe_id} for token {token}: {e}"
|
|
322
|
+
)
|
|
160
323
|
raise ValueError(f"Failed to load patient examination {pe_id}: {e}")
|
|
161
324
|
|
|
162
325
|
selected_rs_ids: List[int] = data.get("selectedRequirementSetIds", [])
|
|
163
|
-
logger.debug(
|
|
164
|
-
|
|
165
|
-
|
|
326
|
+
logger.debug(
|
|
327
|
+
f"Selected requirement set IDs for token {token}: {selected_rs_ids}"
|
|
328
|
+
)
|
|
329
|
+
|
|
330
|
+
rs_objs = [
|
|
331
|
+
rs
|
|
332
|
+
for rs in requirement_sets_for_patient_exam(pe)
|
|
333
|
+
if rs.id in selected_rs_ids
|
|
334
|
+
]
|
|
166
335
|
logger.debug(f"Found {len(rs_objs)} requirement set objects for token {token}")
|
|
167
336
|
|
|
168
337
|
# 1) requirements grouped by set (already prefetched in load func)
|
|
169
338
|
requirements_by_set = {
|
|
170
|
-
rs.id: [
|
|
339
|
+
rs.id: [{"id": r.id, "name": r.name} for r in rs.requirements.all()]
|
|
171
340
|
for rs in rs_objs
|
|
172
341
|
}
|
|
173
342
|
|
|
@@ -180,7 +349,9 @@ def recompute_lookup(token: str) -> Dict[str, Any]:
|
|
|
180
349
|
ok = bool(r.evaluate(pe, mode="strict")) # or "loose" if you prefer
|
|
181
350
|
requirement_status[str(r.id)] = ok
|
|
182
351
|
req_results.append(ok)
|
|
183
|
-
set_status[str(rs.id)] =
|
|
352
|
+
set_status[str(rs.id)] = (
|
|
353
|
+
rs.eval_function(req_results) if rs.eval_function else all(req_results)
|
|
354
|
+
)
|
|
184
355
|
|
|
185
356
|
# 3) suggestions per requirement (defaults + classification choices you already expose)
|
|
186
357
|
suggested_actions: Dict[str, List[Dict[str, Any]]] = {}
|
|
@@ -189,8 +360,12 @@ def recompute_lookup(token: str) -> Dict[str, Any]:
|
|
|
189
360
|
|
|
190
361
|
for rs in rs_objs:
|
|
191
362
|
for r in rs.requirements.all():
|
|
192
|
-
defaults = getattr(r, "default_findings", lambda pe: [])(
|
|
193
|
-
|
|
363
|
+
defaults = getattr(r, "default_findings", lambda pe: [])(
|
|
364
|
+
pe
|
|
365
|
+
) # [{finding_id, payload...}]
|
|
366
|
+
choices = getattr(r, "classification_choices", lambda pe: [])(
|
|
367
|
+
pe
|
|
368
|
+
) # [{classification_id, label,...}]
|
|
194
369
|
if defaults:
|
|
195
370
|
req_defaults[str(r.id)] = defaults
|
|
196
371
|
if choices:
|
|
@@ -200,15 +375,19 @@ def recompute_lookup(token: str) -> Dict[str, Any]:
|
|
|
200
375
|
# turn default proposals into explicit UI actions
|
|
201
376
|
acts = []
|
|
202
377
|
for d in defaults or []:
|
|
203
|
-
acts.append(
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
378
|
+
acts.append(
|
|
379
|
+
{
|
|
380
|
+
"type": "add_finding",
|
|
381
|
+
"finding_id": d.get("finding_id"),
|
|
382
|
+
"classification_ids": d.get("classification_ids") or [],
|
|
383
|
+
"note": "default",
|
|
384
|
+
}
|
|
385
|
+
)
|
|
209
386
|
# If r expects patient edits, add an edit action hint
|
|
210
387
|
if "PatientExamination" in [m.__name__ for m in r.expected_models]:
|
|
211
|
-
acts.append(
|
|
388
|
+
acts.append(
|
|
389
|
+
{"type": "edit_patient", "fields": ["gender", "dob"]}
|
|
390
|
+
) # example
|
|
212
391
|
if acts:
|
|
213
392
|
suggested_actions[str(r.id)] = acts
|
|
214
393
|
|
|
@@ -220,22 +399,26 @@ def recompute_lookup(token: str) -> Dict[str, Any]:
|
|
|
220
399
|
"requirementsBySet": requirements_by_set,
|
|
221
400
|
"requirementStatus": requirement_status,
|
|
222
401
|
"requirementSetStatus": set_status,
|
|
223
|
-
"requirementDefaults": req_defaults,
|
|
224
|
-
"classificationChoices": cls_choices,
|
|
225
|
-
"suggestedActions": suggested_actions,
|
|
402
|
+
"requirementDefaults": req_defaults, # keep your existing key
|
|
403
|
+
"classificationChoices": cls_choices, # keep your existing key
|
|
404
|
+
"suggestedActions": suggested_actions, # new
|
|
226
405
|
}
|
|
227
|
-
|
|
228
|
-
logger.debug(
|
|
229
|
-
|
|
406
|
+
|
|
407
|
+
logger.debug(
|
|
408
|
+
f"Updating store for token {token} with {len(updates)} update keys"
|
|
409
|
+
)
|
|
410
|
+
|
|
230
411
|
# Only write if changed (idempotent)
|
|
231
412
|
prev_derived = store.get_many(list(updates.keys()))
|
|
232
413
|
if prev_derived != updates:
|
|
233
414
|
store.set_many(updates) # <-- does NOT call recompute
|
|
234
415
|
logger.debug(f"Derived data changed, updated store for token {token}")
|
|
235
416
|
else:
|
|
236
|
-
logger.debug(
|
|
237
|
-
|
|
417
|
+
logger.debug(
|
|
418
|
+
f"Derived data unchanged, skipping store update for token {token}"
|
|
419
|
+
)
|
|
420
|
+
|
|
238
421
|
store.mark_recompute_done()
|
|
239
422
|
return updates
|
|
240
423
|
finally:
|
|
241
|
-
store.set(
|
|
424
|
+
store.set("_recomputing", False)
|