endoreg-db 0.8.9.2__py3-none-any.whl → 0.8.9.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/admin.py +10 -5
- endoreg_db/apps.py +4 -7
- endoreg_db/authz/auth.py +1 -0
- endoreg_db/authz/backends.py +1 -1
- endoreg_db/authz/management/commands/list_routes.py +2 -0
- endoreg_db/authz/middleware.py +8 -7
- endoreg_db/authz/permissions.py +21 -10
- endoreg_db/authz/policy.py +14 -19
- endoreg_db/authz/views_auth.py +14 -10
- endoreg_db/codemods/rename_datetime_fields.py +8 -1
- endoreg_db/exceptions.py +5 -2
- endoreg_db/forms/__init__.py +0 -1
- endoreg_db/forms/examination_form.py +4 -3
- endoreg_db/forms/patient_finding_intervention_form.py +30 -8
- endoreg_db/forms/patient_form.py +9 -13
- endoreg_db/forms/questionnaires/__init__.py +1 -1
- endoreg_db/forms/settings/__init__.py +4 -1
- endoreg_db/forms/unit.py +2 -1
- endoreg_db/helpers/count_db.py +17 -14
- endoreg_db/helpers/default_objects.py +2 -1
- endoreg_db/helpers/download_segmentation_model.py +4 -3
- endoreg_db/helpers/interact.py +0 -5
- endoreg_db/helpers/test_video_helper.py +33 -25
- endoreg_db/import_files/__init__.py +1 -1
- endoreg_db/import_files/context/__init__.py +1 -1
- endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
- endoreg_db/import_files/context/ensure_center.py +4 -4
- endoreg_db/import_files/context/file_lock.py +3 -3
- endoreg_db/import_files/context/import_context.py +11 -12
- endoreg_db/import_files/context/validate_directories.py +1 -0
- endoreg_db/import_files/file_storage/create_report_file.py +57 -34
- endoreg_db/import_files/file_storage/create_video_file.py +64 -35
- endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
- endoreg_db/import_files/file_storage/state_management.py +89 -122
- endoreg_db/import_files/file_storage/storage.py +5 -1
- endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
- endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
- endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
- endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
- endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
- endoreg_db/import_files/report_import_service.py +36 -30
- endoreg_db/import_files/video_import_service.py +27 -23
- endoreg_db/logger_conf.py +56 -40
- endoreg_db/management/__init__.py +1 -1
- endoreg_db/management/commands/__init__.py +1 -1
- endoreg_db/management/commands/check_auth.py +45 -38
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
- endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
- endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
- endoreg_db/management/commands/fix_video_paths.py +75 -54
- endoreg_db/management/commands/import_report.py +1 -3
- endoreg_db/management/commands/list_routes.py +2 -0
- endoreg_db/management/commands/load_ai_model_data.py +8 -2
- endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
- endoreg_db/management/commands/load_center_data.py +3 -3
- endoreg_db/management/commands/load_distribution_data.py +35 -38
- endoreg_db/management/commands/load_endoscope_data.py +0 -3
- endoreg_db/management/commands/load_examination_data.py +20 -4
- endoreg_db/management/commands/load_finding_data.py +18 -3
- endoreg_db/management/commands/load_gender_data.py +17 -24
- endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
- endoreg_db/management/commands/load_information_source.py +0 -3
- endoreg_db/management/commands/load_lab_value_data.py +14 -3
- endoreg_db/management/commands/load_legacy_data.py +303 -0
- endoreg_db/management/commands/load_name_data.py +1 -2
- endoreg_db/management/commands/load_pdf_type_data.py +4 -8
- endoreg_db/management/commands/load_profession_data.py +0 -1
- endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
- endoreg_db/management/commands/load_requirement_data.py +6 -2
- endoreg_db/management/commands/load_unit_data.py +0 -4
- endoreg_db/management/commands/load_user_groups.py +5 -7
- endoreg_db/management/commands/model_input.py +169 -0
- endoreg_db/management/commands/register_ai_model.py +22 -16
- endoreg_db/management/commands/setup_endoreg_db.py +110 -32
- endoreg_db/management/commands/storage_management.py +14 -8
- endoreg_db/management/commands/summarize_db_content.py +154 -63
- endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
- endoreg_db/management/commands/validate_video_files.py +82 -50
- endoreg_db/management/commands/video_validation.py +4 -6
- endoreg_db/migrations/0001_initial.py +112 -63
- endoreg_db/models/__init__.py +8 -0
- endoreg_db/models/administration/ai/active_model.py +5 -5
- endoreg_db/models/administration/ai/ai_model.py +41 -18
- endoreg_db/models/administration/ai/model_type.py +1 -0
- endoreg_db/models/administration/case/case.py +22 -22
- endoreg_db/models/administration/center/__init__.py +5 -5
- endoreg_db/models/administration/center/center.py +6 -2
- endoreg_db/models/administration/center/center_resource.py +18 -4
- endoreg_db/models/administration/center/center_shift.py +3 -1
- endoreg_db/models/administration/center/center_waste.py +6 -2
- endoreg_db/models/administration/person/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/employee_type.py +3 -1
- endoreg_db/models/administration/person/examiner/__init__.py +1 -1
- endoreg_db/models/administration/person/examiner/examiner.py +10 -2
- endoreg_db/models/administration/person/names/first_name.py +6 -4
- endoreg_db/models/administration/person/names/last_name.py +4 -3
- endoreg_db/models/administration/person/patient/__init__.py +1 -1
- endoreg_db/models/administration/person/patient/patient.py +0 -1
- endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
- endoreg_db/models/administration/person/person.py +1 -1
- endoreg_db/models/administration/product/__init__.py +7 -6
- endoreg_db/models/administration/product/product.py +6 -2
- endoreg_db/models/administration/product/product_group.py +9 -7
- endoreg_db/models/administration/product/product_material.py +9 -2
- endoreg_db/models/administration/product/reference_product.py +64 -15
- endoreg_db/models/administration/qualification/qualification.py +3 -1
- endoreg_db/models/administration/shift/shift.py +3 -1
- endoreg_db/models/administration/shift/shift_type.py +12 -4
- endoreg_db/models/aidataset/__init__.py +5 -0
- endoreg_db/models/aidataset/aidataset.py +193 -0
- endoreg_db/models/label/__init__.py +1 -1
- endoreg_db/models/label/label.py +10 -2
- endoreg_db/models/label/label_set.py +3 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
- endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
- endoreg_db/models/media/__init__.py +12 -5
- endoreg_db/models/media/frame/__init__.py +1 -1
- endoreg_db/models/media/frame/frame.py +34 -8
- endoreg_db/models/media/pdf/__init__.py +2 -1
- endoreg_db/models/media/pdf/raw_pdf.py +11 -4
- endoreg_db/models/media/pdf/report_file.py +6 -2
- endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
- endoreg_db/models/media/video/create_from_file.py +20 -41
- endoreg_db/models/media/video/pipe_1.py +75 -30
- endoreg_db/models/media/video/pipe_2.py +37 -12
- endoreg_db/models/media/video/video_file.py +36 -24
- endoreg_db/models/media/video/video_file_ai.py +235 -70
- endoreg_db/models/media/video/video_file_anonymize.py +240 -65
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
- endoreg_db/models/media/video/video_file_io.py +85 -33
- endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
- endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
- endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
- endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
- endoreg_db/models/media/video/video_file_segments.py +118 -27
- endoreg_db/models/media/video/video_metadata.py +25 -6
- endoreg_db/models/media/video/video_processing.py +54 -15
- endoreg_db/models/medical/__init__.py +3 -13
- endoreg_db/models/medical/contraindication/__init__.py +3 -1
- endoreg_db/models/medical/disease.py +18 -6
- endoreg_db/models/medical/event.py +6 -2
- endoreg_db/models/medical/examination/__init__.py +5 -1
- endoreg_db/models/medical/examination/examination.py +22 -6
- endoreg_db/models/medical/examination/examination_indication.py +23 -7
- endoreg_db/models/medical/examination/examination_time.py +6 -2
- endoreg_db/models/medical/finding/__init__.py +3 -1
- endoreg_db/models/medical/finding/finding.py +37 -12
- endoreg_db/models/medical/finding/finding_classification.py +27 -8
- endoreg_db/models/medical/finding/finding_intervention.py +19 -6
- endoreg_db/models/medical/finding/finding_type.py +3 -1
- endoreg_db/models/medical/hardware/__init__.py +1 -1
- endoreg_db/models/medical/hardware/endoscope.py +14 -2
- endoreg_db/models/medical/laboratory/__init__.py +1 -1
- endoreg_db/models/medical/laboratory/lab_value.py +139 -39
- endoreg_db/models/medical/medication/__init__.py +7 -3
- endoreg_db/models/medical/medication/medication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
- endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
- endoreg_db/models/medical/medication/medication_schedule.py +3 -1
- endoreg_db/models/medical/patient/__init__.py +2 -10
- endoreg_db/models/medical/patient/medication_examples.py +3 -14
- endoreg_db/models/medical/patient/patient_disease.py +17 -5
- endoreg_db/models/medical/patient/patient_event.py +12 -4
- endoreg_db/models/medical/patient/patient_examination.py +52 -15
- endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
- endoreg_db/models/medical/patient/patient_finding.py +105 -29
- endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
- endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
- endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
- endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
- endoreg_db/models/medical/patient/patient_medication.py +25 -7
- endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
- endoreg_db/models/metadata/model_meta.py +40 -12
- endoreg_db/models/metadata/model_meta_logic.py +51 -16
- endoreg_db/models/metadata/sensitive_meta.py +65 -28
- endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
- endoreg_db/models/metadata/video_meta.py +146 -39
- endoreg_db/models/metadata/video_prediction_logic.py +70 -21
- endoreg_db/models/metadata/video_prediction_meta.py +80 -27
- endoreg_db/models/operation_log.py +63 -0
- endoreg_db/models/other/__init__.py +10 -10
- endoreg_db/models/other/distribution/__init__.py +9 -7
- endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
- endoreg_db/models/other/emission/__init__.py +1 -1
- endoreg_db/models/other/emission/emission_factor.py +9 -3
- endoreg_db/models/other/information_source.py +15 -5
- endoreg_db/models/other/material.py +3 -1
- endoreg_db/models/other/transport_route.py +3 -1
- endoreg_db/models/other/unit.py +6 -2
- endoreg_db/models/report/report.py +0 -1
- endoreg_db/models/requirement/requirement.py +84 -27
- endoreg_db/models/requirement/requirement_error.py +5 -6
- endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
- endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
- endoreg_db/models/requirement/requirement_operator.py +28 -8
- endoreg_db/models/requirement/requirement_set.py +34 -11
- endoreg_db/models/state/__init__.py +1 -0
- endoreg_db/models/state/audit_ledger.py +9 -2
- endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
- endoreg_db/models/state/processing_history/processing_history.py +136 -0
- endoreg_db/models/state/raw_pdf.py +0 -1
- endoreg_db/models/state/video.py +2 -4
- endoreg_db/models/utils.py +4 -2
- endoreg_db/queries/__init__.py +2 -6
- endoreg_db/queries/annotations/__init__.py +1 -3
- endoreg_db/queries/annotations/legacy.py +37 -26
- endoreg_db/root_urls.py +3 -4
- endoreg_db/schemas/examination_evaluation.py +3 -0
- endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
- endoreg_db/serializers/__init__.py +2 -8
- endoreg_db/serializers/administration/__init__.py +1 -2
- endoreg_db/serializers/administration/ai/__init__.py +0 -1
- endoreg_db/serializers/administration/ai/active_model.py +3 -1
- endoreg_db/serializers/administration/ai/ai_model.py +5 -3
- endoreg_db/serializers/administration/ai/model_type.py +3 -1
- endoreg_db/serializers/administration/center.py +7 -2
- endoreg_db/serializers/administration/gender.py +4 -2
- endoreg_db/serializers/anonymization.py +13 -13
- endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
- endoreg_db/serializers/examination/__init__.py +1 -1
- endoreg_db/serializers/examination/base.py +12 -13
- endoreg_db/serializers/examination/dropdown.py +6 -7
- endoreg_db/serializers/examination_serializer.py +3 -6
- endoreg_db/serializers/finding/__init__.py +1 -1
- endoreg_db/serializers/finding/finding.py +14 -7
- endoreg_db/serializers/finding_classification/__init__.py +3 -3
- endoreg_db/serializers/finding_classification/choice.py +3 -3
- endoreg_db/serializers/finding_classification/classification.py +2 -4
- endoreg_db/serializers/label_video_segment/__init__.py +5 -3
- endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
- endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
- endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
- endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
- endoreg_db/serializers/meta/__init__.py +1 -2
- endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
- endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
- endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
- endoreg_db/serializers/misc/__init__.py +2 -2
- endoreg_db/serializers/misc/file_overview.py +11 -7
- endoreg_db/serializers/misc/stats.py +10 -8
- endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
- endoreg_db/serializers/misc/upload_job.py +32 -29
- endoreg_db/serializers/patient/__init__.py +2 -1
- endoreg_db/serializers/patient/patient.py +32 -15
- endoreg_db/serializers/patient/patient_dropdown.py +11 -3
- endoreg_db/serializers/patient_examination/__init__.py +1 -1
- endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
- endoreg_db/serializers/patient_finding/__init__.py +1 -1
- endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
- endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
- endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
- endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
- endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
- endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
- endoreg_db/serializers/pdf/__init__.py +1 -3
- endoreg_db/serializers/requirements/requirement_schema.py +1 -6
- endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
- endoreg_db/serializers/video/__init__.py +2 -2
- endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
- endoreg_db/serializers/video/video_file_brief.py +6 -2
- endoreg_db/serializers/video/video_file_detail.py +36 -23
- endoreg_db/serializers/video/video_file_list.py +4 -2
- endoreg_db/serializers/video/video_processing_history.py +54 -50
- endoreg_db/services/__init__.py +1 -1
- endoreg_db/services/anonymization.py +2 -2
- endoreg_db/services/examination_evaluation.py +40 -17
- endoreg_db/services/model_meta_from_hf.py +76 -0
- endoreg_db/services/polling_coordinator.py +101 -70
- endoreg_db/services/pseudonym_service.py +27 -22
- endoreg_db/services/report_import.py +6 -3
- endoreg_db/services/segment_sync.py +75 -59
- endoreg_db/services/video_import.py +6 -7
- endoreg_db/urls/__init__.py +2 -2
- endoreg_db/urls/ai.py +7 -25
- endoreg_db/urls/anonymization.py +61 -15
- endoreg_db/urls/auth.py +4 -4
- endoreg_db/urls/classification.py +4 -9
- endoreg_db/urls/examination.py +27 -18
- endoreg_db/urls/media.py +27 -34
- endoreg_db/urls/patient.py +11 -7
- endoreg_db/urls/requirements.py +3 -1
- endoreg_db/urls/root_urls.py +2 -3
- endoreg_db/urls/stats.py +24 -16
- endoreg_db/urls/upload.py +3 -11
- endoreg_db/utils/__init__.py +14 -15
- endoreg_db/utils/ai/__init__.py +1 -1
- endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
- endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
- endoreg_db/utils/ai/get.py +2 -1
- endoreg_db/utils/ai/inference_dataset.py +14 -15
- endoreg_db/utils/ai/model_training/config.py +117 -0
- endoreg_db/utils/ai/model_training/dataset.py +74 -0
- endoreg_db/utils/ai/model_training/losses.py +68 -0
- endoreg_db/utils/ai/model_training/metrics.py +78 -0
- endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
- endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
- endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
- endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
- endoreg_db/utils/ai/predict.py +4 -4
- endoreg_db/utils/ai/preprocess.py +19 -11
- endoreg_db/utils/calc_duration_seconds.py +4 -4
- endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
- endoreg_db/utils/check_video_files.py +74 -47
- endoreg_db/utils/cropping.py +10 -9
- endoreg_db/utils/dataloader.py +11 -3
- endoreg_db/utils/dates.py +3 -4
- endoreg_db/utils/defaults/set_default_center.py +7 -6
- endoreg_db/utils/env.py +6 -2
- endoreg_db/utils/extract_specific_frames.py +24 -9
- endoreg_db/utils/file_operations.py +30 -18
- endoreg_db/utils/fix_video_path_direct.py +57 -41
- endoreg_db/utils/frame_anonymization_utils.py +157 -157
- endoreg_db/utils/hashs.py +3 -18
- endoreg_db/utils/links/requirement_link.py +96 -52
- endoreg_db/utils/ocr.py +30 -25
- endoreg_db/utils/operation_log.py +61 -0
- endoreg_db/utils/parse_and_generate_yaml.py +12 -13
- endoreg_db/utils/paths.py +6 -6
- endoreg_db/utils/permissions.py +40 -24
- endoreg_db/utils/pipelines/process_video_dir.py +50 -26
- endoreg_db/utils/product/sum_emissions.py +5 -3
- endoreg_db/utils/product/sum_weights.py +4 -2
- endoreg_db/utils/pydantic_models/__init__.py +3 -4
- endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
- endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
- endoreg_db/utils/setup_config.py +21 -5
- endoreg_db/utils/storage.py +3 -1
- endoreg_db/utils/translation.py +19 -15
- endoreg_db/utils/uuid.py +1 -0
- endoreg_db/utils/validate_endo_roi.py +12 -4
- endoreg_db/utils/validate_subcategory_dict.py +26 -24
- endoreg_db/utils/validate_video_detailed.py +207 -149
- endoreg_db/utils/video/__init__.py +7 -3
- endoreg_db/utils/video/extract_frames.py +30 -18
- endoreg_db/utils/video/names.py +11 -6
- endoreg_db/utils/video/streaming_processor.py +175 -101
- endoreg_db/utils/video/video_splitter.py +30 -19
- endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
- endoreg_db/views/__init__.py +0 -20
- endoreg_db/views/anonymization/__init__.py +6 -2
- endoreg_db/views/anonymization/media_management.py +2 -6
- endoreg_db/views/anonymization/overview.py +34 -1
- endoreg_db/views/anonymization/validate.py +79 -18
- endoreg_db/views/auth/__init__.py +1 -1
- endoreg_db/views/auth/keycloak.py +16 -14
- endoreg_db/views/examination/__init__.py +12 -15
- endoreg_db/views/examination/examination.py +5 -5
- endoreg_db/views/examination/examination_manifest_cache.py +5 -5
- endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
- endoreg_db/views/examination/get_finding_classifications.py +9 -7
- endoreg_db/views/examination/get_findings.py +8 -10
- endoreg_db/views/examination/get_instruments.py +3 -2
- endoreg_db/views/examination/get_interventions.py +1 -1
- endoreg_db/views/finding/__init__.py +2 -2
- endoreg_db/views/finding/finding.py +58 -54
- endoreg_db/views/finding/get_classifications.py +1 -1
- endoreg_db/views/finding/get_interventions.py +1 -1
- endoreg_db/views/finding_classification/__init__.py +5 -5
- endoreg_db/views/finding_classification/finding_classification.py +5 -6
- endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
- endoreg_db/views/media/__init__.py +13 -13
- endoreg_db/views/media/pdf_media.py +9 -9
- endoreg_db/views/media/sensitive_metadata.py +10 -7
- endoreg_db/views/media/video_media.py +4 -4
- endoreg_db/views/meta/__init__.py +1 -1
- endoreg_db/views/meta/sensitive_meta_list.py +20 -22
- endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
- endoreg_db/views/misc/__init__.py +6 -34
- endoreg_db/views/misc/center.py +2 -1
- endoreg_db/views/misc/csrf.py +2 -1
- endoreg_db/views/misc/gender.py +2 -1
- endoreg_db/views/misc/stats.py +141 -106
- endoreg_db/views/patient/__init__.py +1 -3
- endoreg_db/views/patient/patient.py +141 -99
- endoreg_db/views/patient_examination/__init__.py +5 -5
- endoreg_db/views/patient_examination/patient_examination.py +43 -42
- endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
- endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
- endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
- endoreg_db/views/patient_examination/video.py +114 -80
- endoreg_db/views/patient_finding/__init__.py +1 -1
- endoreg_db/views/patient_finding/patient_finding.py +17 -10
- endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
- endoreg_db/views/patient_finding_classification/__init__.py +1 -1
- endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
- endoreg_db/views/report/reimport.py +1 -1
- endoreg_db/views/report/report_stream.py +5 -8
- endoreg_db/views/requirement/__init__.py +2 -1
- endoreg_db/views/requirement/evaluate.py +7 -9
- endoreg_db/views/requirement/lookup.py +2 -3
- endoreg_db/views/requirement/lookup_store.py +0 -1
- endoreg_db/views/requirement/requirement_utils.py +2 -4
- endoreg_db/views/stats/__init__.py +4 -4
- endoreg_db/views/stats/stats_views.py +152 -115
- endoreg_db/views/video/__init__.py +18 -27
- endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
- endoreg_db/views/{ai → video/ai}/label.py +20 -16
- endoreg_db/views/video/correction.py +5 -6
- endoreg_db/views/video/reimport.py +134 -99
- endoreg_db/views/video/segments_crud.py +134 -44
- endoreg_db/views/video/video_apply_mask.py +13 -12
- endoreg_db/views/video/video_correction.py +2 -1
- endoreg_db/views/video/video_download_processed.py +15 -15
- endoreg_db/views/video/video_meta_stats.py +7 -6
- endoreg_db/views/video/video_processing_history.py +3 -2
- endoreg_db/views/video/video_remove_frames.py +13 -12
- endoreg_db/views/video/video_stream.py +110 -82
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +434 -431
- endoreg_db/management/commands/import_fallback_video.py +0 -203
- endoreg_db/management/commands/import_video.py +0 -422
- endoreg_db/management/commands/import_video_with_classification.py +0 -367
- endoreg_db/models/media/processing_history/processing_history.py +0 -96
- endoreg_db/serializers/label/__init__.py +0 -7
- endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
- endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
- endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
- endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
- endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
- endoreg_db/services/__old/pdf_import.py +0 -1487
- endoreg_db/services/__old/video_import.py +0 -1306
- endoreg_db/tasks/upload_tasks.py +0 -216
- endoreg_db/tasks/video_ingest.py +0 -161
- endoreg_db/tasks/video_processing_tasks.py +0 -327
- endoreg_db/views/misc/translation.py +0 -182
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,9 +1,14 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING,
|
|
1
|
+
from typing import TYPE_CHECKING, cast
|
|
2
2
|
|
|
3
3
|
from django.db import models
|
|
4
4
|
|
|
5
5
|
if TYPE_CHECKING:
|
|
6
|
-
from endoreg_db.models import
|
|
6
|
+
from endoreg_db.models import (
|
|
7
|
+
Examination,
|
|
8
|
+
FindingIntervention,
|
|
9
|
+
InformationSource,
|
|
10
|
+
Requirement,
|
|
11
|
+
)
|
|
7
12
|
from endoreg_db.utils.links.requirement_link import RequirementLinks
|
|
8
13
|
|
|
9
14
|
|
|
@@ -60,12 +65,21 @@ class ExaminationIndication(models.Model):
|
|
|
60
65
|
objects = ExaminationIndicationManager()
|
|
61
66
|
|
|
62
67
|
if TYPE_CHECKING:
|
|
63
|
-
classifications = cast(
|
|
64
|
-
|
|
65
|
-
|
|
68
|
+
classifications = cast(
|
|
69
|
+
models.manager.RelatedManager["ExaminationIndicationClassification"],
|
|
70
|
+
classifications,
|
|
71
|
+
)
|
|
72
|
+
expected_interventions = cast(
|
|
73
|
+
models.manager.RelatedManager["FindingIntervention"], expected_interventions
|
|
74
|
+
)
|
|
75
|
+
information_sources = cast(
|
|
76
|
+
models.manager.RelatedManager["InformationSource"], information_sources
|
|
77
|
+
)
|
|
66
78
|
|
|
67
79
|
@property
|
|
68
|
-
def related_requirements(
|
|
80
|
+
def related_requirements(
|
|
81
|
+
self,
|
|
82
|
+
) -> "models.manager.RelatedManager[Requirement]": ...
|
|
69
83
|
|
|
70
84
|
@property
|
|
71
85
|
def examinations(self) -> "models.manager.RelatedManager[Examination]": ...
|
|
@@ -164,7 +178,9 @@ class ExaminationIndicationClassificationChoiceManager(models.Manager):
|
|
|
164
178
|
Manager for ExaminationIndicationClassificationChoice with custom query methods.
|
|
165
179
|
"""
|
|
166
180
|
|
|
167
|
-
def get_by_natural_key(
|
|
181
|
+
def get_by_natural_key(
|
|
182
|
+
self, name: str
|
|
183
|
+
) -> "ExaminationIndicationClassificationChoice":
|
|
168
184
|
"""
|
|
169
185
|
Retrieves an ExaminationIndicationClassificationChoice instance by its natural key.
|
|
170
186
|
|
|
@@ -37,8 +37,12 @@ class ExaminationTime(models.Model):
|
|
|
37
37
|
)
|
|
38
38
|
|
|
39
39
|
if TYPE_CHECKING:
|
|
40
|
-
time_types = cast(
|
|
41
|
-
|
|
40
|
+
time_types = cast(
|
|
41
|
+
models.manager.RelatedManager["ExaminationTimeType"], time_types
|
|
42
|
+
)
|
|
43
|
+
information_sources = cast(
|
|
44
|
+
models.manager.RelatedManager["InformationSource"], information_sources
|
|
45
|
+
)
|
|
42
46
|
|
|
43
47
|
def __str__(self) -> str:
|
|
44
48
|
"""
|
|
@@ -2,7 +2,9 @@ from .finding import Finding
|
|
|
2
2
|
from .finding_type import FindingType
|
|
3
3
|
|
|
4
4
|
from .finding_classification import (
|
|
5
|
-
FindingClassificationType,
|
|
5
|
+
FindingClassificationType,
|
|
6
|
+
FindingClassification,
|
|
7
|
+
FindingClassificationChoice,
|
|
6
8
|
)
|
|
7
9
|
|
|
8
10
|
from .finding_intervention import FindingIntervention, FindingInterventionType
|
|
@@ -12,11 +12,21 @@ class FindingManager(models.Manager):
|
|
|
12
12
|
class Finding(models.Model):
|
|
13
13
|
name = models.CharField(max_length=100, unique=True)
|
|
14
14
|
description = models.TextField(blank=True, null=True)
|
|
15
|
-
finding_types = models.ManyToManyField(
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
15
|
+
finding_types = models.ManyToManyField(
|
|
16
|
+
"FindingType", blank=True, related_name="findings"
|
|
17
|
+
)
|
|
18
|
+
finding_interventions = models.ManyToManyField(
|
|
19
|
+
"FindingIntervention", blank=True, related_name="findings"
|
|
20
|
+
)
|
|
21
|
+
caused_by_interventions = models.ManyToManyField(
|
|
22
|
+
"FindingIntervention", blank=True, related_name="causes_findings"
|
|
23
|
+
)
|
|
24
|
+
finding_classifications = models.ManyToManyField(
|
|
25
|
+
"FindingClassification", blank=True, related_name="findings"
|
|
26
|
+
)
|
|
27
|
+
information_sources = models.ManyToManyField(
|
|
28
|
+
"InformationSource", blank=True, related_name="findings"
|
|
29
|
+
)
|
|
20
30
|
objects = FindingManager()
|
|
21
31
|
|
|
22
32
|
if TYPE_CHECKING:
|
|
@@ -30,9 +40,16 @@ class Finding(models.Model):
|
|
|
30
40
|
PatientFindingClassification,
|
|
31
41
|
)
|
|
32
42
|
|
|
33
|
-
finding_types = cast(
|
|
34
|
-
|
|
35
|
-
|
|
43
|
+
finding_types = cast(
|
|
44
|
+
models.manager.RelatedManager["FindingType"], finding_types
|
|
45
|
+
)
|
|
46
|
+
finding_interventions = cast(
|
|
47
|
+
models.manager.RelatedManager["FindingIntervention"], finding_interventions
|
|
48
|
+
)
|
|
49
|
+
finding_classifications = cast(
|
|
50
|
+
models.manager.RelatedManager["FindingClassification"],
|
|
51
|
+
finding_classifications,
|
|
52
|
+
)
|
|
36
53
|
|
|
37
54
|
def natural_key(self):
|
|
38
55
|
"""
|
|
@@ -55,7 +72,9 @@ class Finding(models.Model):
|
|
|
55
72
|
"""
|
|
56
73
|
return self.finding_types.all()
|
|
57
74
|
|
|
58
|
-
def get_classifications(
|
|
75
|
+
def get_classifications(
|
|
76
|
+
self, classification_type: Optional[str] = None
|
|
77
|
+
) -> models.QuerySet["FindingClassification"]:
|
|
59
78
|
"""
|
|
60
79
|
Retrieve all classifications associated with this finding, optionally filtered by classification type.
|
|
61
80
|
|
|
@@ -66,7 +85,9 @@ class Finding(models.Model):
|
|
|
66
85
|
List[FindingClassification]: List of related classification objects, filtered by type if specified.
|
|
67
86
|
"""
|
|
68
87
|
if classification_type:
|
|
69
|
-
return self.finding_classifications.filter(
|
|
88
|
+
return self.finding_classifications.filter(
|
|
89
|
+
classification_types__name=classification_type
|
|
90
|
+
)
|
|
70
91
|
return self.finding_classifications.all()
|
|
71
92
|
|
|
72
93
|
def get_location_classifications(self):
|
|
@@ -76,7 +97,9 @@ class Finding(models.Model):
|
|
|
76
97
|
Returns:
|
|
77
98
|
QuerySet: All FindingClassification instances linked to this finding where the classification type name is 'location' (case-insensitive).
|
|
78
99
|
"""
|
|
79
|
-
return self.finding_classifications.filter(
|
|
100
|
+
return self.finding_classifications.filter(
|
|
101
|
+
classification_types__name__iexact="location"
|
|
102
|
+
)
|
|
80
103
|
|
|
81
104
|
def get_morphology_classifications(self):
|
|
82
105
|
"""
|
|
@@ -85,4 +108,6 @@ class Finding(models.Model):
|
|
|
85
108
|
Returns:
|
|
86
109
|
QuerySet: A queryset of FindingClassification instances associated with this finding and classified as 'morphology'.
|
|
87
110
|
"""
|
|
88
|
-
return self.finding_classifications.filter(
|
|
111
|
+
return self.finding_classifications.filter(
|
|
112
|
+
classification_types__name__iexact="morphology"
|
|
113
|
+
)
|
|
@@ -28,8 +28,12 @@ class FindingClassificationManager(models.Manager):
|
|
|
28
28
|
class FindingClassification(models.Model):
|
|
29
29
|
name = models.CharField(max_length=255, unique=True)
|
|
30
30
|
description = models.TextField(blank=True)
|
|
31
|
-
finding_types = models.ManyToManyField(
|
|
32
|
-
|
|
31
|
+
finding_types = models.ManyToManyField(
|
|
32
|
+
"FindingType", blank=True, related_name="finding_classifications"
|
|
33
|
+
)
|
|
34
|
+
choices = models.ManyToManyField(
|
|
35
|
+
"FindingClassificationChoice", related_name="classifications", blank=True
|
|
36
|
+
)
|
|
33
37
|
|
|
34
38
|
classification_types = models.ManyToManyField(
|
|
35
39
|
to=FindingClassificationType,
|
|
@@ -50,12 +54,27 @@ class FindingClassification(models.Model):
|
|
|
50
54
|
objects = FindingClassificationManager()
|
|
51
55
|
|
|
52
56
|
if TYPE_CHECKING:
|
|
53
|
-
from endoreg_db.models import
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
57
|
+
from endoreg_db.models import (
|
|
58
|
+
Examination,
|
|
59
|
+
Finding,
|
|
60
|
+
FindingType,
|
|
61
|
+
InformationSource,
|
|
62
|
+
PatientFindingClassification,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
classification_types = cast(
|
|
66
|
+
models.manager.RelatedManager["FindingClassificationType"],
|
|
67
|
+
classification_types,
|
|
68
|
+
)
|
|
69
|
+
choices = cast(
|
|
70
|
+
models.manager.RelatedManager["FindingClassificationChoice"], choices
|
|
71
|
+
)
|
|
72
|
+
finding_types = cast(
|
|
73
|
+
models.manager.RelatedManager["FindingType"], finding_types
|
|
74
|
+
)
|
|
75
|
+
information_sources = cast(
|
|
76
|
+
models.manager.RelatedManager["InformationSource"], information_sources
|
|
77
|
+
)
|
|
59
78
|
|
|
60
79
|
@property
|
|
61
80
|
def findings(self) -> "models.manager.RelatedManager[Finding]": ...
|
|
@@ -11,7 +11,9 @@ class FindingInterventionManager(models.Manager):
|
|
|
11
11
|
class FindingIntervention(models.Model):
|
|
12
12
|
name = models.CharField(max_length=100, unique=True)
|
|
13
13
|
description = models.TextField(blank=True, null=True)
|
|
14
|
-
intervention_types = models.ManyToManyField(
|
|
14
|
+
intervention_types = models.ManyToManyField(
|
|
15
|
+
"FindingInterventionType", blank=True, related_name="interventions"
|
|
16
|
+
)
|
|
15
17
|
information_sources = models.ManyToManyField(
|
|
16
18
|
"InformationSource",
|
|
17
19
|
related_name="finding_interventions",
|
|
@@ -20,10 +22,19 @@ class FindingIntervention(models.Model):
|
|
|
20
22
|
objects = FindingInterventionManager()
|
|
21
23
|
|
|
22
24
|
if TYPE_CHECKING:
|
|
23
|
-
from endoreg_db.models import
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
from endoreg_db.models import (
|
|
26
|
+
Contraindication,
|
|
27
|
+
FindingInterventionType,
|
|
28
|
+
InformationSource,
|
|
29
|
+
LabValue,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
intervention_types = cast(
|
|
33
|
+
models.manager.RelatedManager["FindingInterventionType"], intervention_types
|
|
34
|
+
)
|
|
35
|
+
information_sources = cast(
|
|
36
|
+
models.manager.RelatedManager["InformationSource"], information_sources
|
|
37
|
+
)
|
|
27
38
|
|
|
28
39
|
def natural_key(self):
|
|
29
40
|
return (self.name,)
|
|
@@ -46,7 +57,9 @@ class FindingInterventionType(models.Model):
|
|
|
46
57
|
if TYPE_CHECKING:
|
|
47
58
|
|
|
48
59
|
@property
|
|
49
|
-
def interventions(
|
|
60
|
+
def interventions(
|
|
61
|
+
self,
|
|
62
|
+
) -> "models.manager.RelatedManager[FindingIntervention]": ...
|
|
50
63
|
|
|
51
64
|
def natural_key(self):
|
|
52
65
|
return (self.name,)
|
|
@@ -27,7 +27,9 @@ class FindingType(models.Model):
|
|
|
27
27
|
from endoreg_db.models import Examination, Finding, FindingClassification
|
|
28
28
|
|
|
29
29
|
@property
|
|
30
|
-
def finding_classifications(
|
|
30
|
+
def finding_classifications(
|
|
31
|
+
self,
|
|
32
|
+
) -> "models.manager.RelatedManager[FindingClassification]": ...
|
|
31
33
|
|
|
32
34
|
def natural_key(self):
|
|
33
35
|
return (self.name,)
|
|
@@ -13,8 +13,20 @@ class Endoscope(models.Model):
|
|
|
13
13
|
|
|
14
14
|
name = models.CharField(max_length=255)
|
|
15
15
|
sn = models.CharField(max_length=255)
|
|
16
|
-
center = models.ForeignKey(
|
|
17
|
-
|
|
16
|
+
center = models.ForeignKey(
|
|
17
|
+
"Center",
|
|
18
|
+
blank=True,
|
|
19
|
+
null=True,
|
|
20
|
+
on_delete=models.CASCADE,
|
|
21
|
+
related_name="endoscopes",
|
|
22
|
+
)
|
|
23
|
+
endoscope_type = models.ForeignKey(
|
|
24
|
+
"EndoscopeType",
|
|
25
|
+
blank=True,
|
|
26
|
+
null=True,
|
|
27
|
+
on_delete=models.CASCADE,
|
|
28
|
+
related_name="endoscopes",
|
|
29
|
+
)
|
|
18
30
|
|
|
19
31
|
if TYPE_CHECKING:
|
|
20
32
|
from endoreg_db.models import Center
|
|
@@ -56,7 +56,9 @@ class LabValueManager(models.Manager):
|
|
|
56
56
|
class LabValue(models.Model):
|
|
57
57
|
name = models.CharField(max_length=255, unique=True)
|
|
58
58
|
abbreviation = models.CharField(max_length=10, blank=True, null=True)
|
|
59
|
-
default_unit = models.ForeignKey(
|
|
59
|
+
default_unit = models.ForeignKey(
|
|
60
|
+
"Unit", on_delete=models.CASCADE, blank=True, null=True
|
|
61
|
+
)
|
|
60
62
|
numeric_precision = models.IntegerField(default=3)
|
|
61
63
|
default_single_categorical_value_distribution = models.ForeignKey(
|
|
62
64
|
"SingleCategoricalValueDistribution",
|
|
@@ -91,15 +93,22 @@ class LabValue(models.Model):
|
|
|
91
93
|
normal_range_gender_dependent = models.BooleanField(default=False)
|
|
92
94
|
normal_range_special_case = models.BooleanField(default=False)
|
|
93
95
|
bound_adjustment_factor = models.FloatField(
|
|
94
|
-
default=0.1,
|
|
96
|
+
default=0.1,
|
|
97
|
+
help_text="Factor for adjusting bounds when generating increased/decreased values, e.g., 0.1 for 10%.",
|
|
95
98
|
)
|
|
96
99
|
objects = LabValueManager()
|
|
97
100
|
|
|
98
101
|
if TYPE_CHECKING:
|
|
99
102
|
default_unit: models.ForeignKey["Unit|None"]
|
|
100
|
-
default_single_categorical_value_distribution: models.ForeignKey[
|
|
101
|
-
|
|
102
|
-
|
|
103
|
+
default_single_categorical_value_distribution: models.ForeignKey[
|
|
104
|
+
"SingleCategoricalValueDistribution|None"
|
|
105
|
+
]
|
|
106
|
+
default_numerical_value_distribution: models.ForeignKey[
|
|
107
|
+
"NumericValueDistribution|None"
|
|
108
|
+
]
|
|
109
|
+
default_multiple_categorical_value_distribution: models.ForeignKey[
|
|
110
|
+
"MultipleCategoricalValueDistribution|None"
|
|
111
|
+
]
|
|
103
112
|
default_date_value_distribution: models.ForeignKey["DateValueDistribution|None"]
|
|
104
113
|
|
|
105
114
|
@classmethod
|
|
@@ -158,7 +167,9 @@ class LabValue(models.Model):
|
|
|
158
167
|
warnings.warn("No default distribution set for lab value")
|
|
159
168
|
return None
|
|
160
169
|
|
|
161
|
-
def get_normal_range(
|
|
170
|
+
def get_normal_range(
|
|
171
|
+
self, age: Optional[int] = None, gender: Optional["Gender"] = None
|
|
172
|
+
):
|
|
162
173
|
"""
|
|
163
174
|
Returns the normal range for this lab value, considering age and gender dependencies.
|
|
164
175
|
|
|
@@ -183,11 +194,15 @@ class LabValue(models.Model):
|
|
|
183
194
|
gender_name_to_use = gender.name
|
|
184
195
|
if gender_name_to_use not in current_range_source:
|
|
185
196
|
warnings.warn(
|
|
186
|
-
f"Normal range for gender '{gender_name_to_use}' not found for LabValue '{self.name}'. Defaulting to 'male' range.",
|
|
197
|
+
f"Normal range for gender '{gender_name_to_use}' not found for LabValue '{self.name}'. Defaulting to 'male' range.",
|
|
198
|
+
UserWarning,
|
|
187
199
|
)
|
|
188
200
|
gender_name_to_use = "male"
|
|
189
201
|
else:
|
|
190
|
-
warnings.warn(
|
|
202
|
+
warnings.warn(
|
|
203
|
+
f"Gender not provided for gender-dependent LabValue '{self.name}'. Defaulting to 'male' range.",
|
|
204
|
+
UserWarning,
|
|
205
|
+
)
|
|
191
206
|
gender_name_to_use = "male"
|
|
192
207
|
|
|
193
208
|
# Attempt gender-specific lookup
|
|
@@ -202,17 +217,23 @@ class LabValue(models.Model):
|
|
|
202
217
|
)
|
|
203
218
|
|
|
204
219
|
# Fallback to general min/max if needed
|
|
205
|
-
if (min_value is None or max_value is None) and isinstance(
|
|
220
|
+
if (min_value is None or max_value is None) and isinstance(
|
|
221
|
+
current_range_source, dict
|
|
222
|
+
):
|
|
206
223
|
if min_value is None:
|
|
207
224
|
min_value = current_range_source.get("min")
|
|
208
225
|
if max_value is None:
|
|
209
226
|
max_value = current_range_source.get("max")
|
|
210
227
|
|
|
211
228
|
if age_dependent:
|
|
212
|
-
warnings.warn(
|
|
229
|
+
warnings.warn(
|
|
230
|
+
f"Age dependent normal range not implemented yet for LabValue '{self.name}'. Age: {age}."
|
|
231
|
+
)
|
|
213
232
|
|
|
214
233
|
if special_case:
|
|
215
|
-
warnings.warn(
|
|
234
|
+
warnings.warn(
|
|
235
|
+
f"Special case normal range not implemented yet for LabValue '{self.name}'."
|
|
236
|
+
)
|
|
216
237
|
|
|
217
238
|
# Final contextual warning
|
|
218
239
|
if min_value is None and max_value is None:
|
|
@@ -221,14 +242,20 @@ class LabValue(models.Model):
|
|
|
221
242
|
if min_value is None:
|
|
222
243
|
context_parts = []
|
|
223
244
|
if gender_dependent:
|
|
224
|
-
gender_repr =
|
|
245
|
+
gender_repr = (
|
|
246
|
+
gender.name if gender and hasattr(gender, "name") else "None"
|
|
247
|
+
)
|
|
225
248
|
if gender_name_to_use and gender_name_to_use != gender_repr:
|
|
226
|
-
gender_repr =
|
|
249
|
+
gender_repr = (
|
|
250
|
+
f"{gender_repr} (lookup attempted for: {gender_name_to_use})"
|
|
251
|
+
)
|
|
227
252
|
context_parts.append(f"gender: {gender_repr}")
|
|
228
253
|
if age_dependent:
|
|
229
254
|
context_parts.append(f"age: {age}")
|
|
230
255
|
|
|
231
|
-
warning_message =
|
|
256
|
+
warning_message = (
|
|
257
|
+
f"Could not determine a 'min' normal range for LabValue '{self.name}'"
|
|
258
|
+
)
|
|
232
259
|
if context_parts:
|
|
233
260
|
warning_message += f" with context ({', '.join(context_parts)})."
|
|
234
261
|
else:
|
|
@@ -238,7 +265,9 @@ class LabValue(models.Model):
|
|
|
238
265
|
|
|
239
266
|
return {"min": min_value, "max": max_value}
|
|
240
267
|
|
|
241
|
-
def get_increased_value(
|
|
268
|
+
def get_increased_value(
|
|
269
|
+
self, patient: Optional["Patient"] = None
|
|
270
|
+
): # -> Any | None:
|
|
242
271
|
"""
|
|
243
272
|
Returns a value that is considered increased for this lab value.
|
|
244
273
|
It prioritizes sampling from a numerical distribution if available,
|
|
@@ -252,8 +281,14 @@ class LabValue(models.Model):
|
|
|
252
281
|
if self.default_numerical_value_distribution:
|
|
253
282
|
if patient:
|
|
254
283
|
# Attempt to sample above the upper bound, or a high value if no bound
|
|
255
|
-
for _ in range(
|
|
256
|
-
|
|
284
|
+
for _ in range(
|
|
285
|
+
10
|
|
286
|
+
): # Try a few times to get a value if bounds are restrictive
|
|
287
|
+
generated_value = (
|
|
288
|
+
self.default_numerical_value_distribution.generate_value(
|
|
289
|
+
lab_value=self, patient=patient
|
|
290
|
+
)
|
|
291
|
+
)
|
|
257
292
|
if upper_bound is not None:
|
|
258
293
|
if generated_value > upper_bound:
|
|
259
294
|
return generated_value
|
|
@@ -263,28 +298,50 @@ class LabValue(models.Model):
|
|
|
263
298
|
and hasattr(self.default_numerical_value_distribution, "stddev")
|
|
264
299
|
and self.default_numerical_value_distribution.mean is not None
|
|
265
300
|
and self.default_numerical_value_distribution.stddev is not None
|
|
266
|
-
and generated_value
|
|
301
|
+
and generated_value
|
|
302
|
+
> (
|
|
303
|
+
self.default_numerical_value_distribution.mean
|
|
304
|
+
+ self.default_numerical_value_distribution.stddev
|
|
305
|
+
)
|
|
267
306
|
):
|
|
268
307
|
return generated_value
|
|
269
308
|
# Fallback if sampling fails to produce a clearly increased value
|
|
270
309
|
if upper_bound is not None:
|
|
271
|
-
return upper_bound + (
|
|
310
|
+
return upper_bound + (
|
|
311
|
+
abs(upper_bound * self.bound_adjustment_factor)
|
|
312
|
+
if upper_bound != 0
|
|
313
|
+
else 1
|
|
314
|
+
) # Increase by factor or 1
|
|
272
315
|
# If no upper bound and sampling didn't provide a clear high value, return a generated value as last resort
|
|
273
|
-
return self.default_numerical_value_distribution.generate_value(
|
|
316
|
+
return self.default_numerical_value_distribution.generate_value(
|
|
317
|
+
lab_value=self, patient=patient
|
|
318
|
+
)
|
|
274
319
|
else: # No patient, cannot use distribution
|
|
275
320
|
warnings.warn(
|
|
276
321
|
f"Cannot use numerical distribution for {self.name} without patient context. Falling back to normal range logic for increased value."
|
|
277
322
|
)
|
|
278
323
|
if upper_bound is not None:
|
|
279
|
-
return upper_bound + (
|
|
324
|
+
return upper_bound + (
|
|
325
|
+
abs(upper_bound * self.bound_adjustment_factor)
|
|
326
|
+
if upper_bound != 0
|
|
327
|
+
else 1
|
|
328
|
+
)
|
|
280
329
|
else:
|
|
281
|
-
warnings.warn(
|
|
330
|
+
warnings.warn(
|
|
331
|
+
f"Cannot determine an increased value for {self.name} without an upper normal range or patient context for distribution."
|
|
332
|
+
)
|
|
282
333
|
return None
|
|
283
334
|
|
|
284
335
|
elif upper_bound is not None:
|
|
285
|
-
return upper_bound + (
|
|
336
|
+
return upper_bound + (
|
|
337
|
+
abs(upper_bound * self.bound_adjustment_factor)
|
|
338
|
+
if upper_bound != 0
|
|
339
|
+
else 1
|
|
340
|
+
)
|
|
286
341
|
else:
|
|
287
|
-
warnings.warn(
|
|
342
|
+
warnings.warn(
|
|
343
|
+
f"Cannot determine an increased value for {self.name} without a numerical distribution or an upper normal range."
|
|
344
|
+
)
|
|
288
345
|
return None
|
|
289
346
|
|
|
290
347
|
def get_normal_value(self, patient: Optional["Patient"] = None):
|
|
@@ -302,21 +359,33 @@ class LabValue(models.Model):
|
|
|
302
359
|
if self.default_numerical_value_distribution:
|
|
303
360
|
if patient:
|
|
304
361
|
for _ in range(10): # Try a few times
|
|
305
|
-
generated_value =
|
|
362
|
+
generated_value = (
|
|
363
|
+
self.default_numerical_value_distribution.generate_value(
|
|
364
|
+
lab_value=self, patient=patient
|
|
365
|
+
)
|
|
366
|
+
)
|
|
306
367
|
if lower_bound is not None and upper_bound is not None:
|
|
307
368
|
if lower_bound <= generated_value <= upper_bound:
|
|
308
369
|
return generated_value
|
|
309
|
-
elif
|
|
370
|
+
elif (
|
|
371
|
+
lower_bound is not None and generated_value >= lower_bound
|
|
372
|
+
): # No upper bound
|
|
310
373
|
return generated_value
|
|
311
|
-
elif
|
|
374
|
+
elif (
|
|
375
|
+
upper_bound is not None and generated_value <= upper_bound
|
|
376
|
+
): # No lower bound
|
|
312
377
|
return generated_value
|
|
313
|
-
elif
|
|
378
|
+
elif (
|
|
379
|
+
lower_bound is None and upper_bound is None
|
|
380
|
+
): # No range defined
|
|
314
381
|
return generated_value
|
|
315
382
|
# Fallback if sampling fails to produce a value in range
|
|
316
383
|
if lower_bound is not None and upper_bound is not None:
|
|
317
384
|
return (lower_bound + upper_bound) / 2.0
|
|
318
385
|
# Return any generated value as last resort
|
|
319
|
-
return self.default_numerical_value_distribution.generate_value(
|
|
386
|
+
return self.default_numerical_value_distribution.generate_value(
|
|
387
|
+
lab_value=self, patient=patient
|
|
388
|
+
)
|
|
320
389
|
else: # No patient, cannot use distribution
|
|
321
390
|
warnings.warn(
|
|
322
391
|
f"Cannot use numerical distribution for {self.name} without patient context. Falling back to normal range logic for normal value."
|
|
@@ -328,7 +397,10 @@ class LabValue(models.Model):
|
|
|
328
397
|
elif upper_bound is not None:
|
|
329
398
|
return upper_bound
|
|
330
399
|
else:
|
|
331
|
-
warnings.warn(
|
|
400
|
+
warnings.warn(
|
|
401
|
+
f"Cannot determine a normal value for {self.name} without a normal range or patient context for distribution.",
|
|
402
|
+
UserWarning,
|
|
403
|
+
)
|
|
332
404
|
return None
|
|
333
405
|
|
|
334
406
|
elif lower_bound is not None and upper_bound is not None:
|
|
@@ -338,7 +410,9 @@ class LabValue(models.Model):
|
|
|
338
410
|
elif upper_bound is not None: # Only max is defined
|
|
339
411
|
return upper_bound
|
|
340
412
|
else:
|
|
341
|
-
warnings.warn(
|
|
413
|
+
warnings.warn(
|
|
414
|
+
f"Cannot determine a normal value for {self.name} without a numerical distribution or a normal range."
|
|
415
|
+
)
|
|
342
416
|
return None
|
|
343
417
|
|
|
344
418
|
def get_decreased_value(self, patient: Optional["Patient"] = None):
|
|
@@ -355,7 +429,11 @@ class LabValue(models.Model):
|
|
|
355
429
|
if self.default_numerical_value_distribution:
|
|
356
430
|
if patient:
|
|
357
431
|
for _ in range(10): # Try a few times
|
|
358
|
-
generated_value =
|
|
432
|
+
generated_value = (
|
|
433
|
+
self.default_numerical_value_distribution.generate_value(
|
|
434
|
+
lab_value=self, patient=patient
|
|
435
|
+
)
|
|
436
|
+
)
|
|
359
437
|
if lower_bound is not None:
|
|
360
438
|
if generated_value < lower_bound:
|
|
361
439
|
return generated_value
|
|
@@ -365,26 +443,48 @@ class LabValue(models.Model):
|
|
|
365
443
|
and hasattr(self.default_numerical_value_distribution, "stddev")
|
|
366
444
|
and self.default_numerical_value_distribution.mean is not None
|
|
367
445
|
and self.default_numerical_value_distribution.stddev is not None
|
|
368
|
-
and generated_value
|
|
446
|
+
and generated_value
|
|
447
|
+
< (
|
|
448
|
+
self.default_numerical_value_distribution.mean
|
|
449
|
+
- self.default_numerical_value_distribution.stddev
|
|
450
|
+
)
|
|
369
451
|
):
|
|
370
452
|
return generated_value
|
|
371
453
|
# Fallback
|
|
372
454
|
if lower_bound is not None:
|
|
373
|
-
return lower_bound - (
|
|
455
|
+
return lower_bound - (
|
|
456
|
+
abs(lower_bound * self.bound_adjustment_factor)
|
|
457
|
+
if lower_bound != 0
|
|
458
|
+
else 1
|
|
459
|
+
) # Decrease by factor or 1
|
|
374
460
|
# Return any generated value as last resort
|
|
375
|
-
return self.default_numerical_value_distribution.generate_value(
|
|
461
|
+
return self.default_numerical_value_distribution.generate_value(
|
|
462
|
+
lab_value=self, patient=patient
|
|
463
|
+
)
|
|
376
464
|
else: # No patient, cannot use distribution
|
|
377
465
|
warnings.warn(
|
|
378
466
|
f"Cannot use numerical distribution for {self.name} without patient context. Falling back to normal range logic for decreased value."
|
|
379
467
|
)
|
|
380
468
|
if lower_bound is not None:
|
|
381
|
-
return lower_bound - (
|
|
469
|
+
return lower_bound - (
|
|
470
|
+
abs(lower_bound * self.bound_adjustment_factor)
|
|
471
|
+
if lower_bound != 0
|
|
472
|
+
else 1
|
|
473
|
+
)
|
|
382
474
|
else:
|
|
383
|
-
warnings.warn(
|
|
475
|
+
warnings.warn(
|
|
476
|
+
f"Cannot determine a decreased value for {self.name} without a lower normal range or patient context for distribution."
|
|
477
|
+
)
|
|
384
478
|
return None
|
|
385
479
|
|
|
386
480
|
elif lower_bound is not None:
|
|
387
|
-
return lower_bound - (
|
|
481
|
+
return lower_bound - (
|
|
482
|
+
abs(lower_bound * self.bound_adjustment_factor)
|
|
483
|
+
if lower_bound != 0
|
|
484
|
+
else 1
|
|
485
|
+
)
|
|
388
486
|
else:
|
|
389
|
-
warnings.warn(
|
|
487
|
+
warnings.warn(
|
|
488
|
+
f"Cannot determine a decreased value for {self.name} without a numerical distribution or a lower normal range."
|
|
489
|
+
)
|
|
390
490
|
return None
|