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
endoreg_db/admin.py
CHANGED
|
@@ -11,6 +11,7 @@ from endoreg_db.models import (
|
|
|
11
11
|
FindingIntervention, # Import Finding Interventions
|
|
12
12
|
PatientFindingIntervention,
|
|
13
13
|
)
|
|
14
|
+
|
|
14
15
|
# from endoreg_db.forms.patient_finding_intervention_form import (
|
|
15
16
|
# PatientFindingInterventionForm,
|
|
16
17
|
# )
|
|
@@ -29,7 +30,7 @@ class PatientAdmin(admin.ModelAdmin):
|
|
|
29
30
|
@admin.register(Examination)
|
|
30
31
|
class ExaminationAdmin(admin.ModelAdmin):
|
|
31
32
|
list_display = ("id", "name")
|
|
32
|
-
search_fields = ("name",
|
|
33
|
+
search_fields = ("name",)
|
|
33
34
|
list_filter = ("name",)
|
|
34
35
|
ordering = ("name",)
|
|
35
36
|
|
|
@@ -45,9 +46,13 @@ class PatientFindingInterventionAdmin(admin.ModelAdmin):
|
|
|
45
46
|
"patients": Patient.objects.all(),
|
|
46
47
|
"examinations": Examination.objects.all(),
|
|
47
48
|
"findings": Finding.objects.all(),
|
|
48
|
-
"locations": FindingClassification.objects.filter(
|
|
49
|
+
"locations": FindingClassification.objects.filter(
|
|
50
|
+
classification_types__name__iexact="location"
|
|
51
|
+
),
|
|
49
52
|
"location_choices": FindingClassificationChoice.objects.none(),
|
|
50
|
-
"morphologies": FindingClassification.objects.filter(
|
|
53
|
+
"morphologies": FindingClassification.objects.filter(
|
|
54
|
+
classification_types__name__iexact="morphology"
|
|
55
|
+
),
|
|
51
56
|
"morphology_choices": FindingClassificationChoice.objects.none(),
|
|
52
57
|
"finding_interventions": FindingIntervention.objects.all(),
|
|
53
58
|
}
|
|
@@ -56,7 +61,7 @@ class PatientFindingInterventionAdmin(admin.ModelAdmin):
|
|
|
56
61
|
def get_location_choices_json(self, request):
|
|
57
62
|
"""
|
|
58
63
|
Handles AJAX requests to retrieve location classification choices as JSON.
|
|
59
|
-
|
|
64
|
+
|
|
60
65
|
Expects a "location" parameter in the GET request and returns a list of matching FindingClassificationChoice objects with their IDs and names. Returns an error message with appropriate HTTP status if the parameter is missing or an exception occurs.
|
|
61
66
|
"""
|
|
62
67
|
location_id = request.GET.get("location")
|
|
@@ -67,7 +72,7 @@ class PatientFindingInterventionAdmin(admin.ModelAdmin):
|
|
|
67
72
|
choices = list(
|
|
68
73
|
FindingClassificationChoice.objects.filter(
|
|
69
74
|
classifications__id=location_id,
|
|
70
|
-
classifications__classification_types__name__iexact="location"
|
|
75
|
+
classifications__classification_types__name__iexact="location",
|
|
71
76
|
).values("id", "name")
|
|
72
77
|
)
|
|
73
78
|
if not choices:
|
endoreg_db/apps.py
CHANGED
|
@@ -2,17 +2,14 @@ from django.apps import AppConfig
|
|
|
2
2
|
|
|
3
3
|
|
|
4
4
|
class EndoregDbConfig(AppConfig):
|
|
5
|
-
default_auto_field =
|
|
6
|
-
name =
|
|
7
|
-
|
|
5
|
+
default_auto_field = "django.db.models.BigAutoField"
|
|
6
|
+
name = "endoreg_db"
|
|
7
|
+
|
|
8
8
|
def ready(self):
|
|
9
9
|
"""
|
|
10
10
|
Performs application startup tasks when the Django app is fully loaded.
|
|
11
|
-
|
|
11
|
+
|
|
12
12
|
This method imports media-related model modules to ensure they are registered
|
|
13
13
|
and ready for use when the application starts.
|
|
14
14
|
"""
|
|
15
|
-
import endoreg_db.models.media.video
|
|
16
|
-
import endoreg_db.models.media.frame
|
|
17
|
-
import endoreg_db.models.media.pdf
|
|
18
15
|
pass
|
endoreg_db/authz/auth.py
CHANGED
endoreg_db/authz/backends.py
CHANGED
|
@@ -115,7 +115,7 @@ class KeycloakOIDCBackend(OIDCAuthenticationBackend):
|
|
|
115
115
|
# Keep user profile in sync with IdP data (safe truncation to field max length)
|
|
116
116
|
user.email = claims.get("email", user.email)
|
|
117
117
|
user.first_name = (claims.get("given_name") or user.first_name)[:150]
|
|
118
|
-
user.last_name
|
|
118
|
+
user.last_name = (claims.get("family_name") or user.last_name)[:150]
|
|
119
119
|
user.save(update_fields=["email", "first_name", "last_name"])
|
|
120
120
|
|
|
121
121
|
# Keep roles (groups) in sync on every login
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
from django.core.management.base import BaseCommand
|
|
2
2
|
from django.urls import get_resolver, URLPattern, URLResolver
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
def iter_patterns(prefix, patterns):
|
|
5
6
|
for p in patterns:
|
|
6
7
|
if isinstance(p, URLPattern):
|
|
@@ -8,6 +9,7 @@ def iter_patterns(prefix, patterns):
|
|
|
8
9
|
elif isinstance(p, URLResolver):
|
|
9
10
|
yield from iter_patterns(prefix, p.url_patterns)
|
|
10
11
|
|
|
12
|
+
|
|
11
13
|
class Command(BaseCommand):
|
|
12
14
|
help = "List all URL names (useful to fill policy.py)"
|
|
13
15
|
|
endoreg_db/authz/middleware.py
CHANGED
|
@@ -20,13 +20,11 @@
|
|
|
20
20
|
# though using a relative path from request.get_full_path() is already safe.
|
|
21
21
|
|
|
22
22
|
from django.shortcuts import redirect
|
|
23
|
-
from django.conf import settings
|
|
24
|
-
from urllib.parse import urlencode
|
|
25
23
|
|
|
26
24
|
# Any URL path that starts with one of these prefixes is considered "protected" for browser UX.
|
|
27
25
|
# You can add more prefixes if you want the same login-redirect behavior elsewhere
|
|
28
26
|
# (e.g., PROTECTED_PREFIXES = ("/api/", "/reports/", "/dashboard/")).
|
|
29
|
-
#PROTECTED_PREFIXES = ("/api/",)
|
|
27
|
+
# PROTECTED_PREFIXES = ("/api/",)
|
|
30
28
|
|
|
31
29
|
# Protect the SPA shell too (everything except static/assets/oidc)
|
|
32
30
|
PROTECTED_PREFIXES = ("/",) # catch-all; we'll skip known public paths below
|
|
@@ -36,10 +34,11 @@ PUBLIC_PREFIXES = (
|
|
|
36
34
|
"/assets/",
|
|
37
35
|
"/media/",
|
|
38
36
|
"/favicon.ico",
|
|
39
|
-
"/oidc/",
|
|
40
|
-
"/__vite",
|
|
37
|
+
"/oidc/", # OIDC endpoints must stay public
|
|
38
|
+
"/__vite", # if Vite dev assets ever used
|
|
41
39
|
)
|
|
42
40
|
|
|
41
|
+
|
|
43
42
|
class LoginRequiredForAPIsMiddleware:
|
|
44
43
|
"""
|
|
45
44
|
For browser traffic:
|
|
@@ -51,7 +50,8 @@ class LoginRequiredForAPIsMiddleware:
|
|
|
51
50
|
|
|
52
51
|
"""
|
|
53
52
|
|
|
54
|
-
def __init__(self, get_response):
|
|
53
|
+
def __init__(self, get_response):
|
|
54
|
+
self.get_response = get_response
|
|
55
55
|
|
|
56
56
|
def __call__(self, request):
|
|
57
57
|
# request.path is the URL path without scheme/host/query (e.g., "/api/patients/").
|
|
@@ -76,8 +76,9 @@ class LoginRequiredForAPIsMiddleware:
|
|
|
76
76
|
if not request.user.is_authenticated:
|
|
77
77
|
from django.conf import settings
|
|
78
78
|
from urllib.parse import urlencode
|
|
79
|
+
|
|
79
80
|
params = urlencode({"next": request.get_full_path()})
|
|
80
81
|
return redirect(f"{settings.LOGIN_URL}?{params}")
|
|
81
82
|
|
|
82
83
|
# 4) Authenticated → pass through
|
|
83
|
-
return self.get_response(request)
|
|
84
|
+
return self.get_response(request)
|
endoreg_db/authz/permissions.py
CHANGED
|
@@ -29,11 +29,12 @@ from rest_framework.permissions import BasePermission
|
|
|
29
29
|
from django.contrib.auth.models import AnonymousUser
|
|
30
30
|
from django.utils.functional import cached_property
|
|
31
31
|
from endoreg_db.utils.permissions import is_debug_mode
|
|
32
|
-
from .policy import REQUIRED_ROLES,
|
|
32
|
+
from .policy import REQUIRED_ROLES, satisfies, get_needed_role
|
|
33
33
|
import logging
|
|
34
34
|
|
|
35
35
|
logger = logging.getLogger(__name__)
|
|
36
36
|
|
|
37
|
+
|
|
37
38
|
def _normalized_route_name(request, view) -> str:
|
|
38
39
|
"""
|
|
39
40
|
Return a stable, de-namespaced route name, e.g. 'patient-list'.
|
|
@@ -51,6 +52,7 @@ def _normalized_route_name(request, view) -> str:
|
|
|
51
52
|
return url_name
|
|
52
53
|
return view.__class__.__name__
|
|
53
54
|
|
|
55
|
+
|
|
54
56
|
def _route_name(request, view):
|
|
55
57
|
"""
|
|
56
58
|
Resolve a stable name for the current endpoint.
|
|
@@ -95,8 +97,12 @@ class PolicyPermission(BasePermission):
|
|
|
95
97
|
|
|
96
98
|
# 1) DEBUG bypass
|
|
97
99
|
if is_debug_mode():
|
|
98
|
-
logger.info(
|
|
99
|
-
|
|
100
|
+
logger.info(
|
|
101
|
+
"RBAC BYPASS (DEBUG): route=%s method=%s user=%s",
|
|
102
|
+
route,
|
|
103
|
+
method,
|
|
104
|
+
getattr(getattr(request, "user", None), "username", "anon"),
|
|
105
|
+
)
|
|
100
106
|
return True
|
|
101
107
|
|
|
102
108
|
# 2) Must be authenticated
|
|
@@ -108,11 +114,12 @@ class PolicyPermission(BasePermission):
|
|
|
108
114
|
# 3) Determine needed role
|
|
109
115
|
needed = get_needed_role(route, method)
|
|
110
116
|
if not needed:
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
117
|
+
logger.info(
|
|
118
|
+
"RBAC DENY (NO ROLE): route=%s method=%s reason=no mapping",
|
|
119
|
+
route,
|
|
120
|
+
method,
|
|
121
|
+
)
|
|
122
|
+
return False
|
|
116
123
|
|
|
117
124
|
# 4) Collect roles and decide
|
|
118
125
|
user_roles = set(user.groups.values_list("name", flat=True))
|
|
@@ -120,8 +127,12 @@ class PolicyPermission(BasePermission):
|
|
|
120
127
|
|
|
121
128
|
logger.info(
|
|
122
129
|
"RBAC DECISION: route=%s method=%s need=%s user=%s roles=%s => %s",
|
|
123
|
-
route,
|
|
124
|
-
|
|
130
|
+
route,
|
|
131
|
+
method,
|
|
132
|
+
needed,
|
|
133
|
+
getattr(user, "username", "anon"),
|
|
134
|
+
sorted(user_roles),
|
|
135
|
+
"ALLOW" if allowed else "DENY",
|
|
125
136
|
)
|
|
126
137
|
|
|
127
138
|
return allowed
|
endoreg_db/authz/policy.py
CHANGED
|
@@ -52,7 +52,7 @@ RESOURCE_ROLES = {
|
|
|
52
52
|
"read": "video:read",
|
|
53
53
|
"write": "video:write",
|
|
54
54
|
},
|
|
55
|
-
#anonymization resource
|
|
55
|
+
# anonymization resource
|
|
56
56
|
"anonymization": {
|
|
57
57
|
"read": "anonymization:read",
|
|
58
58
|
"write": "anonymization:write",
|
|
@@ -75,20 +75,15 @@ RESOURCE_ROLES = {
|
|
|
75
75
|
# exactly that "name"
|
|
76
76
|
ROUTE_RESOURCE = {
|
|
77
77
|
# Patients
|
|
78
|
-
"patient-list":
|
|
79
|
-
"patient-detail": "patient",
|
|
80
|
-
|
|
78
|
+
"patient-list": "patient", # /api/patients/
|
|
79
|
+
"patient-detail": "patient", # /api/patients/{id}/
|
|
81
80
|
# Custom patient helper
|
|
82
81
|
"check_pe_exist": "patient",
|
|
83
|
-
|
|
84
82
|
# Example for videos (if you have these ViewSets registered)
|
|
85
|
-
"videos-list":
|
|
83
|
+
"videos-list": "video",
|
|
86
84
|
"videos-detail": "video",
|
|
87
|
-
|
|
88
85
|
"anonymization_items_overview": "anonymization",
|
|
89
86
|
# Add more mappings as your API grows
|
|
90
|
-
|
|
91
|
-
|
|
92
87
|
}
|
|
93
88
|
|
|
94
89
|
# ------------------------------------------------------------
|
|
@@ -108,7 +103,6 @@ REQUIRED_ROLES: RouteRoles = {
|
|
|
108
103
|
# "patient-detail": {
|
|
109
104
|
# "DELETE": "admin", # admin role in Keycloak
|
|
110
105
|
# },
|
|
111
|
-
|
|
112
106
|
# Example: a special helper route that you always want read-only patients role:
|
|
113
107
|
# "check_pe_exist": "patient:read",
|
|
114
108
|
}
|
|
@@ -125,21 +119,21 @@ REQUIRED_ROLES: RouteRoles = {
|
|
|
125
119
|
# If you move fully to resource-based roles, you can leave this as None
|
|
126
120
|
# or a generic "data:read"/"data:write" depending on your preference.
|
|
127
121
|
DEFAULT_ROLE_BY_METHOD = {
|
|
128
|
-
"GET":
|
|
129
|
-
"HEAD":
|
|
122
|
+
"GET": "data:read",
|
|
123
|
+
"HEAD": "data:read",
|
|
130
124
|
"OPTIONS": "data:read",
|
|
131
|
-
"POST":
|
|
132
|
-
"PUT":
|
|
133
|
-
"PATCH":
|
|
134
|
-
"DELETE":
|
|
125
|
+
"POST": "data:write",
|
|
126
|
+
"PUT": "data:write",
|
|
127
|
+
"PATCH": "data:write",
|
|
128
|
+
"DELETE": "data:write",
|
|
135
129
|
}
|
|
136
130
|
|
|
137
131
|
|
|
138
|
-
|
|
139
132
|
# ------------------------------------------------------------
|
|
140
133
|
# Role satisfaction rule
|
|
141
134
|
# ------------------------------------------------------------
|
|
142
135
|
|
|
136
|
+
|
|
143
137
|
def satisfies(user_roles: set[str], needed: str) -> bool:
|
|
144
138
|
"""
|
|
145
139
|
Return True if user_roles satisfy the needed role.
|
|
@@ -160,8 +154,8 @@ def satisfies(user_roles: set[str], needed: str) -> bool:
|
|
|
160
154
|
"""
|
|
161
155
|
if not needed:
|
|
162
156
|
return False
|
|
163
|
-
|
|
164
|
-
|
|
157
|
+
|
|
158
|
+
# Global override: any user with role "endoregdb_user" passes all checks
|
|
165
159
|
if "endoregdb_user" in user_roles:
|
|
166
160
|
return True
|
|
167
161
|
|
|
@@ -182,6 +176,7 @@ def satisfies(user_roles: set[str], needed: str) -> bool:
|
|
|
182
176
|
# Helper: compute which role is needed for a given route + method
|
|
183
177
|
# ------------------------------------------------------------
|
|
184
178
|
|
|
179
|
+
|
|
185
180
|
def get_needed_role(route_name: str, method: str) -> str | None:
|
|
186
181
|
"""
|
|
187
182
|
Compute the required role for a given route + HTTP method.
|
endoreg_db/authz/views_auth.py
CHANGED
|
@@ -4,7 +4,7 @@ from rest_framework.decorators import api_view, permission_classes
|
|
|
4
4
|
from rest_framework.permissions import IsAuthenticated
|
|
5
5
|
from rest_framework.response import Response
|
|
6
6
|
|
|
7
|
-
from .policy import
|
|
7
|
+
from .policy import satisfies, get_needed_role
|
|
8
8
|
|
|
9
9
|
# Map frontend page keys → (DRF route name, HTTP method)
|
|
10
10
|
#
|
|
@@ -40,7 +40,7 @@ def auth_bootstrap(request):
|
|
|
40
40
|
method = method.upper()
|
|
41
41
|
|
|
42
42
|
# Look up which role is needed for this route/method
|
|
43
|
-
#needed = REQUIRED_ROLES.get(route_name) or DEFAULT_ROLE_BY_METHOD.get(method)
|
|
43
|
+
# needed = REQUIRED_ROLES.get(route_name) or DEFAULT_ROLE_BY_METHOD.get(method)
|
|
44
44
|
needed = get_needed_role(route_name, method)
|
|
45
45
|
|
|
46
46
|
if not needed:
|
|
@@ -48,7 +48,9 @@ def auth_bootstrap(request):
|
|
|
48
48
|
capabilities[cap_key] = {"read": False, "write": False}
|
|
49
49
|
continue
|
|
50
50
|
|
|
51
|
-
allowed = satisfies(
|
|
51
|
+
allowed = satisfies(
|
|
52
|
+
roles, needed
|
|
53
|
+
) # uses your existing rule: write ⇒ read, etc.
|
|
52
54
|
|
|
53
55
|
# For UI pages we usually only care about "read"
|
|
54
56
|
capabilities[cap_key] = {
|
|
@@ -56,11 +58,13 @@ def auth_bootstrap(request):
|
|
|
56
58
|
"write": False, # or bool(allowed) if this page allows writes in UI
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
return Response(
|
|
60
|
-
|
|
61
|
-
"
|
|
61
|
+
return Response(
|
|
62
|
+
{
|
|
63
|
+
"user": {
|
|
64
|
+
"username": user.username,
|
|
65
|
+
"roles": sorted(roles),
|
|
66
|
+
},
|
|
62
67
|
"roles": sorted(roles),
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
})
|
|
68
|
+
"capabilities": capabilities,
|
|
69
|
+
}
|
|
70
|
+
)
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
# endoreg_db/codemods/rename_datetime_fields.py
|
|
2
2
|
from bowler import Query
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
import argparse
|
|
4
|
+
import argparse
|
|
5
|
+
import yaml
|
|
6
|
+
import sys
|
|
5
7
|
|
|
6
8
|
# Paths
|
|
7
9
|
BASE = Path(__file__).resolve().parents[1] # .../endoreg_db
|
|
@@ -9,6 +11,7 @@ RENAMES_YML = BASE / "renames.yml"
|
|
|
9
11
|
DEFAULT_TARGETS = ["endoreg_db/models"] # safer default
|
|
10
12
|
EXCLUDE_DIR_NAMES = {"migrations", "__pycache__"}
|
|
11
13
|
|
|
14
|
+
|
|
12
15
|
def load_renames():
|
|
13
16
|
if not RENAMES_YML.exists():
|
|
14
17
|
print(f"ERROR: renames.yml not found at {RENAMES_YML}", file=sys.stderr)
|
|
@@ -19,6 +22,7 @@ def load_renames():
|
|
|
19
22
|
sys.exit(2)
|
|
20
23
|
return data
|
|
21
24
|
|
|
25
|
+
|
|
22
26
|
def iter_python_targets(paths):
|
|
23
27
|
"""Yield *.py files under given paths, excluding migrations and caches."""
|
|
24
28
|
for p in map(Path, paths):
|
|
@@ -31,10 +35,12 @@ def iter_python_targets(paths):
|
|
|
31
35
|
continue
|
|
32
36
|
yield str(f)
|
|
33
37
|
|
|
38
|
+
|
|
34
39
|
def build_query(files):
|
|
35
40
|
# Bowler can take a list of files; we’ve already filtered them
|
|
36
41
|
return Query(list(files))
|
|
37
42
|
|
|
43
|
+
|
|
38
44
|
def main(argv=None):
|
|
39
45
|
parser = argparse.ArgumentParser(
|
|
40
46
|
description="Rename legacy datetime fields to standardized names."
|
|
@@ -88,5 +94,6 @@ def main(argv=None):
|
|
|
88
94
|
)
|
|
89
95
|
return 0
|
|
90
96
|
|
|
97
|
+
|
|
91
98
|
if __name__ == "__main__":
|
|
92
99
|
raise SystemExit(main())
|
endoreg_db/exceptions.py
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
1
|
import logging
|
|
2
|
+
|
|
2
3
|
logger = logging.getLogger(__name__)
|
|
3
4
|
|
|
4
5
|
|
|
5
6
|
class InsufficientStorageError(Exception):
|
|
6
7
|
"""Raised when there's not enough disk space for an operation."""
|
|
7
|
-
|
|
8
|
+
|
|
8
9
|
def __init__(self, message, required_space=None, available_space=None):
|
|
9
10
|
super().__init__(message)
|
|
10
11
|
self.required_space = required_space
|
|
11
12
|
self.available_space = available_space
|
|
12
|
-
|
|
13
|
+
|
|
13
14
|
|
|
14
15
|
class TranscodingError(Exception):
|
|
15
16
|
"""Raised when video transcoding fails."""
|
|
17
|
+
|
|
16
18
|
pass
|
|
17
19
|
|
|
18
20
|
|
|
19
21
|
class VideoProcessingError(Exception):
|
|
20
22
|
"""Base class for video processing errors."""
|
|
23
|
+
|
|
21
24
|
pass
|
endoreg_db/forms/__init__.py
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
from django import forms
|
|
2
2
|
from endoreg_db.models.medical import Examination
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
class ExaminationForm(forms.ModelForm):
|
|
5
6
|
class Meta:
|
|
6
7
|
model = Examination
|
|
7
|
-
fields =
|
|
8
|
+
fields = "__all__"
|
|
8
9
|
widgets = {
|
|
9
|
-
|
|
10
|
-
|
|
10
|
+
"date": forms.DateInput(attrs={"type": "date", "class": "form-control"}),
|
|
11
|
+
"time": forms.TimeInput(attrs={"type": "time", "class": "form-control"}),
|
|
11
12
|
}
|
|
@@ -1,18 +1,40 @@
|
|
|
1
1
|
from django import forms
|
|
2
2
|
from endoreg_db.models import (
|
|
3
|
-
Patient,
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
Patient,
|
|
4
|
+
PatientExamination,
|
|
5
|
+
Finding,
|
|
6
|
+
FindingClassification,
|
|
7
|
+
FindingClassificationChoice,
|
|
8
|
+
FindingIntervention,
|
|
9
|
+
PatientFindingIntervention,
|
|
6
10
|
)
|
|
7
11
|
|
|
12
|
+
|
|
8
13
|
class PatientFindingInterventionForm(forms.ModelForm):
|
|
9
14
|
patient = forms.ModelChoiceField(queryset=Patient.objects.all(), required=True)
|
|
10
|
-
examination = forms.ModelChoiceField(
|
|
15
|
+
examination = forms.ModelChoiceField(
|
|
16
|
+
queryset=PatientExamination.objects.none(), required=True
|
|
17
|
+
)
|
|
11
18
|
finding = forms.ModelChoiceField(queryset=Finding.objects.none(), required=True)
|
|
12
|
-
classification = forms.ModelChoiceField(
|
|
13
|
-
|
|
14
|
-
|
|
19
|
+
classification = forms.ModelChoiceField(
|
|
20
|
+
queryset=FindingClassification.objects.none(), required=True
|
|
21
|
+
)
|
|
22
|
+
classification_choice = forms.ModelChoiceField(
|
|
23
|
+
queryset=FindingClassificationChoice.objects.none(), required=True
|
|
24
|
+
)
|
|
25
|
+
interventions = forms.ModelMultipleChoiceField(
|
|
26
|
+
queryset=FindingIntervention.objects.none(),
|
|
27
|
+
widget=forms.CheckboxSelectMultiple,
|
|
28
|
+
required=True,
|
|
29
|
+
)
|
|
15
30
|
|
|
16
31
|
class Meta:
|
|
17
32
|
model = PatientFindingIntervention
|
|
18
|
-
fields = [
|
|
33
|
+
fields = [
|
|
34
|
+
"patient",
|
|
35
|
+
"examination",
|
|
36
|
+
"finding",
|
|
37
|
+
"classification",
|
|
38
|
+
"classification_choice",
|
|
39
|
+
"interventions",
|
|
40
|
+
]
|
endoreg_db/forms/patient_form.py
CHANGED
|
@@ -1,27 +1,23 @@
|
|
|
1
1
|
from django import forms
|
|
2
2
|
from django.forms import ModelForm, Select
|
|
3
|
-
from endoreg_db.models import
|
|
4
|
-
Patient,
|
|
5
|
-
Gender,
|
|
6
|
-
Center
|
|
7
|
-
)
|
|
3
|
+
from endoreg_db.models import Patient, Gender, Center
|
|
8
4
|
|
|
9
5
|
|
|
10
6
|
class PatientForm(ModelForm):
|
|
11
7
|
gender = forms.ModelChoiceField(
|
|
12
|
-
queryset=Gender.objects.all(),
|
|
13
|
-
empty_label="Select Gender",
|
|
14
|
-
widget=Select(attrs={
|
|
8
|
+
queryset=Gender.objects.all(),
|
|
9
|
+
empty_label="Select Gender",
|
|
10
|
+
widget=Select(attrs={"class": "form-control"}),
|
|
15
11
|
)
|
|
16
12
|
center = forms.ModelChoiceField(
|
|
17
|
-
queryset=Center.objects.all(),
|
|
18
|
-
empty_label="Select Center",
|
|
19
|
-
widget=Select(attrs={
|
|
13
|
+
queryset=Center.objects.all(),
|
|
14
|
+
empty_label="Select Center",
|
|
15
|
+
widget=Select(attrs={"class": "form-control"}),
|
|
20
16
|
)
|
|
21
17
|
|
|
22
18
|
class Meta:
|
|
23
19
|
model = Patient
|
|
24
|
-
fields =
|
|
20
|
+
fields = "__all__"
|
|
25
21
|
widgets = {
|
|
26
|
-
|
|
22
|
+
"dob": forms.DateInput(attrs={"type": "date", "class": "form-control"}),
|
|
27
23
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
# from .tto_questionnaire import TtoQuestionnaireForm, TtoQuestionnaireCreate
|
|
1
|
+
# from .tto_questionnaire import TtoQuestionnaireForm, TtoQuestionnaireCreate
|
|
@@ -2,7 +2,10 @@
|
|
|
2
2
|
from django import forms
|
|
3
3
|
from endoreg_db.models import ActiveModel
|
|
4
4
|
|
|
5
|
+
|
|
5
6
|
class ActiveModelForm(forms.ModelForm):
|
|
6
7
|
class Meta:
|
|
7
8
|
model = ActiveModel
|
|
8
|
-
fields = [
|
|
9
|
+
fields = [
|
|
10
|
+
"model_meta"
|
|
11
|
+
] # Or list the specific fields you want to include in the form.
|
endoreg_db/forms/unit.py
CHANGED
endoreg_db/helpers/count_db.py
CHANGED
|
@@ -1,29 +1,30 @@
|
|
|
1
|
-
# endoreg_db/helpers/count_db.py
|
|
1
|
+
# endoreg_db/helpers/count_db.py
|
|
2
2
|
from endoreg_db.models.state.audit_ledger import AuditLedger
|
|
3
3
|
|
|
4
|
+
|
|
4
5
|
def _distinct(object_type: str, action: str):
|
|
5
6
|
"""
|
|
6
7
|
Returns the count of distinct primary keys for records in AuditLedger filtered by object type and action.
|
|
7
|
-
|
|
8
|
+
|
|
8
9
|
Args:
|
|
9
10
|
object_type: The type of object to filter by.
|
|
10
11
|
action: The action to filter by.
|
|
11
|
-
|
|
12
|
+
|
|
12
13
|
Returns:
|
|
13
14
|
The number of unique object primary keys matching the specified type and action.
|
|
14
15
|
"""
|
|
15
16
|
return (
|
|
16
|
-
AuditLedger.objects
|
|
17
|
-
.
|
|
18
|
-
.values('object_pk')
|
|
17
|
+
AuditLedger.objects.filter(object_type=object_type, action=action)
|
|
18
|
+
.values("object_pk")
|
|
19
19
|
.distinct()
|
|
20
20
|
.count()
|
|
21
21
|
)
|
|
22
22
|
|
|
23
|
+
|
|
23
24
|
def collect_counters():
|
|
24
25
|
"""
|
|
25
26
|
Returns a dictionary with statistical counts for cases, videos, annotations, anonymizations, images, and video statuses.
|
|
26
|
-
|
|
27
|
+
|
|
27
28
|
The returned dictionary includes:
|
|
28
29
|
- "totalCases": Count of distinct created video files.
|
|
29
30
|
- "totalVideos": Count of distinct created video files.
|
|
@@ -34,12 +35,14 @@ def collect_counters():
|
|
|
34
35
|
- "videosAnonym": Count of distinct video files that have been anonymized.
|
|
35
36
|
"""
|
|
36
37
|
return {
|
|
37
|
-
#TODO @maxhild can we remove the totalCases key?
|
|
38
|
+
# TODO @maxhild can we remove the totalCases key?
|
|
38
39
|
# "totalCases": _distinct("VideoFile", "created"),
|
|
39
|
-
"totalVideos":
|
|
40
|
-
"totalAnnotations":
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
"
|
|
44
|
-
"
|
|
40
|
+
"totalVideos": _distinct("VideoFile", "created"),
|
|
41
|
+
"totalAnnotations": AuditLedger.objects.filter(
|
|
42
|
+
action="annotation_added"
|
|
43
|
+
).count(),
|
|
44
|
+
"totalAnonymizations": _distinct("VideoFile", "anonymized"),
|
|
45
|
+
"totalImages": _distinct("Image", "created"),
|
|
46
|
+
"videosCompleted": _distinct("VideoFile", "validated"),
|
|
47
|
+
"videosAnonym": _distinct("VideoFile", "anonymized"),
|
|
45
48
|
}
|
|
@@ -8,7 +8,7 @@ from typing import Optional
|
|
|
8
8
|
from django.conf import settings # Import settings
|
|
9
9
|
from django.core.files.storage import default_storage # Import default storage
|
|
10
10
|
from django.db.models.fields.files import FieldFile
|
|
11
|
-
|
|
11
|
+
from endoreg_db.utils.file_operations import sha256_file
|
|
12
12
|
from endoreg_db.models import (
|
|
13
13
|
AiModel,
|
|
14
14
|
Center,
|
|
@@ -408,6 +408,7 @@ def get_default_video_file():
|
|
|
408
408
|
center_name=DEFAULT_CENTER_NAME, # Pass center name as expected by _create_from_file
|
|
409
409
|
delete_source=False, # Keep the original asset for other tests
|
|
410
410
|
processor_name=DEFAULT_ENDOSCOPY_PROCESSOR_NAME,
|
|
411
|
+
video_hash=sha256_file(video_path),
|
|
411
412
|
)
|
|
412
413
|
|
|
413
414
|
return video_file
|