endoreg-db 0.8.8.9__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 +146 -83
- 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/migrations/__init__.py +0 -0
- 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 -3
- 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/ffmpeg_wrapper.py +217 -52
- 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.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
- {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +436 -433
- endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +0 -119
- 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.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.8.9.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -16,32 +16,62 @@ if TYPE_CHECKING:
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class PatientFinding(models.Model):
|
|
19
|
-
patient_examination = models.ForeignKey(
|
|
20
|
-
|
|
19
|
+
patient_examination = models.ForeignKey(
|
|
20
|
+
"PatientExamination", on_delete=models.CASCADE, related_name="patient_findings"
|
|
21
|
+
)
|
|
22
|
+
finding = models.ForeignKey(
|
|
23
|
+
"Finding", on_delete=models.CASCADE, related_name="finding_patient_findings"
|
|
24
|
+
)
|
|
21
25
|
|
|
22
26
|
# Audit-Felder für medizinische Nachverfolgung
|
|
23
27
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
24
28
|
updated_at = models.DateTimeField(auto_now=True)
|
|
25
|
-
created_by = models.ForeignKey(
|
|
26
|
-
|
|
29
|
+
created_by = models.ForeignKey(
|
|
30
|
+
"auth.User",
|
|
31
|
+
on_delete=models.PROTECT,
|
|
32
|
+
related_name="created_findings",
|
|
33
|
+
null=True,
|
|
34
|
+
blank=True,
|
|
35
|
+
)
|
|
36
|
+
updated_by = models.ForeignKey(
|
|
37
|
+
"auth.User",
|
|
38
|
+
on_delete=models.PROTECT,
|
|
39
|
+
related_name="updated_findings",
|
|
40
|
+
null=True,
|
|
41
|
+
blank=True,
|
|
42
|
+
)
|
|
27
43
|
|
|
28
44
|
# Soft Delete für historische Daten
|
|
29
|
-
is_active = models.BooleanField(
|
|
45
|
+
is_active = models.BooleanField(
|
|
46
|
+
default=True, help_text="Deaktiviert statt gelöscht für Audit-Trail"
|
|
47
|
+
)
|
|
30
48
|
deactivated_at = models.DateTimeField(null=True, blank=True)
|
|
31
|
-
deactivated_by = models.ForeignKey(
|
|
49
|
+
deactivated_by = models.ForeignKey(
|
|
50
|
+
"auth.User",
|
|
51
|
+
on_delete=models.PROTECT,
|
|
52
|
+
related_name="deactivated_findings",
|
|
53
|
+
null=True,
|
|
54
|
+
blank=True,
|
|
55
|
+
)
|
|
32
56
|
|
|
33
57
|
if TYPE_CHECKING:
|
|
34
58
|
patient_examination: models.ForeignKey["PatientExamination"]
|
|
35
59
|
finding: models.ForeignKey["Finding"]
|
|
36
60
|
|
|
37
61
|
@property
|
|
38
|
-
def video_segments(
|
|
62
|
+
def video_segments(
|
|
63
|
+
self,
|
|
64
|
+
) -> models.manager.RelatedManager["LabelVideoSegment"]: ...
|
|
39
65
|
|
|
40
66
|
@property
|
|
41
|
-
def interventions(
|
|
67
|
+
def interventions(
|
|
68
|
+
self,
|
|
69
|
+
) -> models.manager.RelatedManager["PatientFindingIntervention"]: ...
|
|
42
70
|
|
|
43
71
|
@property
|
|
44
|
-
def classifications(
|
|
72
|
+
def classifications(
|
|
73
|
+
self,
|
|
74
|
+
) -> models.manager.RelatedManager["PatientFindingClassification"]: ...
|
|
45
75
|
|
|
46
76
|
class Meta:
|
|
47
77
|
verbose_name = "Patient Finding"
|
|
@@ -51,13 +81,19 @@ class PatientFinding(models.Model):
|
|
|
51
81
|
# Wichtige Constraints für Datenintegrität
|
|
52
82
|
constraints = [
|
|
53
83
|
models.UniqueConstraint(
|
|
54
|
-
fields=["patient_examination", "finding"],
|
|
84
|
+
fields=["patient_examination", "finding"],
|
|
85
|
+
condition=models.Q(is_active=True),
|
|
86
|
+
name="unique_active_finding_per_examination",
|
|
55
87
|
),
|
|
56
88
|
models.CheckConstraint(
|
|
57
89
|
condition=models.Q( # called .check in future?
|
|
58
90
|
deactivated_at__isnull=True, deactivated_by__isnull=True
|
|
59
91
|
)
|
|
60
|
-
| models.Q(
|
|
92
|
+
| models.Q(
|
|
93
|
+
deactivated_at__isnull=False,
|
|
94
|
+
deactivated_by__isnull=False,
|
|
95
|
+
is_active=False,
|
|
96
|
+
),
|
|
61
97
|
name="deactivation_fields_consistency",
|
|
62
98
|
),
|
|
63
99
|
]
|
|
@@ -89,10 +125,14 @@ class PatientFinding(models.Model):
|
|
|
89
125
|
|
|
90
126
|
# Prüfe ob Finding für diese Examination erlaubt ist
|
|
91
127
|
if self.finding and self.patient_examination:
|
|
92
|
-
available_findings =
|
|
128
|
+
available_findings = (
|
|
129
|
+
self.patient_examination.examination_safe.get_available_findings()
|
|
130
|
+
)
|
|
93
131
|
if self.finding not in available_findings:
|
|
94
132
|
raise ValidationError(
|
|
95
|
-
{
|
|
133
|
+
{
|
|
134
|
+
"finding": f'Finding "{self.finding.name}" ist nicht für Examination "{self.patient_examination.examination_safe.name}" erlaubt.'
|
|
135
|
+
}
|
|
96
136
|
)
|
|
97
137
|
|
|
98
138
|
# Prüfe Required Findings Logic
|
|
@@ -118,10 +158,15 @@ class PatientFinding(models.Model):
|
|
|
118
158
|
required_findings = getattr(examination, "required_findings", None)
|
|
119
159
|
if required_findings and required_findings.exists():
|
|
120
160
|
# Prüfe ob alle Required Findings vorhanden sind
|
|
121
|
-
existing_findings = self.patient_examination.patient_findings.filter(
|
|
161
|
+
existing_findings = self.patient_examination.patient_findings.filter(
|
|
162
|
+
is_active=True
|
|
163
|
+
).values_list("finding", flat=True)
|
|
122
164
|
|
|
123
165
|
missing_required = required_findings.exclude(id__in=existing_findings)
|
|
124
|
-
if
|
|
166
|
+
if (
|
|
167
|
+
missing_required.exists()
|
|
168
|
+
and self.finding not in required_findings.all()
|
|
169
|
+
):
|
|
125
170
|
missing_names = ", ".join([f.name for f in missing_required])
|
|
126
171
|
raise ValidationError(f"Erforderliche Findings fehlen: {missing_names}")
|
|
127
172
|
|
|
@@ -151,7 +196,14 @@ class PatientFinding(models.Model):
|
|
|
151
196
|
self.deactivated_at = None
|
|
152
197
|
self.deactivated_by = None
|
|
153
198
|
self.updated_by = user
|
|
154
|
-
self.save(
|
|
199
|
+
self.save(
|
|
200
|
+
update_fields=[
|
|
201
|
+
"is_active",
|
|
202
|
+
"deactivated_at",
|
|
203
|
+
"deactivated_by",
|
|
204
|
+
"updated_by",
|
|
205
|
+
]
|
|
206
|
+
)
|
|
155
207
|
except ValidationError as e:
|
|
156
208
|
raise ValidationError(f"Reaktivierung nicht möglich: {e}")
|
|
157
209
|
|
|
@@ -189,20 +241,30 @@ class PatientFinding(models.Model):
|
|
|
189
241
|
|
|
190
242
|
try:
|
|
191
243
|
classification = FindingClassification.objects.get(id=classification_id)
|
|
192
|
-
classification_choice = FindingClassificationChoice.objects.filter(
|
|
244
|
+
classification_choice = FindingClassificationChoice.objects.filter(
|
|
245
|
+
id=classification_choice_id
|
|
246
|
+
).first()
|
|
193
247
|
|
|
194
248
|
if not classification.choices.filter(id=classification_choice_id).exists():
|
|
195
|
-
raise ValidationError(
|
|
249
|
+
raise ValidationError(
|
|
250
|
+
f"Classification Choice {classification_choice_id} gehört nicht zu Classification {classification_id}"
|
|
251
|
+
)
|
|
196
252
|
|
|
197
|
-
existing = self.classifications.filter(
|
|
253
|
+
existing = self.classifications.filter(
|
|
254
|
+
classification=classification,
|
|
255
|
+
classification_choice=classification_choice,
|
|
256
|
+
is_active=True,
|
|
257
|
+
).first()
|
|
198
258
|
|
|
199
259
|
if existing:
|
|
200
260
|
return existing
|
|
201
261
|
|
|
202
|
-
patient_finding_classification =
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
262
|
+
patient_finding_classification = (
|
|
263
|
+
PatientFindingClassification.objects.create(
|
|
264
|
+
finding=self,
|
|
265
|
+
classification_id=classification_id,
|
|
266
|
+
classification_choice_id=classification_choice_id,
|
|
267
|
+
)
|
|
206
268
|
)
|
|
207
269
|
|
|
208
270
|
return patient_finding_classification
|
|
@@ -233,7 +295,11 @@ class PatientFinding(models.Model):
|
|
|
233
295
|
intervention = FindingIntervention.objects.get(id=intervention_id)
|
|
234
296
|
|
|
235
297
|
patient_finding_intervention = PatientFindingIntervention.objects.create(
|
|
236
|
-
patient_finding=self,
|
|
298
|
+
patient_finding=self,
|
|
299
|
+
intervention=intervention,
|
|
300
|
+
state=state,
|
|
301
|
+
date=date or timezone.now(),
|
|
302
|
+
created_by=user,
|
|
237
303
|
)
|
|
238
304
|
|
|
239
305
|
return patient_finding_intervention
|
|
@@ -273,7 +339,9 @@ class PatientFinding(models.Model):
|
|
|
273
339
|
Returns:
|
|
274
340
|
QuerySet: Classifications related to this finding filtered by the "location" classification type.
|
|
275
341
|
"""
|
|
276
|
-
classifications = self.classifications.filter(
|
|
342
|
+
classifications = self.classifications.filter(
|
|
343
|
+
classification__classification_types__name__iexact="location"
|
|
344
|
+
)
|
|
277
345
|
return classifications
|
|
278
346
|
|
|
279
347
|
@property
|
|
@@ -284,7 +352,9 @@ class PatientFinding(models.Model):
|
|
|
284
352
|
Returns:
|
|
285
353
|
QuerySet: Classifications related to this finding filtered by the "morphology" classification type.
|
|
286
354
|
"""
|
|
287
|
-
classifications = self.classifications.filter(
|
|
355
|
+
classifications = self.classifications.filter(
|
|
356
|
+
classification__classification_types__name__iexact="morphology"
|
|
357
|
+
)
|
|
288
358
|
return classifications
|
|
289
359
|
|
|
290
360
|
@property
|
|
@@ -318,7 +388,9 @@ class PatientFinding(models.Model):
|
|
|
318
388
|
if pf_classification.classification:
|
|
319
389
|
finding_classifications_list.append(pf_classification.classification)
|
|
320
390
|
if pf_classification.classification_choice:
|
|
321
|
-
finding_classification_choices_list.append(
|
|
391
|
+
finding_classification_choices_list.append(
|
|
392
|
+
pf_classification.classification_choice
|
|
393
|
+
)
|
|
322
394
|
|
|
323
395
|
# Get all active finding interventions
|
|
324
396
|
finding_interventions_list = []
|
|
@@ -327,8 +399,12 @@ class PatientFinding(models.Model):
|
|
|
327
399
|
finding_interventions_list.append(pf_intervention.intervention)
|
|
328
400
|
|
|
329
401
|
# Include patient examination and patient for context
|
|
330
|
-
patient_examinations_list =
|
|
331
|
-
|
|
402
|
+
patient_examinations_list = (
|
|
403
|
+
[self.patient_examination] if self.patient_examination else []
|
|
404
|
+
)
|
|
405
|
+
patient_findings_list = cast(
|
|
406
|
+
"List[PatientFinding]", [self]
|
|
407
|
+
) # Include self for direct patient finding evaluations
|
|
332
408
|
|
|
333
409
|
return RequirementLinks(
|
|
334
410
|
findings=findings_list,
|
|
@@ -18,11 +18,23 @@ class PatientFindingClassification(models.Model):
|
|
|
18
18
|
Links a PatientFinding to a specific classification and choice, with optional subcategory values.
|
|
19
19
|
"""
|
|
20
20
|
|
|
21
|
-
finding = models.ForeignKey(
|
|
22
|
-
|
|
23
|
-
|
|
21
|
+
finding = models.ForeignKey(
|
|
22
|
+
"PatientFinding", on_delete=models.CASCADE, related_name="classifications"
|
|
23
|
+
)
|
|
24
|
+
classification = models.ForeignKey(
|
|
25
|
+
"FindingClassification",
|
|
26
|
+
on_delete=models.CASCADE,
|
|
27
|
+
related_name="patient_finding_classifications",
|
|
28
|
+
)
|
|
29
|
+
classification_choice = models.ForeignKey(
|
|
30
|
+
"FindingClassificationChoice",
|
|
31
|
+
on_delete=models.CASCADE,
|
|
32
|
+
related_name="patient_finding_classifications",
|
|
33
|
+
)
|
|
24
34
|
|
|
25
|
-
is_active = models.BooleanField(
|
|
35
|
+
is_active = models.BooleanField(
|
|
36
|
+
default=True, help_text="Indicates if the classification is currently active."
|
|
37
|
+
)
|
|
26
38
|
subcategories = models.JSONField(blank=True, null=True)
|
|
27
39
|
numerical_descriptors = models.JSONField(blank=True, null=True)
|
|
28
40
|
|
|
@@ -55,7 +67,9 @@ class PatientFindingClassification(models.Model):
|
|
|
55
67
|
self.subcategories = self.classification_choice.subcategories
|
|
56
68
|
|
|
57
69
|
if not self.numerical_descriptors:
|
|
58
|
-
self.numerical_descriptors =
|
|
70
|
+
self.numerical_descriptors = (
|
|
71
|
+
self.classification_choice.numerical_descriptors
|
|
72
|
+
)
|
|
59
73
|
|
|
60
74
|
super().save(*args, **kwargs)
|
|
61
75
|
|
|
@@ -80,7 +94,9 @@ class PatientFindingClassification(models.Model):
|
|
|
80
94
|
self.save()
|
|
81
95
|
return self.numerical_descriptors
|
|
82
96
|
|
|
83
|
-
def set_subcategory(
|
|
97
|
+
def set_subcategory(
|
|
98
|
+
self, subcategory_name: str, subcategory_value: Dict[str, dict]
|
|
99
|
+
):
|
|
84
100
|
"""
|
|
85
101
|
Update the value of a specified subcategory and save the classification.
|
|
86
102
|
|
|
@@ -92,7 +108,9 @@ class PatientFindingClassification(models.Model):
|
|
|
92
108
|
dict: The updated subcategory dictionary.
|
|
93
109
|
"""
|
|
94
110
|
assert self.subcategories, "Subcategories must be initialized."
|
|
95
|
-
assert subcategory_name in self.subcategories,
|
|
111
|
+
assert subcategory_name in self.subcategories, (
|
|
112
|
+
"Subcategory must be in subcategories."
|
|
113
|
+
)
|
|
96
114
|
self.subcategories[subcategory_name]["value"] = subcategory_value
|
|
97
115
|
self.save()
|
|
98
116
|
|
|
@@ -136,8 +154,12 @@ class PatientFindingClassification(models.Model):
|
|
|
136
154
|
Raises:
|
|
137
155
|
ValueError: If the descriptor's distribution type is not supported.
|
|
138
156
|
"""
|
|
139
|
-
assert self.numerical_descriptors is not None,
|
|
140
|
-
|
|
157
|
+
assert self.numerical_descriptors is not None, (
|
|
158
|
+
"Numerical descriptors must be initialized."
|
|
159
|
+
)
|
|
160
|
+
assert descriptor_name in self.numerical_descriptors, (
|
|
161
|
+
"Descriptor must be in numerical descriptors."
|
|
162
|
+
)
|
|
141
163
|
descriptor = self.numerical_descriptors[descriptor_name]
|
|
142
164
|
min_val = descriptor.get("min", 0)
|
|
143
165
|
max_val = descriptor.get("max", 1)
|
|
@@ -173,7 +195,9 @@ class PatientFindingClassification(models.Model):
|
|
|
173
195
|
raise ValueError("Descriptor name must be in numerical descriptors.")
|
|
174
196
|
|
|
175
197
|
value = self.get_random_value_for_numerical_descriptor(descriptor_name)
|
|
176
|
-
assert self.numerical_descriptors is not None,
|
|
198
|
+
assert self.numerical_descriptors is not None, (
|
|
199
|
+
"Numerical descriptors must be initialized."
|
|
200
|
+
)
|
|
177
201
|
self.numerical_descriptors[descriptor_name]["value"] = value
|
|
178
202
|
if save:
|
|
179
203
|
self.save()
|
|
@@ -190,11 +214,16 @@ class PatientFindingClassification(models.Model):
|
|
|
190
214
|
if not self.subcategories or not self.numerical_descriptors:
|
|
191
215
|
self.save()
|
|
192
216
|
|
|
193
|
-
assert self.numerical_descriptors is not None,
|
|
217
|
+
assert self.numerical_descriptors is not None, (
|
|
218
|
+
"Numerical descriptors must be initialized."
|
|
219
|
+
)
|
|
194
220
|
|
|
195
221
|
numerical_descriptors = self.numerical_descriptors
|
|
196
222
|
|
|
197
|
-
for
|
|
223
|
+
for (
|
|
224
|
+
numerical_descriptor_name,
|
|
225
|
+
_numerical_descriptor_dict,
|
|
226
|
+
) in numerical_descriptors.items():
|
|
198
227
|
self.set_random_numerical_descriptor(numerical_descriptor_name, save=False)
|
|
199
228
|
|
|
200
229
|
self.save()
|
|
@@ -8,9 +8,17 @@ if TYPE_CHECKING:
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class PatientFindingIntervention(models.Model):
|
|
11
|
-
finding = models.ForeignKey(
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
finding = models.ForeignKey(
|
|
12
|
+
"PatientFinding", on_delete=models.CASCADE, related_name="interventions"
|
|
13
|
+
)
|
|
14
|
+
intervention = models.ForeignKey(
|
|
15
|
+
"FindingIntervention",
|
|
16
|
+
on_delete=models.CASCADE,
|
|
17
|
+
related_name="patient_finding_interventions",
|
|
18
|
+
)
|
|
19
|
+
is_active = models.BooleanField(
|
|
20
|
+
default=True, help_text="Indicates if the intervention is currently active."
|
|
21
|
+
)
|
|
14
22
|
state = models.CharField(max_length=100, blank=True, null=True)
|
|
15
23
|
time_start = models.DateTimeField(blank=True, null=True)
|
|
16
24
|
time_end = models.DateTimeField(blank=True, null=True)
|
|
@@ -60,7 +60,9 @@ class PatientLabSample(models.Model):
|
|
|
60
60
|
values (PatientLabValue; One2Many): The value of the lab sample.
|
|
61
61
|
"""
|
|
62
62
|
|
|
63
|
-
patient = models.ForeignKey(
|
|
63
|
+
patient = models.ForeignKey(
|
|
64
|
+
"Patient", on_delete=models.CASCADE, related_name="lab_samples"
|
|
65
|
+
)
|
|
64
66
|
sample_type = models.ForeignKey("PatientLabSampleType", on_delete=models.CASCADE)
|
|
65
67
|
date = models.DateTimeField()
|
|
66
68
|
|
|
@@ -125,7 +127,9 @@ class PatientLabSample(models.Model):
|
|
|
125
127
|
if not date:
|
|
126
128
|
date = dt.now(timezone.utc)
|
|
127
129
|
|
|
128
|
-
patient_lab_sample = cls.objects.create(
|
|
130
|
+
patient_lab_sample = cls.objects.create(
|
|
131
|
+
patient=patient, sample_type=sample_type, date=date
|
|
132
|
+
)
|
|
129
133
|
|
|
130
134
|
if save:
|
|
131
135
|
patient_lab_sample.save()
|
|
@@ -23,11 +23,23 @@ class PatientLabValue(models.Model):
|
|
|
23
23
|
date (datetime): The date of the lab value.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
patient = models.ForeignKey(
|
|
26
|
+
patient = models.ForeignKey(
|
|
27
|
+
"Patient",
|
|
28
|
+
on_delete=models.CASCADE,
|
|
29
|
+
related_name="lab_values",
|
|
30
|
+
blank=True,
|
|
31
|
+
null=True,
|
|
32
|
+
)
|
|
27
33
|
lab_value = models.ForeignKey("LabValue", on_delete=models.CASCADE)
|
|
28
34
|
value = models.FloatField(blank=True, null=True)
|
|
29
35
|
value_str = models.CharField(max_length=255, blank=True, null=True)
|
|
30
|
-
sample = models.ForeignKey(
|
|
36
|
+
sample = models.ForeignKey(
|
|
37
|
+
"PatientLabSample",
|
|
38
|
+
on_delete=models.CASCADE,
|
|
39
|
+
blank=True,
|
|
40
|
+
null=True,
|
|
41
|
+
related_name="values",
|
|
42
|
+
)
|
|
31
43
|
datetime = models.DateTimeField( # if not set, use now
|
|
32
44
|
auto_now_add=True
|
|
33
45
|
)
|
|
@@ -57,7 +69,12 @@ class PatientLabValue(models.Model):
|
|
|
57
69
|
|
|
58
70
|
@classmethod
|
|
59
71
|
def create_lab_value_by_sample(
|
|
60
|
-
cls,
|
|
72
|
+
cls,
|
|
73
|
+
sample: "PatientLabSample",
|
|
74
|
+
lab_value_name: str,
|
|
75
|
+
value: Optional[float] = None,
|
|
76
|
+
value_str: Optional[str] = None,
|
|
77
|
+
unit: Optional["Unit"] = None,
|
|
61
78
|
):
|
|
62
79
|
from ..laboratory import LabValue
|
|
63
80
|
|
|
@@ -80,7 +97,9 @@ class PatientLabValue(models.Model):
|
|
|
80
97
|
def __str__(self):
|
|
81
98
|
formatted_datetime = self.datetime.strftime("%Y-%m-%d %H:%M")
|
|
82
99
|
# normal_range = self.get_normal_range()
|
|
83
|
-
norm_range_string =
|
|
100
|
+
norm_range_string = (
|
|
101
|
+
f"[{self.normal_range.get('min', '')} - {self.normal_range.get('max', '')}]"
|
|
102
|
+
)
|
|
84
103
|
_str = f"{self.lab_value} - {self.value} {self.unit} - {norm_range_string} ({formatted_datetime})"
|
|
85
104
|
return _str
|
|
86
105
|
|
|
@@ -157,16 +176,29 @@ class PatientLabValue(models.Model):
|
|
|
157
176
|
distribution = lab_value.get_default_default_distribution()
|
|
158
177
|
|
|
159
178
|
if not distribution:
|
|
160
|
-
warnings.warn(
|
|
179
|
+
warnings.warn(
|
|
180
|
+
f"No distribution set for lab value {lab_value}, assuming uniform numeric distribution based on normal values"
|
|
181
|
+
)
|
|
161
182
|
|
|
162
|
-
if not self.normal_range.get("min", None) or not self.normal_range.get(
|
|
183
|
+
if not self.normal_range.get("min", None) or not self.normal_range.get(
|
|
184
|
+
"max", None
|
|
185
|
+
):
|
|
163
186
|
self.set_norm_values_from_default()
|
|
164
187
|
_min = self.normal_range.get("min", 0.0001)
|
|
165
188
|
_max = self.normal_range.get("max", 100)
|
|
166
|
-
_name =
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
189
|
+
_name = (
|
|
190
|
+
"auto-" + self.lab_value_safe.name + "-distribution-default-uniform"
|
|
191
|
+
)
|
|
192
|
+
distribution = NumericValueDistribution(
|
|
193
|
+
name=_name,
|
|
194
|
+
min_descriptor=_min,
|
|
195
|
+
max_max_desciptor=_max,
|
|
196
|
+
distribution_type="uniform",
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
value = distribution.generate_value(
|
|
200
|
+
lab_value=lab_value, patient=patient
|
|
201
|
+
)
|
|
170
202
|
self.value = value
|
|
171
203
|
if save:
|
|
172
204
|
self.save()
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, List,
|
|
1
|
+
from typing import TYPE_CHECKING, List, cast # Added List
|
|
2
2
|
|
|
3
3
|
from django.db import models
|
|
4
4
|
|
|
5
5
|
# Added imports for type hints
|
|
6
6
|
if TYPE_CHECKING:
|
|
7
|
-
from ....utils.links.requirement_link import
|
|
7
|
+
from ....utils.links.requirement_link import (
|
|
8
|
+
RequirementLinks,
|
|
9
|
+
) # Added RequirementLinks
|
|
8
10
|
from ...administration.person.patient import Patient
|
|
9
11
|
from ...other.unit import Unit
|
|
10
12
|
from ..medication import Medication, MedicationIndication, MedicationIntakeTime
|
|
@@ -18,9 +20,19 @@ class PatientMedication(models.Model):
|
|
|
18
20
|
"""
|
|
19
21
|
|
|
20
22
|
patient = models.ForeignKey("Patient", on_delete=models.CASCADE)
|
|
21
|
-
medication_indication = models.ForeignKey(
|
|
23
|
+
medication_indication = models.ForeignKey(
|
|
24
|
+
"MedicationIndication",
|
|
25
|
+
on_delete=models.CASCADE,
|
|
26
|
+
related_name="indication_patient_medications",
|
|
27
|
+
null=True,
|
|
28
|
+
)
|
|
22
29
|
|
|
23
|
-
medication = models.ForeignKey(
|
|
30
|
+
medication = models.ForeignKey(
|
|
31
|
+
"Medication",
|
|
32
|
+
on_delete=models.CASCADE,
|
|
33
|
+
blank=True,
|
|
34
|
+
related_name="medication_patient_medications",
|
|
35
|
+
)
|
|
24
36
|
|
|
25
37
|
intake_times = models.ManyToManyField(
|
|
26
38
|
"MedicationIntakeTime",
|
|
@@ -39,7 +51,9 @@ class PatientMedication(models.Model):
|
|
|
39
51
|
medication_indication: models.ForeignKey["MedicationIndication|None"]
|
|
40
52
|
medication: models.ForeignKey["Medication|None"]
|
|
41
53
|
|
|
42
|
-
intake_times = cast(
|
|
54
|
+
intake_times = cast(
|
|
55
|
+
models.manager.RelatedManager["MedicationIntakeTime"], intake_times
|
|
56
|
+
)
|
|
43
57
|
unit: models.ForeignKey["Unit|None"]
|
|
44
58
|
dosage = cast(models.JSONField, dosage)
|
|
45
59
|
|
|
@@ -73,10 +87,14 @@ class PatientMedication(models.Model):
|
|
|
73
87
|
verbose_name_plural = "Patient Medications"
|
|
74
88
|
|
|
75
89
|
@classmethod
|
|
76
|
-
def create_by_patient_and_indication(
|
|
90
|
+
def create_by_patient_and_indication(
|
|
91
|
+
cls, patient, medication_indication: "MedicationIndication"
|
|
92
|
+
):
|
|
77
93
|
"""Creates a PatientMedication instance linking a patient and an indication."""
|
|
78
94
|
|
|
79
|
-
patient_medication = cls.objects.create(
|
|
95
|
+
patient_medication = cls.objects.create(
|
|
96
|
+
patient=patient, medication_indication=medication_indication
|
|
97
|
+
)
|
|
80
98
|
patient_medication.save()
|
|
81
99
|
|
|
82
100
|
return patient_medication
|
|
@@ -7,7 +7,11 @@ if TYPE_CHECKING:
|
|
|
7
7
|
from .patient_medication import PatientMedication
|
|
8
8
|
from ..medication import MedicationSchedule
|
|
9
9
|
from ....utils.links.requirement_link import RequirementLinks # Added
|
|
10
|
-
from ..medication import
|
|
10
|
+
from ..medication import (
|
|
11
|
+
Medication,
|
|
12
|
+
MedicationIndication,
|
|
13
|
+
MedicationIntakeTime,
|
|
14
|
+
) # Added
|
|
11
15
|
|
|
12
16
|
|
|
13
17
|
class PatientMedicationSchedule(models.Model):
|
|
@@ -16,7 +20,9 @@ class PatientMedicationSchedule(models.Model):
|
|
|
16
20
|
"""
|
|
17
21
|
|
|
18
22
|
patient = models.ForeignKey("Patient", on_delete=models.CASCADE)
|
|
19
|
-
medication = models.ManyToManyField(
|
|
23
|
+
medication = models.ManyToManyField(
|
|
24
|
+
"PatientMedication", related_name="patient_medication_schedules", blank=True
|
|
25
|
+
)
|
|
20
26
|
|
|
21
27
|
created_at = models.DateTimeField(auto_now_add=True)
|
|
22
28
|
updated_at = models.DateTimeField(auto_now=True)
|
|
@@ -24,7 +30,9 @@ class PatientMedicationSchedule(models.Model):
|
|
|
24
30
|
if TYPE_CHECKING:
|
|
25
31
|
patient: models.ForeignKey["Patient"]
|
|
26
32
|
|
|
27
|
-
medication = cast(
|
|
33
|
+
medication = cast(
|
|
34
|
+
models.manager.RelatedManager["PatientMedication"], medication
|
|
35
|
+
)
|
|
28
36
|
|
|
29
37
|
@property
|
|
30
38
|
def links(self) -> "RequirementLinks":
|
|
@@ -37,14 +45,20 @@ class PatientMedicationSchedule(models.Model):
|
|
|
37
45
|
aggregated_medication_indications: List["MedicationIndication"] = []
|
|
38
46
|
aggregated_medication_intake_times: List["MedicationIntakeTime"] = []
|
|
39
47
|
|
|
40
|
-
patient_meds_in_schedule: List["PatientMedication"] = list(
|
|
48
|
+
patient_meds_in_schedule: List["PatientMedication"] = list(
|
|
49
|
+
self.medication.all()
|
|
50
|
+
)
|
|
41
51
|
|
|
42
52
|
for pm_instance in patient_meds_in_schedule:
|
|
43
53
|
pm_links_obj = pm_instance.links
|
|
44
54
|
|
|
45
55
|
aggregated_medications.extend(pm_links_obj.medications)
|
|
46
|
-
aggregated_medication_indications.extend(
|
|
47
|
-
|
|
56
|
+
aggregated_medication_indications.extend(
|
|
57
|
+
pm_links_obj.medication_indications
|
|
58
|
+
)
|
|
59
|
+
aggregated_medication_intake_times.extend(
|
|
60
|
+
pm_links_obj.medication_intake_times
|
|
61
|
+
)
|
|
48
62
|
|
|
49
63
|
return RequirementLinks(
|
|
50
64
|
medications=list(set(aggregated_medications)),
|
|
@@ -64,12 +78,16 @@ class PatientMedicationSchedule(models.Model):
|
|
|
64
78
|
from ..medication import MedicationIndicationType
|
|
65
79
|
from .patient_medication import PatientMedication
|
|
66
80
|
|
|
67
|
-
medication_indication = MedicationIndicationType.get_random_indication_by_type(
|
|
81
|
+
medication_indication = MedicationIndicationType.get_random_indication_by_type(
|
|
82
|
+
name=indication_type
|
|
83
|
+
)
|
|
68
84
|
|
|
69
85
|
patient_medication_schedule = cls.objects.create(patient=patient)
|
|
70
86
|
patient_medication_schedule.save()
|
|
71
87
|
|
|
72
|
-
patient_medication = PatientMedication.create_by_patient_and_indication(
|
|
88
|
+
patient_medication = PatientMedication.create_by_patient_and_indication(
|
|
89
|
+
patient, medication_indication
|
|
90
|
+
)
|
|
73
91
|
patient_medication_schedule.medication.add(patient_medication)
|
|
74
92
|
patient_medication_schedule.save()
|
|
75
93
|
|
|
@@ -87,7 +105,9 @@ class PatientMedicationSchedule(models.Model):
|
|
|
87
105
|
patient_medication_schedule = cls.objects.create(patient=patient)
|
|
88
106
|
patient_medication_schedule.save()
|
|
89
107
|
|
|
90
|
-
patient_medication = PatientMedication.create_by_patient_and_indication(
|
|
108
|
+
patient_medication = PatientMedication.create_by_patient_and_indication(
|
|
109
|
+
patient, medication_indication
|
|
110
|
+
)
|
|
91
111
|
patient_medication_schedule.medication.add(patient_medication)
|
|
92
112
|
patient_medication_schedule.save()
|
|
93
113
|
|
|
@@ -112,7 +132,11 @@ class PatientMedicationSchedule(models.Model):
|
|
|
112
132
|
intake_times = medication_schedule.get_intake_times()
|
|
113
133
|
|
|
114
134
|
patient_medication = PatientMedication.objects.create(
|
|
115
|
-
patient=self.patient,
|
|
135
|
+
patient=self.patient,
|
|
136
|
+
medication=drug,
|
|
137
|
+
medication_indication=medication_indication,
|
|
138
|
+
unit=unit,
|
|
139
|
+
dosage=dosage,
|
|
116
140
|
)
|
|
117
141
|
|
|
118
142
|
patient_medication.intake_times.set(intake_times)
|