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
|
@@ -7,9 +7,7 @@ from django.db import models
|
|
|
7
7
|
|
|
8
8
|
from endoreg_db.models.label import LabelSet
|
|
9
9
|
|
|
10
|
-
from ..label.label_video_segment import
|
|
11
|
-
LabelVideoSegment,
|
|
12
|
-
)
|
|
10
|
+
from ..label.label_video_segment import LabelVideoSegment
|
|
13
11
|
from ..utils import find_segments_in_prediction_array
|
|
14
12
|
|
|
15
13
|
logger = logging.getLogger(__name__)
|
|
@@ -49,7 +47,12 @@ class VideoPredictionMeta(models.Model):
|
|
|
49
47
|
label_video_segments: "models.Manager[LabelVideoSegment]"
|
|
50
48
|
|
|
51
49
|
class Meta:
|
|
52
|
-
constraints = [
|
|
50
|
+
constraints = [
|
|
51
|
+
models.UniqueConstraint(
|
|
52
|
+
fields=["model_meta", "video_file"],
|
|
53
|
+
name="unique_prediction_per_video_model",
|
|
54
|
+
)
|
|
55
|
+
]
|
|
53
56
|
indexes = [
|
|
54
57
|
models.Index(fields=["model_meta", "video_file"]),
|
|
55
58
|
]
|
|
@@ -72,11 +75,15 @@ class VideoPredictionMeta(models.Model):
|
|
|
72
75
|
def __str__(self):
|
|
73
76
|
try:
|
|
74
77
|
video_obj = self.get_video()
|
|
75
|
-
return f"Prediction Meta for Video {video_obj.
|
|
78
|
+
return f"Prediction Meta for Video {video_obj.video_hash} - {self.model_meta.name}"
|
|
76
79
|
except ValueError:
|
|
77
80
|
return f"Prediction Meta {self.pk} (Error: No VideoFile) - {self.model_meta.name}"
|
|
78
81
|
except Exception as e:
|
|
79
|
-
logger.warning(
|
|
82
|
+
logger.warning(
|
|
83
|
+
"Error generating string representation for VideoPredictionMeta %s: %s",
|
|
84
|
+
self.pk,
|
|
85
|
+
e,
|
|
86
|
+
)
|
|
80
87
|
return f"Prediction Meta {self.pk} (Error: {e})"
|
|
81
88
|
|
|
82
89
|
def get_labelset(self) -> Optional["LabelSet"]:
|
|
@@ -123,19 +130,29 @@ class VideoPredictionMeta(models.Model):
|
|
|
123
130
|
num_frames = video_obj.frame_count
|
|
124
131
|
|
|
125
132
|
if num_frames is None or num_frames <= 0:
|
|
126
|
-
logger.warning(
|
|
133
|
+
logger.warning(
|
|
134
|
+
f"Cannot calculate prediction array for {video_obj} with invalid frame count ({num_frames})."
|
|
135
|
+
)
|
|
127
136
|
return
|
|
128
137
|
|
|
129
138
|
if not label_list:
|
|
130
|
-
logger.warning(
|
|
139
|
+
logger.warning(
|
|
140
|
+
f"No labels found for model {model_meta}. Cannot calculate prediction array."
|
|
141
|
+
)
|
|
131
142
|
return
|
|
132
143
|
|
|
133
144
|
prediction_array = np.zeros((num_frames, len(label_list)))
|
|
134
145
|
|
|
135
|
-
base_pred_qs = ImageClassificationAnnotation.objects.filter(
|
|
146
|
+
base_pred_qs = ImageClassificationAnnotation.objects.filter(
|
|
147
|
+
model_meta=model_meta, frame__video_file=video_obj
|
|
148
|
+
)
|
|
136
149
|
|
|
137
150
|
for i, label in enumerate(label_list):
|
|
138
|
-
predictions =
|
|
151
|
+
predictions = (
|
|
152
|
+
base_pred_qs.filter(label=label)
|
|
153
|
+
.order_by("frame__frame_number")
|
|
154
|
+
.values_list("frame__frame_number", "float_value")
|
|
155
|
+
)
|
|
139
156
|
|
|
140
157
|
confidences = np.full(num_frames, 0.5)
|
|
141
158
|
found_predictions = False
|
|
@@ -144,19 +161,27 @@ class VideoPredictionMeta(models.Model):
|
|
|
144
161
|
confidences[frame_num] = confidence
|
|
145
162
|
found_predictions = True
|
|
146
163
|
else:
|
|
147
|
-
logger.warning(
|
|
164
|
+
logger.warning(
|
|
165
|
+
f"Prediction found for out-of-bounds frame number {frame_num} (max: {num_frames - 1}). Skipping."
|
|
166
|
+
)
|
|
148
167
|
|
|
149
168
|
if not found_predictions:
|
|
150
|
-
logger.warning(
|
|
169
|
+
logger.warning(
|
|
170
|
+
f"No predictions found for label '{label.name}' in {video_obj}. Using default confidence."
|
|
171
|
+
)
|
|
151
172
|
|
|
152
|
-
smooth_confidences = self.apply_running_mean(
|
|
173
|
+
smooth_confidences = self.apply_running_mean(
|
|
174
|
+
confidences, window_size_in_seconds
|
|
175
|
+
)
|
|
153
176
|
binary_predictions = smooth_confidences > 0.5
|
|
154
177
|
prediction_array[:, i] = binary_predictions
|
|
155
178
|
|
|
156
179
|
self.save_prediction_array(prediction_array)
|
|
157
180
|
logger.info(f"Calculated and saved prediction array for {self}")
|
|
158
181
|
|
|
159
|
-
def apply_running_mean(
|
|
182
|
+
def apply_running_mean(
|
|
183
|
+
self, confidence_array, window_size_in_seconds: Optional[float] = None
|
|
184
|
+
):
|
|
160
185
|
"""
|
|
161
186
|
Apply a running mean filter to the confidence array for smoothing, assuming a padding
|
|
162
187
|
of 0.5 for the edges.
|
|
@@ -165,7 +190,9 @@ class VideoPredictionMeta(models.Model):
|
|
|
165
190
|
fps = video_obj.get_fps()
|
|
166
191
|
|
|
167
192
|
if fps is None or fps <= 0:
|
|
168
|
-
logger.warning(
|
|
193
|
+
logger.warning(
|
|
194
|
+
f"Invalid FPS ({fps}) for {video_obj}. Cannot apply running mean. Returning original array."
|
|
195
|
+
)
|
|
169
196
|
return confidence_array
|
|
170
197
|
|
|
171
198
|
if not window_size_in_seconds:
|
|
@@ -190,19 +217,25 @@ class VideoPredictionMeta(models.Model):
|
|
|
190
217
|
running_mean = running_mean[start_index:end_index]
|
|
191
218
|
|
|
192
219
|
if running_mean.shape != confidence_array.shape:
|
|
193
|
-
logger.warning(
|
|
220
|
+
logger.warning(
|
|
221
|
+
f"Running mean output shape {running_mean.shape} differs from input {confidence_array.shape}. Check padding/slicing."
|
|
222
|
+
)
|
|
194
223
|
return confidence_array
|
|
195
224
|
|
|
196
225
|
return running_mean
|
|
197
226
|
|
|
198
|
-
def create_video_segments_for_label(
|
|
227
|
+
def create_video_segments_for_label(
|
|
228
|
+
self, segments: List[Tuple[int, int]], label: "Label"
|
|
229
|
+
):
|
|
199
230
|
"""
|
|
200
231
|
Creates LabelVideoSegment instances for the given label and segments.
|
|
201
232
|
"""
|
|
202
233
|
from endoreg_db.models import InformationSource
|
|
203
234
|
|
|
204
235
|
video_obj = self.get_video()
|
|
205
|
-
information_source, _ = InformationSource.objects.get_or_create(
|
|
236
|
+
information_source, _ = InformationSource.objects.get_or_create(
|
|
237
|
+
name="prediction"
|
|
238
|
+
)
|
|
206
239
|
|
|
207
240
|
segments_to_create = []
|
|
208
241
|
for start_frame, end_frame in segments:
|
|
@@ -215,17 +248,27 @@ class VideoPredictionMeta(models.Model):
|
|
|
215
248
|
"video_file": video_obj,
|
|
216
249
|
}
|
|
217
250
|
if not LabelVideoSegment.objects.filter(
|
|
218
|
-
video_file=video_obj,
|
|
251
|
+
video_file=video_obj,
|
|
252
|
+
prediction_meta=self,
|
|
253
|
+
label=label,
|
|
254
|
+
start_frame_number=start_frame,
|
|
255
|
+
end_frame_number=end_frame,
|
|
219
256
|
).exists():
|
|
220
257
|
segments_to_create.append(LabelVideoSegment(**segment_data))
|
|
221
258
|
|
|
222
259
|
if segments_to_create:
|
|
223
260
|
LabelVideoSegment.objects.bulk_create(segments_to_create)
|
|
224
|
-
logger.info(
|
|
261
|
+
logger.info(
|
|
262
|
+
f"Created {len(segments_to_create)} video segments for label '{label.name}' in {video_obj}."
|
|
263
|
+
)
|
|
225
264
|
else:
|
|
226
|
-
logger.info(
|
|
265
|
+
logger.info(
|
|
266
|
+
f"No new video segments needed for label '{label.name}' in {video_obj}."
|
|
267
|
+
)
|
|
227
268
|
|
|
228
|
-
def create_video_segments(
|
|
269
|
+
def create_video_segments(
|
|
270
|
+
self, segment_length_threshold_in_s: Optional[float] = None
|
|
271
|
+
):
|
|
229
272
|
"""
|
|
230
273
|
Generates LabelVideoSegments based on the stored prediction array.
|
|
231
274
|
"""
|
|
@@ -236,7 +279,9 @@ class VideoPredictionMeta(models.Model):
|
|
|
236
279
|
fps = video_obj.get_fps()
|
|
237
280
|
|
|
238
281
|
if fps is None or fps <= 0:
|
|
239
|
-
logger.warning(
|
|
282
|
+
logger.warning(
|
|
283
|
+
f"Cannot create video segments for {video_obj} with invalid FPS ({fps})."
|
|
284
|
+
)
|
|
240
285
|
return
|
|
241
286
|
|
|
242
287
|
min_frame_length = int(segment_length_threshold_in_s * fps)
|
|
@@ -250,17 +295,25 @@ class VideoPredictionMeta(models.Model):
|
|
|
250
295
|
self.calculate_prediction_array()
|
|
251
296
|
prediction_array = self.get_prediction_array()
|
|
252
297
|
if prediction_array is None:
|
|
253
|
-
logger.error(
|
|
298
|
+
logger.error(
|
|
299
|
+
f"Failed to get or calculate prediction array for {self}. Cannot create segments."
|
|
300
|
+
)
|
|
254
301
|
return
|
|
255
302
|
|
|
256
303
|
if prediction_array.shape[1] != len(label_list):
|
|
257
|
-
logger.warning(
|
|
304
|
+
logger.warning(
|
|
305
|
+
f"Prediction array shape {prediction_array.shape} incompatible with label list length {len(label_list)} for {self}."
|
|
306
|
+
)
|
|
258
307
|
return
|
|
259
308
|
|
|
260
|
-
logger.info(
|
|
309
|
+
logger.info(
|
|
310
|
+
f"Creating video segments for {self} (min length: {min_frame_length} frames)..."
|
|
311
|
+
)
|
|
261
312
|
for i, label in enumerate(label_list):
|
|
262
313
|
binary_predictions = prediction_array[:, i].astype(bool)
|
|
263
|
-
segments = find_segments_in_prediction_array(
|
|
314
|
+
segments = find_segments_in_prediction_array(
|
|
315
|
+
binary_predictions, min_frame_length
|
|
316
|
+
)
|
|
264
317
|
if segments:
|
|
265
318
|
self.create_video_segments_for_label(segments, label)
|
|
266
319
|
logger.info(f"Finished creating video segments for {self}.")
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
from django.conf import settings
|
|
2
|
+
from django.db import models
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class OperationLog(models.Model):
|
|
6
|
+
"""
|
|
7
|
+
Lightweight log of user-triggered operations (audit-like).
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
# actor_id – internal Django user ID (primary key)
|
|
11
|
+
# Who did it
|
|
12
|
+
actor_user = models.ForeignKey(
|
|
13
|
+
settings.AUTH_USER_MODEL,
|
|
14
|
+
on_delete=models.SET_NULL,
|
|
15
|
+
null=True,
|
|
16
|
+
blank=True,
|
|
17
|
+
related_name="operation_logs",
|
|
18
|
+
)
|
|
19
|
+
actor_username = models.CharField(max_length=150, blank=True)
|
|
20
|
+
actor_email = models.EmailField(blank=True)
|
|
21
|
+
actor_keycloak_id = models.CharField(
|
|
22
|
+
max_length=255,
|
|
23
|
+
blank=True,
|
|
24
|
+
help_text="Keycloak subject/ID if you later want to store it.",
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
# What happened
|
|
28
|
+
action = models.CharField(
|
|
29
|
+
max_length=100,
|
|
30
|
+
help_text="e.g. 'anonymization.start', 'anonymization.validate'",
|
|
31
|
+
)
|
|
32
|
+
http_method = models.CharField(max_length=10, blank=True)
|
|
33
|
+
path = models.CharField(max_length=512, blank=True)
|
|
34
|
+
|
|
35
|
+
# On what resource
|
|
36
|
+
resource_type = models.CharField(
|
|
37
|
+
max_length=50,
|
|
38
|
+
blank=True,
|
|
39
|
+
help_text="e.g. 'video', 'pdf'",
|
|
40
|
+
)
|
|
41
|
+
resource_id = models.IntegerField(
|
|
42
|
+
null=True,
|
|
43
|
+
blank=True,
|
|
44
|
+
help_text="ID of VideoFile / RawPdfFile etc.",
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
# State before/after
|
|
48
|
+
status_before = models.CharField(max_length=50, blank=True)
|
|
49
|
+
status_after = models.CharField(max_length=50, blank=True)
|
|
50
|
+
|
|
51
|
+
# Extra info
|
|
52
|
+
meta = models.JSONField(null=True, blank=True)
|
|
53
|
+
|
|
54
|
+
created_at = models.DateTimeField(auto_now_add=True)
|
|
55
|
+
|
|
56
|
+
#
|
|
57
|
+
class Meta:
|
|
58
|
+
verbose_name = "Operation Log"
|
|
59
|
+
verbose_name_plural = "Operation Logs"
|
|
60
|
+
ordering = ["-created_at"]
|
|
61
|
+
|
|
62
|
+
def __str__(self) -> str:
|
|
63
|
+
return f"[{self.created_at.isoformat()}] {self.action} by {self.actor_username or 'unknown'}"
|
|
@@ -22,19 +22,19 @@ from .unit import Unit
|
|
|
22
22
|
from .tag import Tag
|
|
23
23
|
|
|
24
24
|
__all__ = [
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
25
|
+
"Material",
|
|
26
|
+
"Resource",
|
|
27
|
+
"TransportRoute",
|
|
28
|
+
"Waste",
|
|
29
|
+
"BaseValueDistribution",
|
|
30
|
+
"NumericValueDistribution",
|
|
31
|
+
"SingleCategoricalValueDistribution",
|
|
32
|
+
"MultipleCategoricalValueDistribution",
|
|
33
|
+
"DateValueDistribution",
|
|
34
34
|
"Gender",
|
|
35
35
|
"InformationSource",
|
|
36
36
|
"InformationSourceType",
|
|
37
37
|
"Unit",
|
|
38
38
|
"EmissionFactor",
|
|
39
39
|
"Tag",
|
|
40
|
-
]
|
|
40
|
+
]
|
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
|
|
1
|
+
"""Module for distribution models."""
|
|
2
2
|
|
|
3
3
|
from .base_value_distribution import BaseValueDistribution
|
|
4
4
|
from .numeric_value_distribution import NumericValueDistribution
|
|
5
5
|
from .single_categorical_value_distribution import SingleCategoricalValueDistribution
|
|
6
|
-
from .multiple_categorical_value_distribution import
|
|
6
|
+
from .multiple_categorical_value_distribution import (
|
|
7
|
+
MultipleCategoricalValueDistribution,
|
|
8
|
+
)
|
|
7
9
|
from .date_value_distribution import DateValueDistribution
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
__all__ = [
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
13
|
+
"BaseValueDistribution",
|
|
14
|
+
"NumericValueDistribution",
|
|
15
|
+
"SingleCategoricalValueDistribution",
|
|
16
|
+
"MultipleCategoricalValueDistribution",
|
|
17
|
+
"DateValueDistribution",
|
|
16
18
|
]
|
|
17
19
|
|
|
18
20
|
# Example Usage
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
from django.db import models
|
|
2
2
|
|
|
3
|
+
|
|
3
4
|
class BaseValueDistribution(models.Model):
|
|
4
5
|
"""
|
|
5
6
|
Abstract base class for value distributions.
|
|
6
7
|
"""
|
|
8
|
+
|
|
7
9
|
name = models.CharField(max_length=100)
|
|
8
10
|
|
|
9
11
|
class Meta:
|
|
@@ -15,6 +17,6 @@ class BaseValueDistribution(models.Model):
|
|
|
15
17
|
Must be implemented by subclasses.
|
|
16
18
|
"""
|
|
17
19
|
raise NotImplementedError("Subclasses must implement this method")
|
|
18
|
-
|
|
20
|
+
|
|
19
21
|
def natural_key(self):
|
|
20
22
|
return (self.name,)
|
|
@@ -43,7 +43,9 @@ class DateValueDistribution(BaseValueDistribution):
|
|
|
43
43
|
date_min = models.DateField(blank=True, null=True)
|
|
44
44
|
date_max = models.DateField(blank=True, null=True)
|
|
45
45
|
date_mean = models.DateField(blank=True, null=True)
|
|
46
|
-
date_std_dev = models.IntegerField(
|
|
46
|
+
date_std_dev = models.IntegerField(
|
|
47
|
+
blank=True, null=True
|
|
48
|
+
) # Standard deviation in days
|
|
47
49
|
|
|
48
50
|
# Timedelta-related fields
|
|
49
51
|
timedelta_days_min = models.IntegerField(blank=True, null=True)
|
|
@@ -127,18 +129,30 @@ class DateValueDistribution(BaseValueDistribution):
|
|
|
127
129
|
mean_ordinal = self.date_mean_safe.toordinal()
|
|
128
130
|
std_dev_days = self.date_std_dev_safe
|
|
129
131
|
random_ordinal = int(np.random.normal(mean_ordinal, std_dev_days))
|
|
130
|
-
random_ordinal = np.clip(
|
|
132
|
+
random_ordinal = np.clip(
|
|
133
|
+
random_ordinal,
|
|
134
|
+
self.date_min_safe.toordinal(),
|
|
135
|
+
self.date_max_safe.toordinal(),
|
|
136
|
+
)
|
|
131
137
|
return date.fromordinal(random_ordinal)
|
|
132
138
|
else:
|
|
133
139
|
raise ValueError("Unsupported distribution type")
|
|
134
140
|
|
|
135
141
|
def _generate_timedelta_value(self):
|
|
136
142
|
if self.distribution_type == "uniform":
|
|
137
|
-
random_days = np.random.randint(
|
|
143
|
+
random_days = np.random.randint(
|
|
144
|
+
self.timedelta_days_min_safe, self.timedelta_days_max_safe + 1
|
|
145
|
+
)
|
|
138
146
|
|
|
139
147
|
elif self.distribution_type == "normal":
|
|
140
|
-
random_days = int(
|
|
141
|
-
|
|
148
|
+
random_days = int(
|
|
149
|
+
np.random.normal(
|
|
150
|
+
self.timedelta_days_mean_safe, self.timedelta_days_std_dev_safe
|
|
151
|
+
)
|
|
152
|
+
)
|
|
153
|
+
random_days = np.clip(
|
|
154
|
+
random_days, self.timedelta_days_min_safe, self.timedelta_days_max_safe
|
|
155
|
+
)
|
|
142
156
|
|
|
143
157
|
else:
|
|
144
158
|
raise ValueError("Unsupported distribution type")
|
|
@@ -19,7 +19,9 @@ class MultipleCategoricalValueDistribution(BaseValueDistribution):
|
|
|
19
19
|
categories = models.JSONField() # { "category": "probability", ... }
|
|
20
20
|
min_count = models.IntegerField()
|
|
21
21
|
max_count = models.IntegerField()
|
|
22
|
-
count_distribution_type = models.CharField(
|
|
22
|
+
count_distribution_type = models.CharField(
|
|
23
|
+
max_length=20, choices=[("uniform", "Uniform"), ("normal", "Normal")]
|
|
24
|
+
)
|
|
23
25
|
count_mean = models.FloatField(null=True, blank=True)
|
|
24
26
|
count_std_dev = models.FloatField(null=True, blank=True)
|
|
25
27
|
|
|
@@ -35,11 +35,27 @@ class NumericValueDistribution(BaseValueDistribution):
|
|
|
35
35
|
distribution_type = models.CharField(max_length=20, choices=DISTRIBUTION_CHOICES)
|
|
36
36
|
min_descriptor = models.CharField(max_length=20)
|
|
37
37
|
max_descriptor = models.CharField(max_length=20)
|
|
38
|
-
min_value = models.FloatField(
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
38
|
+
min_value = models.FloatField(
|
|
39
|
+
blank=True, null=True, help_text="Lower hard limit for generated values"
|
|
40
|
+
)
|
|
41
|
+
max_value = models.FloatField(
|
|
42
|
+
blank=True, null=True, help_text="Upper hard limit for generated values"
|
|
43
|
+
)
|
|
44
|
+
mean = models.FloatField(
|
|
45
|
+
blank=True,
|
|
46
|
+
null=True,
|
|
47
|
+
help_text="Mean used for normal or skewed normal distributions",
|
|
48
|
+
)
|
|
49
|
+
std_dev = models.FloatField(
|
|
50
|
+
blank=True,
|
|
51
|
+
null=True,
|
|
52
|
+
help_text="Standard deviation for bell-shaped distributions",
|
|
53
|
+
)
|
|
54
|
+
skewness = models.FloatField(
|
|
55
|
+
blank=True,
|
|
56
|
+
null=True,
|
|
57
|
+
help_text="Shape parameter for skewed normal distributions",
|
|
58
|
+
)
|
|
43
59
|
|
|
44
60
|
@property
|
|
45
61
|
def min_value_safe(self):
|
|
@@ -74,7 +90,9 @@ class NumericValueDistribution(BaseValueDistribution):
|
|
|
74
90
|
def generate_value(self, lab_value: "LabValue", patient: "Patient"):
|
|
75
91
|
"""Generate a value based on the distribution rules."""
|
|
76
92
|
|
|
77
|
-
default_normal_range_dict = lab_value.get_normal_range(
|
|
93
|
+
default_normal_range_dict = lab_value.get_normal_range(
|
|
94
|
+
patient.age_safe, patient.gender
|
|
95
|
+
)
|
|
78
96
|
assert isinstance(default_normal_range_dict, dict)
|
|
79
97
|
|
|
80
98
|
if self.distribution_type == "uniform":
|
|
@@ -150,7 +168,10 @@ class NumericValueDistribution(BaseValueDistribution):
|
|
|
150
168
|
value_function_dict = self.parse_value_descriptor(self.min_descriptor)
|
|
151
169
|
value_function_dict.update(self.parse_value_descriptor(self.max_descriptor))
|
|
152
170
|
|
|
153
|
-
result_dict = {
|
|
171
|
+
result_dict = {
|
|
172
|
+
key: value_function(default_normal_range_dict[key])
|
|
173
|
+
for key, value_function in value_function_dict.items()
|
|
174
|
+
}
|
|
154
175
|
|
|
155
176
|
# generate value
|
|
156
177
|
return float(np.random.uniform(result_dict["min"], result_dict["max"]))
|
|
@@ -179,8 +200,12 @@ class NumericValueDistribution(BaseValueDistribution):
|
|
|
179
200
|
|
|
180
201
|
def _validate_normal_parameters(self) -> None:
|
|
181
202
|
if self.mean is None or self.std_dev is None:
|
|
182
|
-
raise ValueError(
|
|
203
|
+
raise ValueError(
|
|
204
|
+
f"Normal distribution '{getattr(self, 'name', self.pk)}' requires both mean and std_dev."
|
|
205
|
+
)
|
|
183
206
|
|
|
184
207
|
def _validate_skewed_normal_parameters(self) -> None:
|
|
185
208
|
if self.mean is None or self.std_dev is None or self.skewness is None:
|
|
186
|
-
raise ValueError(
|
|
209
|
+
raise ValueError(
|
|
210
|
+
f"Skewed normal distribution '{getattr(self, 'name', self.pk)}' requires mean, std_dev, and skewness."
|
|
211
|
+
)
|
|
@@ -86,9 +86,15 @@ class EmissionFactor(models.Model):
|
|
|
86
86
|
from ...administration.product import ReferenceProduct
|
|
87
87
|
|
|
88
88
|
reference_products = []
|
|
89
|
-
reference_products += ReferenceProduct.objects.filter(
|
|
90
|
-
|
|
91
|
-
|
|
89
|
+
reference_products += ReferenceProduct.objects.filter(
|
|
90
|
+
emission_factor_total=self
|
|
91
|
+
)
|
|
92
|
+
reference_products += ReferenceProduct.objects.filter(
|
|
93
|
+
emission_factor_package=self
|
|
94
|
+
)
|
|
95
|
+
reference_products += ReferenceProduct.objects.filter(
|
|
96
|
+
emission_factor_product=self
|
|
97
|
+
)
|
|
92
98
|
|
|
93
99
|
return reference_products
|
|
94
100
|
|
|
@@ -58,19 +58,27 @@ class InformationSource(models.Model):
|
|
|
58
58
|
def examinations(self) -> "models.manager.RelatedManager[Examination]": ...
|
|
59
59
|
|
|
60
60
|
@property
|
|
61
|
-
def examination_indications(
|
|
61
|
+
def examination_indications(
|
|
62
|
+
self,
|
|
63
|
+
) -> "models.manager.RelatedManager[ExaminationIndication]": ...
|
|
62
64
|
|
|
63
65
|
@property
|
|
64
|
-
def examination_times(
|
|
66
|
+
def examination_times(
|
|
67
|
+
self,
|
|
68
|
+
) -> "models.manager.RelatedManager[ExaminationTime]": ...
|
|
65
69
|
|
|
66
70
|
@property
|
|
67
71
|
def findings(self) -> "models.manager.RelatedManager[Finding]": ...
|
|
68
72
|
|
|
69
73
|
@property
|
|
70
|
-
def finding_interventions(
|
|
74
|
+
def finding_interventions(
|
|
75
|
+
self,
|
|
76
|
+
) -> "models.manager.RelatedManager[FindingIntervention]": ...
|
|
71
77
|
|
|
72
78
|
@property
|
|
73
|
-
def finding_classifications(
|
|
79
|
+
def finding_classifications(
|
|
80
|
+
self,
|
|
81
|
+
) -> "models.manager.RelatedManager[FindingClassification]": ...
|
|
74
82
|
|
|
75
83
|
class Meta:
|
|
76
84
|
verbose_name = "Information Source"
|
|
@@ -145,7 +153,9 @@ class InformationSourceType(models.Model):
|
|
|
145
153
|
try:
|
|
146
154
|
return cls.objects.get(name="prediction")
|
|
147
155
|
except cls.DoesNotExist as e:
|
|
148
|
-
raise cls.DoesNotExist(
|
|
156
|
+
raise cls.DoesNotExist(
|
|
157
|
+
"The 'prediction' InformationSourceType was not found. Please check your data fixtures or initial data migrations."
|
|
158
|
+
) from e
|
|
149
159
|
|
|
150
160
|
@classmethod
|
|
151
161
|
def get_manual_annotation_type(cls) -> "InformationSourceType":
|
|
@@ -16,7 +16,9 @@ class Material(models.Model):
|
|
|
16
16
|
objects = MaterialManager()
|
|
17
17
|
|
|
18
18
|
name = models.CharField(max_length=255)
|
|
19
|
-
emission_factor = models.ForeignKey(
|
|
19
|
+
emission_factor = models.ForeignKey(
|
|
20
|
+
"EmissionFactor", on_delete=models.SET_NULL, null=True
|
|
21
|
+
)
|
|
20
22
|
|
|
21
23
|
if TYPE_CHECKING:
|
|
22
24
|
emission_factor: models.ForeignKey["EmissionFactor|None"]
|
|
@@ -20,7 +20,9 @@ class TransportRoute(models.Model):
|
|
|
20
20
|
|
|
21
21
|
distance = models.FloatField()
|
|
22
22
|
name = models.CharField(max_length=255)
|
|
23
|
-
emission_factor = models.ForeignKey(
|
|
23
|
+
emission_factor = models.ForeignKey(
|
|
24
|
+
"EmissionFactor", on_delete=models.SET_NULL, null=True
|
|
25
|
+
)
|
|
24
26
|
unit = models.ForeignKey("Unit", on_delete=models.SET_NULL, null=True)
|
|
25
27
|
|
|
26
28
|
if TYPE_CHECKING:
|
endoreg_db/models/other/unit.py
CHANGED
|
@@ -15,8 +15,12 @@ class Unit(models.Model):
|
|
|
15
15
|
objects = UnitManager()
|
|
16
16
|
|
|
17
17
|
name = models.CharField(max_length=100) # e.g. "Centimeter"
|
|
18
|
-
description = models.CharField(
|
|
19
|
-
|
|
18
|
+
description = models.CharField(
|
|
19
|
+
max_length=100, blank=True, null=True
|
|
20
|
+
) # e.g. "centimeters", "milimeters", "inches"
|
|
21
|
+
abbreviation = models.CharField(
|
|
22
|
+
max_length=25, blank=True, null=True
|
|
23
|
+
) # e.g. "cm", "mm", "in"
|
|
20
24
|
|
|
21
25
|
if TYPE_CHECKING:
|
|
22
26
|
unit_product_materials: models.QuerySet["ProductMaterial"]
|