endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/authz/auth.py +74 -0
- endoreg_db/authz/backends.py +168 -0
- endoreg_db/authz/management/commands/list_routes.py +18 -0
- endoreg_db/authz/middleware.py +83 -0
- endoreg_db/authz/permissions.py +127 -0
- endoreg_db/authz/policy.py +218 -0
- endoreg_db/authz/views_auth.py +66 -0
- endoreg_db/config/env.py +13 -8
- endoreg_db/data/__init__.py +8 -31
- endoreg_db/data/_examples/disease.yaml +55 -0
- endoreg_db/data/_examples/disease_classification.yaml +13 -0
- endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
- endoreg_db/data/_examples/event.yaml +64 -0
- endoreg_db/data/_examples/examination.yaml +72 -0
- endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
- endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
- endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
- endoreg_db/data/_examples/finding/complication.yaml +16 -0
- endoreg_db/data/_examples/finding/data.yaml +105 -0
- endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
- endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
- endoreg_db/data/_examples/finding/outcome.yaml +12 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
- endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
- endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
- endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
- endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
- endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
- endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
- endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
- endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
- endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/_examples/finding_type/data.yaml +43 -0
- endoreg_db/data/_examples/requirement/age.yaml +26 -0
- endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
- endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
- endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
- endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
- endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
- endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
- endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
- endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
- endoreg_db/data/_examples/requirement/gender.yaml +25 -0
- endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
- endoreg_db/data/_examples/requirement/medication.yaml +93 -0
- endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
- endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
- endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
- endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
- endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
- endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
- endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
- endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
- endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
- endoreg_db/data/requirement_set/_old_ +109 -0
- endoreg_db/data/requirement_set_type/data.yaml +21 -0
- endoreg_db/data/setup_config.yaml +4 -4
- endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
- endoreg_db/exceptions.py +5 -2
- endoreg_db/helpers/data_loader.py +1 -1
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
- endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
- endoreg_db/management/commands/import_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +1 -1
- endoreg_db/management/commands/init_default_ai_model.py +1 -1
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_requirement_data.py +60 -31
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/setup_endoreg_db.py +3 -3
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/migrations/0001_initial.py +1799 -1300
- endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
- endoreg_db/migrations/_old/0001_initial.py +1857 -0
- endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
- endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
- endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
- endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
- endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
- endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
- endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
- endoreg_db/models/__init__.py +78 -123
- endoreg_db/models/administration/__init__.py +21 -42
- endoreg_db/models/administration/ai/active_model.py +2 -2
- endoreg_db/models/administration/ai/ai_model.py +7 -6
- endoreg_db/models/administration/case/__init__.py +1 -15
- endoreg_db/models/administration/case/case.py +3 -3
- endoreg_db/models/administration/case/case_template/__init__.py +2 -14
- endoreg_db/models/administration/case/case_template/case_template.py +2 -124
- endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
- endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
- endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
- endoreg_db/models/administration/center/center.py +33 -19
- endoreg_db/models/administration/center/center_product.py +12 -9
- endoreg_db/models/administration/center/center_resource.py +25 -19
- endoreg_db/models/administration/center/center_shift.py +21 -17
- endoreg_db/models/administration/center/center_waste.py +16 -8
- endoreg_db/models/administration/person/__init__.py +2 -0
- endoreg_db/models/administration/person/employee/employee.py +10 -5
- endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
- endoreg_db/models/administration/person/employee/employee_type.py +12 -6
- endoreg_db/models/administration/person/examiner/examiner.py +13 -11
- endoreg_db/models/administration/person/patient/__init__.py +2 -0
- endoreg_db/models/administration/person/patient/patient.py +103 -100
- endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
- endoreg_db/models/administration/person/person.py +4 -0
- endoreg_db/models/administration/person/profession/__init__.py +8 -4
- endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
- endoreg_db/models/administration/product/product.py +20 -15
- endoreg_db/models/administration/product/product_material.py +17 -18
- endoreg_db/models/administration/product/product_weight.py +12 -8
- endoreg_db/models/administration/product/reference_product.py +23 -55
- endoreg_db/models/administration/qualification/qualification.py +7 -3
- endoreg_db/models/administration/qualification/qualification_type.py +7 -3
- endoreg_db/models/administration/shift/scheduled_days.py +8 -5
- endoreg_db/models/administration/shift/shift.py +16 -12
- endoreg_db/models/administration/shift/shift_type.py +23 -31
- endoreg_db/models/label/__init__.py +7 -8
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
- endoreg_db/models/label/label.py +15 -15
- endoreg_db/models/label/label_set.py +19 -6
- endoreg_db/models/label/label_type.py +1 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
- endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
- endoreg_db/models/label/video_segmentation_label.py +4 -0
- endoreg_db/models/label/video_segmentation_labelset.py +4 -3
- endoreg_db/models/media/frame/frame.py +22 -22
- endoreg_db/models/media/pdf/raw_pdf.py +110 -182
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +48 -56
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +150 -108
- endoreg_db/models/media/video/video_file_ai.py +288 -74
- endoreg_db/models/media/video/video_file_anonymize.py +38 -38
- endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
- endoreg_db/models/media/video/video_file_io.py +109 -62
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
- endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
- endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
- endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
- endoreg_db/models/media/video/video_file_segments.py +24 -17
- endoreg_db/models/media/video/video_metadata.py +19 -35
- endoreg_db/models/media/video/video_processing.py +96 -95
- endoreg_db/models/medical/contraindication/__init__.py +13 -3
- endoreg_db/models/medical/disease.py +22 -16
- endoreg_db/models/medical/event.py +31 -18
- endoreg_db/models/medical/examination/__init__.py +13 -6
- endoreg_db/models/medical/examination/examination.py +17 -18
- endoreg_db/models/medical/examination/examination_indication.py +26 -25
- endoreg_db/models/medical/examination/examination_time.py +16 -6
- endoreg_db/models/medical/examination/examination_time_type.py +9 -6
- endoreg_db/models/medical/examination/examination_type.py +3 -4
- endoreg_db/models/medical/finding/finding.py +38 -39
- endoreg_db/models/medical/finding/finding_classification.py +37 -48
- endoreg_db/models/medical/finding/finding_intervention.py +27 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +20 -26
- endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
- endoreg_db/models/medical/laboratory/lab_value.py +62 -91
- endoreg_db/models/medical/medication/medication.py +22 -10
- endoreg_db/models/medical/medication/medication_indication.py +29 -3
- endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
- endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
- endoreg_db/models/medical/medication/medication_schedule.py +27 -16
- endoreg_db/models/medical/organ/__init__.py +15 -12
- endoreg_db/models/medical/patient/medication_examples.py +1 -5
- endoreg_db/models/medical/patient/patient_disease.py +20 -23
- endoreg_db/models/medical/patient/patient_event.py +19 -22
- endoreg_db/models/medical/patient/patient_examination.py +48 -54
- endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
- endoreg_db/models/medical/patient/patient_finding.py +122 -139
- endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
- endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
- endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
- endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
- endoreg_db/models/medical/patient/patient_medication.py +27 -38
- endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
- endoreg_db/models/medical/risk/risk.py +7 -6
- endoreg_db/models/medical/risk/risk_type.py +8 -5
- endoreg_db/models/metadata/model_meta.py +60 -29
- endoreg_db/models/metadata/model_meta_logic.py +125 -18
- endoreg_db/models/metadata/pdf_meta.py +19 -24
- endoreg_db/models/metadata/sensitive_meta.py +102 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +192 -173
- endoreg_db/models/metadata/video_meta.py +51 -31
- endoreg_db/models/metadata/video_prediction_logic.py +16 -23
- endoreg_db/models/metadata/video_prediction_meta.py +29 -33
- endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
- endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
- endoreg_db/models/other/emission/emission_factor.py +18 -8
- endoreg_db/models/other/gender.py +10 -5
- endoreg_db/models/other/information_source.py +25 -25
- endoreg_db/models/other/material.py +9 -5
- endoreg_db/models/other/resource.py +6 -4
- endoreg_db/models/other/tag.py +10 -5
- endoreg_db/models/other/transport_route.py +13 -8
- endoreg_db/models/other/unit.py +10 -6
- endoreg_db/models/other/waste.py +6 -5
- endoreg_db/models/requirement/requirement.py +580 -272
- endoreg_db/models/requirement/requirement_error.py +85 -0
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
- endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
- endoreg_db/models/requirement/requirement_operator.py +36 -33
- endoreg_db/models/requirement/requirement_set.py +74 -57
- endoreg_db/models/state/__init__.py +4 -4
- endoreg_db/models/state/abstract.py +2 -2
- endoreg_db/models/state/anonymization.py +12 -0
- endoreg_db/models/state/audit_ledger.py +46 -47
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +40 -46
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +58 -53
- endoreg_db/models/upload_job.py +32 -55
- endoreg_db/models/utils.py +1 -2
- endoreg_db/root_urls.py +21 -2
- endoreg_db/serializers/__init__.py +0 -2
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/meta/report_meta.py +1 -1
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/file_overview.py +11 -99
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/services/anonymization.py +75 -73
- endoreg_db/services/lookup_service.py +37 -24
- endoreg_db/services/pdf_import.py +166 -68
- endoreg_db/services/storage_aware_video_processor.py +140 -114
- endoreg_db/services/video_import.py +193 -283
- endoreg_db/urls/__init__.py +7 -20
- endoreg_db/urls/media.py +108 -67
- endoreg_db/urls/root_urls.py +29 -0
- endoreg_db/utils/__init__.py +15 -5
- endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
- endoreg_db/utils/case_generator/__init__.py +3 -0
- endoreg_db/utils/dataloader.py +88 -16
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
- endoreg_db/utils/setup_config.py +8 -5
- endoreg_db/utils/storage.py +115 -0
- endoreg_db/utils/validate_endo_roi.py +8 -2
- endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
- endoreg_db/views/__init__.py +0 -10
- endoreg_db/views/anonymization/media_management.py +198 -163
- endoreg_db/views/anonymization/overview.py +4 -1
- endoreg_db/views/anonymization/validate.py +174 -40
- endoreg_db/views/media/__init__.py +2 -0
- endoreg_db/views/media/pdf_media.py +131 -152
- endoreg_db/views/media/sensitive_metadata.py +46 -6
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/media/video_segments.py +2 -3
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/pdf/pdf_stream.py +20 -21
- endoreg_db/views/pdf/reimport.py +11 -32
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +17 -3
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +0 -2
- endoreg_db/views/video/correction.py +2 -2
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +341 -245
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/media/video/video_file_frames.py +0 -0
- endoreg_db/models/metadata/frame_ocr_result.py +0 -0
- endoreg_db/models/rule/__init__.py +0 -13
- endoreg_db/models/rule/rule.py +0 -27
- endoreg_db/models/rule/rule_applicator.py +0 -224
- endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
- endoreg_db/models/rule/rule_type.py +0 -20
- endoreg_db/models/rule/ruleset.py +0 -17
- endoreg_db/serializers/video/video_metadata.py +0 -105
- endoreg_db/urls/report.py +0 -48
- endoreg_db/urls/video.py +0 -61
- endoreg_db/utils/case_generator/case_generator.py +0 -159
- endoreg_db/utils/case_generator/utils.py +0 -30
- endoreg_db/views/report/__init__.py +0 -9
- endoreg_db/views/report/report_list.py +0 -112
- endoreg_db/views/report/report_with_secure_url.py +0 -28
- endoreg_db/views/report/start_examination.py +0 -7
- endoreg_db/views.py +0 -0
- /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
- /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
- /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
- /endoreg_db/{models/media/video/refactor_plan.md → views/pdf/pdf_stream_views.py} +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -8,16 +8,20 @@ This complements VideoStreamView which handles the actual video streaming.
|
|
|
8
8
|
"""
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
|
+
|
|
12
|
+
from django.db.models import Q
|
|
11
13
|
from django.http import Http404
|
|
12
14
|
from rest_framework import status
|
|
13
15
|
from rest_framework.response import Response
|
|
14
16
|
from rest_framework.views import APIView
|
|
15
|
-
from django.db.models import Q
|
|
16
17
|
|
|
17
18
|
from endoreg_db.models import VideoFile
|
|
18
|
-
from endoreg_db.serializers.video.video_file_list import VideoFileListSerializer
|
|
19
19
|
from endoreg_db.serializers.video.video_file_detail import VideoDetailSerializer
|
|
20
|
+
from endoreg_db.serializers.video.video_file_list import VideoFileListSerializer
|
|
20
21
|
from endoreg_db.utils.permissions import EnvironmentAwarePermission
|
|
22
|
+
#for keycloak
|
|
23
|
+
from rest_framework.permissions import IsAuthenticated
|
|
24
|
+
from endoreg_db.authz.permissions import PolicyPermission
|
|
21
25
|
|
|
22
26
|
logger = logging.getLogger(__name__)
|
|
23
27
|
|
|
@@ -25,23 +29,23 @@ logger = logging.getLogger(__name__)
|
|
|
25
29
|
class VideoMediaView(APIView):
|
|
26
30
|
"""
|
|
27
31
|
Video Media Management API for CRUD operations on video files.
|
|
28
|
-
|
|
32
|
+
|
|
29
33
|
Endpoints:
|
|
30
34
|
- GET /api/media/videos/ - List all videos with filtering
|
|
31
35
|
- GET /api/media/videos/{id}/ - Get video details
|
|
32
36
|
- PATCH /api/media/videos/{id}/ - Update video metadata (future)
|
|
33
|
-
- DELETE /api/media/videos/{id}/ - Delete video
|
|
34
|
-
|
|
37
|
+
- DELETE /api/media/videos/{id}/ - Delete video
|
|
38
|
+
|
|
35
39
|
Query Parameters:
|
|
36
40
|
- status: Filter by processing status (not_started, processing, done, failed, validated)
|
|
37
41
|
- search: Search in filename
|
|
38
42
|
- limit: Limit results (default: 50)
|
|
39
43
|
- offset: Pagination offset
|
|
40
|
-
|
|
44
|
+
|
|
41
45
|
Examples:
|
|
42
46
|
- GET /api/media/videos/?status=done&search=exam
|
|
43
47
|
- GET /api/media/videos/123/
|
|
44
|
-
|
|
48
|
+
|
|
45
49
|
Phase 1.2 Implementation:
|
|
46
50
|
- List and detail views implemented
|
|
47
51
|
- Filtering and search functionality
|
|
@@ -49,19 +53,22 @@ class VideoMediaView(APIView):
|
|
|
49
53
|
- Error handling with proper HTTP status codes
|
|
50
54
|
- Integration with existing serializers
|
|
51
55
|
"""
|
|
52
|
-
|
|
56
|
+
|
|
57
|
+
#permission_classes = [EnvironmentAwarePermission]
|
|
58
|
+
#permission_classes = [EnvironmentAwarePermission]
|
|
59
|
+
permission_classes = [IsAuthenticated, PolicyPermission]
|
|
53
60
|
|
|
54
61
|
def get(self, request, pk=None):
|
|
55
62
|
"""
|
|
56
63
|
Handle GET requests for video listing or detail retrieval.
|
|
57
|
-
|
|
64
|
+
|
|
58
65
|
Args:
|
|
59
66
|
request: HTTP request object
|
|
60
67
|
pk: Optional video ID for detail view
|
|
61
|
-
|
|
68
|
+
|
|
62
69
|
Returns:
|
|
63
70
|
Response: JSON response with video data or list
|
|
64
|
-
|
|
71
|
+
|
|
65
72
|
Raises:
|
|
66
73
|
Http404: If specific video not found
|
|
67
74
|
"""
|
|
@@ -75,13 +82,13 @@ class VideoMediaView(APIView):
|
|
|
75
82
|
def _get_video_detail(self, pk):
|
|
76
83
|
"""
|
|
77
84
|
Get detailed information for a specific video.
|
|
78
|
-
|
|
85
|
+
|
|
79
86
|
Args:
|
|
80
87
|
pk: Video primary key
|
|
81
|
-
|
|
88
|
+
|
|
82
89
|
Returns:
|
|
83
90
|
Response: JSON response with video details
|
|
84
|
-
|
|
91
|
+
|
|
85
92
|
Raises:
|
|
86
93
|
Http404: If video not found
|
|
87
94
|
"""
|
|
@@ -91,137 +98,137 @@ class VideoMediaView(APIView):
|
|
|
91
98
|
video_id_int = int(pk)
|
|
92
99
|
except (ValueError, TypeError):
|
|
93
100
|
raise Http404("Invalid video ID format")
|
|
94
|
-
|
|
101
|
+
|
|
95
102
|
# Fetch video with related data
|
|
96
|
-
video = VideoFile.objects.select_related(
|
|
97
|
-
|
|
98
|
-
)
|
|
99
|
-
|
|
103
|
+
video = VideoFile.objects.select_related("state", "sensitive_meta").get(
|
|
104
|
+
pk=video_id_int
|
|
105
|
+
)
|
|
106
|
+
|
|
100
107
|
# Serialize with request context for URL generation
|
|
101
|
-
serializer = VideoDetailSerializer(video, context={
|
|
102
|
-
|
|
108
|
+
serializer = VideoDetailSerializer(video, context={"request": self.request})
|
|
109
|
+
|
|
103
110
|
return Response(serializer.data)
|
|
104
|
-
|
|
111
|
+
|
|
105
112
|
except VideoFile.DoesNotExist:
|
|
106
113
|
raise Http404(f"Video with ID {pk} not found")
|
|
107
|
-
|
|
114
|
+
|
|
108
115
|
except Exception as e:
|
|
109
116
|
logger.error(f"Unexpected error in video detail view for ID {pk}: {str(e)}")
|
|
110
117
|
return Response(
|
|
111
118
|
{"error": "Failed to retrieve video details"},
|
|
112
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
119
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
113
120
|
)
|
|
114
121
|
|
|
115
122
|
def _list_videos(self, request):
|
|
116
123
|
"""
|
|
117
124
|
List videos with filtering, search, and pagination.
|
|
118
|
-
|
|
125
|
+
|
|
119
126
|
Args:
|
|
120
127
|
request: HTTP request with query parameters
|
|
121
|
-
|
|
128
|
+
|
|
122
129
|
Returns:
|
|
123
130
|
Response: JSON response with paginated video list
|
|
124
131
|
"""
|
|
125
132
|
try:
|
|
126
133
|
# Start with all videos
|
|
127
|
-
queryset = VideoFile.objects.select_related(
|
|
128
|
-
|
|
134
|
+
queryset = VideoFile.objects.select_related("state", "sensitive_meta").all()
|
|
135
|
+
|
|
129
136
|
# Apply filters
|
|
130
137
|
queryset = self._apply_filters(queryset, request.query_params)
|
|
131
|
-
|
|
138
|
+
|
|
132
139
|
# Apply search
|
|
133
|
-
search = request.query_params.get(
|
|
140
|
+
search = request.query_params.get("search", "").strip()
|
|
134
141
|
if search:
|
|
135
|
-
queryset = queryset.filter(
|
|
136
|
-
|
|
137
|
-
)
|
|
138
|
-
|
|
142
|
+
queryset = queryset.filter(Q(original_file_name__icontains=search))
|
|
143
|
+
|
|
139
144
|
# Order by upload date (newest first)
|
|
140
|
-
queryset = queryset.order_by(
|
|
141
|
-
|
|
145
|
+
queryset = queryset.order_by("-uploaded_at")
|
|
146
|
+
|
|
142
147
|
# Apply pagination
|
|
143
|
-
limit = min(int(request.query_params.get(
|
|
144
|
-
offset = int(request.query_params.get(
|
|
145
|
-
|
|
148
|
+
limit = min(int(request.query_params.get("limit", 50)), 100)
|
|
149
|
+
offset = int(request.query_params.get("offset", 0))
|
|
150
|
+
|
|
146
151
|
total_count = queryset.count()
|
|
147
|
-
videos = queryset[offset:offset + limit]
|
|
148
|
-
|
|
152
|
+
videos = queryset[offset : offset + limit]
|
|
153
|
+
|
|
149
154
|
# Serialize
|
|
150
155
|
serializer = VideoFileListSerializer(videos, many=True)
|
|
151
|
-
|
|
152
|
-
return Response(
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
156
|
+
|
|
157
|
+
return Response(
|
|
158
|
+
{
|
|
159
|
+
"count": total_count,
|
|
160
|
+
"next": self._get_next_url(request, offset, limit, total_count),
|
|
161
|
+
"previous": self._get_previous_url(request, offset, limit),
|
|
162
|
+
"results": serializer.data,
|
|
163
|
+
}
|
|
164
|
+
)
|
|
165
|
+
|
|
159
166
|
except ValueError as e:
|
|
160
167
|
return Response(
|
|
161
168
|
{"error": f"Invalid query parameter: {str(e)}"},
|
|
162
|
-
status=status.HTTP_400_BAD_REQUEST
|
|
169
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
163
170
|
)
|
|
164
|
-
|
|
171
|
+
|
|
165
172
|
except Exception as e:
|
|
166
173
|
logger.error(f"Unexpected error in video list view: {str(e)}")
|
|
167
174
|
return Response(
|
|
168
175
|
{"error": "Failed to retrieve video list"},
|
|
169
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
176
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
170
177
|
)
|
|
171
178
|
|
|
172
179
|
def _apply_filters(self, queryset, query_params):
|
|
173
180
|
"""
|
|
174
181
|
Apply status and other filters to video queryset.
|
|
175
|
-
|
|
182
|
+
|
|
176
183
|
Args:
|
|
177
184
|
queryset: Base queryset to filter
|
|
178
185
|
query_params: Request query parameters
|
|
179
|
-
|
|
186
|
+
|
|
180
187
|
Returns:
|
|
181
188
|
QuerySet: Filtered queryset
|
|
182
189
|
"""
|
|
183
|
-
status_filter = query_params.get(
|
|
184
|
-
|
|
190
|
+
status_filter = query_params.get("status", "").strip().lower()
|
|
191
|
+
|
|
185
192
|
if status_filter:
|
|
186
|
-
if status_filter ==
|
|
193
|
+
if status_filter == "not_started":
|
|
187
194
|
# Videos without state or with not_started status
|
|
188
195
|
queryset = queryset.filter(
|
|
189
|
-
Q(state__isnull=True)
|
|
190
|
-
Q(
|
|
196
|
+
Q(state__isnull=True)
|
|
197
|
+
| Q(
|
|
198
|
+
state__frames_extracted=False,
|
|
199
|
+
state__sensitive_meta_processed=False,
|
|
200
|
+
)
|
|
191
201
|
)
|
|
192
|
-
elif status_filter ==
|
|
202
|
+
elif status_filter == "processing":
|
|
193
203
|
# Videos in any processing state
|
|
194
204
|
queryset = queryset.filter(
|
|
195
|
-
state__frames_extracted=True,
|
|
196
|
-
state__sensitive_meta_processed=False
|
|
205
|
+
state__frames_extracted=True, state__sensitive_meta_processed=False
|
|
197
206
|
)
|
|
198
|
-
elif status_filter ==
|
|
207
|
+
elif status_filter == "done_processing_anonymization":
|
|
199
208
|
# Videos with anonymization complete but not validated
|
|
200
209
|
queryset = queryset.filter(
|
|
201
|
-
state__anonymized=True,
|
|
202
|
-
sensitive_meta__is_verified=False
|
|
210
|
+
state__anonymized=True, sensitive_meta__is_verified=False
|
|
203
211
|
)
|
|
204
|
-
elif status_filter ==
|
|
212
|
+
elif status_filter == "validated":
|
|
205
213
|
# Videos with human validation complete
|
|
206
214
|
queryset = queryset.filter(
|
|
207
|
-
state__anonymized=True,
|
|
208
|
-
sensitive_meta__is_verified=True
|
|
215
|
+
state__anonymized=True, sensitive_meta__is_verified=True
|
|
209
216
|
)
|
|
210
|
-
elif status_filter ==
|
|
217
|
+
elif status_filter == "failed":
|
|
211
218
|
# Failed videos (this might need adjustment based on actual failure tracking)
|
|
212
219
|
queryset = queryset.filter(
|
|
213
220
|
state__isnull=False,
|
|
214
221
|
state__frames_extracted=False,
|
|
215
|
-
state__sensitive_meta_processed=False
|
|
222
|
+
state__sensitive_meta_processed=False,
|
|
216
223
|
)
|
|
217
|
-
|
|
224
|
+
|
|
218
225
|
return queryset
|
|
219
226
|
|
|
220
227
|
def _get_next_url(self, request, offset, limit, total_count):
|
|
221
228
|
"""Generate next page URL for pagination."""
|
|
222
229
|
if offset + limit >= total_count:
|
|
223
230
|
return None
|
|
224
|
-
|
|
231
|
+
|
|
225
232
|
next_offset = offset + limit
|
|
226
233
|
return self._build_paginated_url(request, next_offset, limit)
|
|
227
234
|
|
|
@@ -229,16 +236,16 @@ class VideoMediaView(APIView):
|
|
|
229
236
|
"""Generate previous page URL for pagination."""
|
|
230
237
|
if offset <= 0:
|
|
231
238
|
return None
|
|
232
|
-
|
|
239
|
+
|
|
233
240
|
prev_offset = max(0, offset - limit)
|
|
234
241
|
return self._build_paginated_url(request, prev_offset, limit)
|
|
235
242
|
|
|
236
243
|
def _build_paginated_url(self, request, offset, limit):
|
|
237
244
|
"""Build URL with pagination parameters."""
|
|
238
245
|
params = request.query_params.copy()
|
|
239
|
-
params[
|
|
240
|
-
params[
|
|
241
|
-
|
|
246
|
+
params["offset"] = offset
|
|
247
|
+
params["limit"] = limit
|
|
248
|
+
|
|
242
249
|
base_url = request.build_absolute_uri(request.path)
|
|
243
250
|
if params:
|
|
244
251
|
return f"{base_url}?{params.urlencode()}"
|
|
@@ -248,25 +255,25 @@ class VideoMediaView(APIView):
|
|
|
248
255
|
def patch(self, request, pk):
|
|
249
256
|
"""
|
|
250
257
|
Update video metadata (Phase 1.2+ future enhancement).
|
|
251
|
-
|
|
258
|
+
|
|
252
259
|
Currently returns 501 Not Implemented.
|
|
253
260
|
"""
|
|
254
261
|
return Response(
|
|
255
262
|
{"error": "Video metadata updates not yet implemented"},
|
|
256
|
-
status=status.HTTP_501_NOT_IMPLEMENTED
|
|
263
|
+
status=status.HTTP_501_NOT_IMPLEMENTED,
|
|
257
264
|
)
|
|
258
265
|
|
|
259
266
|
def delete(self, request, pk):
|
|
260
267
|
"""
|
|
261
268
|
Delete video file (Phase 1.2+ future enhancement).
|
|
262
|
-
|
|
269
|
+
|
|
263
270
|
Currently returns 501 Not Implemented.
|
|
264
271
|
Use /api/media-management/force-remove/{id}/ instead.
|
|
265
272
|
"""
|
|
266
273
|
return Response(
|
|
267
274
|
{
|
|
268
|
-
"error": "Video deletion not yet implemented",
|
|
269
|
-
"alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead"
|
|
275
|
+
"error": "Video deletion not yet implemented",
|
|
276
|
+
"alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead",
|
|
270
277
|
},
|
|
271
|
-
status=status.HTTP_501_NOT_IMPLEMENTED
|
|
278
|
+
status=status.HTTP_501_NOT_IMPLEMENTED,
|
|
272
279
|
)
|
|
@@ -288,8 +288,6 @@ def video_segment_validate(request, pk: int, segment_id: int):
|
|
|
288
288
|
# Update state
|
|
289
289
|
with transaction.atomic():
|
|
290
290
|
segment.state.is_validated = is_validated
|
|
291
|
-
if notes and hasattr(segment.state, "validation_notes"):
|
|
292
|
-
segment.state.validation_notes = notes
|
|
293
291
|
segment.state.save()
|
|
294
292
|
|
|
295
293
|
logger.info(f"Validated segment {segment_id} in video {pk}: {is_validated}")
|
|
@@ -510,7 +508,8 @@ def video_segments_validation_status(request, pk: int):
|
|
|
510
508
|
failed_count += 1
|
|
511
509
|
|
|
512
510
|
logger.info(f"Completed validation for {updated_count} segments in video {pk}")
|
|
513
|
-
|
|
511
|
+
logger.info(f"Removing Outside Segments")
|
|
512
|
+
video_file.label_video_segments.filter(video_file=video, label="outside", state__is_validated=False).delete()
|
|
514
513
|
return Response(
|
|
515
514
|
{
|
|
516
515
|
"message": f"Video segment validation completed for video {pk}",
|
|
@@ -20,69 +20,6 @@ class SensitiveMetaDetailView(APIView):
|
|
|
20
20
|
PATCH: Updates SensitiveMeta fields including verification state
|
|
21
21
|
"""
|
|
22
22
|
|
|
23
|
-
permission_classes = DEBUG_PERMISSIONS # Changed from IsAuthenticated for development
|
|
24
|
-
|
|
25
|
-
def get(self, request, sensitive_meta_id=None):
|
|
26
|
-
"""
|
|
27
|
-
Retrieve SensitiveMeta details for display and annotation.
|
|
28
|
-
|
|
29
|
-
Supports both URL parameter and query parameter access patterns:
|
|
30
|
-
- /api/pdf/sensitivemeta/123/ (URL parameter)
|
|
31
|
-
- /api/pdf/sensitivemeta/?id=123 (query parameter - for backward compatibility)
|
|
32
|
-
- /api/pdf/sensitivemeta/ (list all - returns empty list instead of 400)
|
|
33
|
-
|
|
34
|
-
Returns detailed information suitable for user verification.
|
|
35
|
-
"""
|
|
36
|
-
# Handle both URL parameter and query parameter patterns
|
|
37
|
-
if not sensitive_meta_id:
|
|
38
|
-
sensitive_meta_id = request.query_params.get('id')
|
|
39
|
-
|
|
40
|
-
# If no ID provided, return empty list instead of error
|
|
41
|
-
if not sensitive_meta_id:
|
|
42
|
-
return Response([], status=status.HTTP_200_OK)
|
|
43
|
-
|
|
44
|
-
try:
|
|
45
|
-
# Convert to int if it's a string
|
|
46
|
-
sensitive_meta_id = int(sensitive_meta_id)
|
|
47
|
-
|
|
48
|
-
# Get the SensitiveMeta instance with related data
|
|
49
|
-
sensitive_meta = SensitiveMeta.objects.select_related(
|
|
50
|
-
'center',
|
|
51
|
-
'patient_gender',
|
|
52
|
-
'pseudo_patient',
|
|
53
|
-
'pseudo_examination'
|
|
54
|
-
).prefetch_related(
|
|
55
|
-
'examiners',
|
|
56
|
-
'state'
|
|
57
|
-
).get(id=sensitive_meta_id)
|
|
58
|
-
|
|
59
|
-
# Serialize for display
|
|
60
|
-
serializer = SensitiveMetaDetailSerializer(sensitive_meta)
|
|
61
|
-
|
|
62
|
-
# Return direct data to match anonymization store expectations
|
|
63
|
-
# Instead of wrapping in "sensitive_meta" key, return data directly
|
|
64
|
-
response_data = serializer.data
|
|
65
|
-
|
|
66
|
-
logger.info(f"Retrieved SensitiveMeta {sensitive_meta_id} for user {request.user}")
|
|
67
|
-
return Response(response_data, status=status.HTTP_200_OK)
|
|
68
|
-
|
|
69
|
-
except ValueError:
|
|
70
|
-
return Response(
|
|
71
|
-
{"error": "Invalid sensitive_meta_id format. Must be an integer."},
|
|
72
|
-
status=status.HTTP_400_BAD_REQUEST
|
|
73
|
-
)
|
|
74
|
-
except SensitiveMeta.DoesNotExist:
|
|
75
|
-
return Response(
|
|
76
|
-
{"error": f"SensitiveMeta with ID {sensitive_meta_id} not found"},
|
|
77
|
-
status=status.HTTP_404_NOT_FOUND
|
|
78
|
-
)
|
|
79
|
-
except Exception as e:
|
|
80
|
-
logger.error(f"Error retrieving SensitiveMeta {sensitive_meta_id}: {e}")
|
|
81
|
-
return Response(
|
|
82
|
-
{"error": "Internal server error occurred"},
|
|
83
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
84
|
-
)
|
|
85
|
-
|
|
86
23
|
@transaction.atomic
|
|
87
24
|
def patch(self, request, sensitive_meta_id=None):
|
|
88
25
|
"""
|
|
@@ -6,11 +6,11 @@ from rest_framework import viewsets, status, serializers
|
|
|
6
6
|
from rest_framework.response import Response
|
|
7
7
|
from rest_framework.decorators import action
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
from endoreg_db.authz.permissions import PolicyPermission
|
|
10
10
|
from endoreg_db.models import Patient
|
|
11
11
|
from endoreg_db.serializers.patient import PatientSerializer
|
|
12
12
|
from endoreg_db.models.medical.patient.patient_examination import PatientExamination
|
|
13
|
-
|
|
13
|
+
from rest_framework.permissions import IsAuthenticated
|
|
14
14
|
@staff_member_required # Ensures only staff members can access the page
|
|
15
15
|
def start_examination(request):
|
|
16
16
|
return render(request, 'admin/start_examination.html') # Loads the simple HTML page
|
|
@@ -21,8 +21,9 @@ class PatientViewSet(viewsets.ModelViewSet):
|
|
|
21
21
|
"""API endpoint for managing patients."""
|
|
22
22
|
queryset = Patient.objects.all()
|
|
23
23
|
serializer_class = PatientSerializer
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
permission_classes = [PolicyPermission]
|
|
25
|
+
#permission_classes = [PolicyPermission]
|
|
26
|
+
|
|
26
27
|
def perform_create(self, serializer):
|
|
27
28
|
"""Erweiterte Validierung beim Erstellen eines Patienten"""
|
|
28
29
|
try:
|
|
@@ -3,7 +3,7 @@ import os
|
|
|
3
3
|
import re
|
|
4
4
|
|
|
5
5
|
from django.http import FileResponse, Http404, StreamingHttpResponse
|
|
6
|
-
from django.views.decorators.clickjacking import xframe_options_sameorigin
|
|
6
|
+
from django.views.decorators.clickjacking import xframe_options_exempt, xframe_options_sameorigin
|
|
7
7
|
from rest_framework.views import APIView
|
|
8
8
|
|
|
9
9
|
from endoreg_db.models import RawPdfFile
|
|
@@ -40,30 +40,29 @@ class PdfStreamView(APIView):
|
|
|
40
40
|
"""
|
|
41
41
|
Streams a PDF file with correct HTTP range support and proper file handle management.
|
|
42
42
|
|
|
43
|
-
Supports streaming both raw (original) and
|
|
43
|
+
Supports streaming both raw (original) and processed PDF files.
|
|
44
44
|
|
|
45
45
|
Query Parameters:
|
|
46
|
-
type: 'raw' (default) or '
|
|
46
|
+
type: 'raw' (default) or 'processed' - Selects which PDF file to stream
|
|
47
47
|
|
|
48
48
|
Examples:
|
|
49
49
|
GET /api/media/pdf/1/?type=raw - Stream original raw PDF
|
|
50
|
-
GET /api/media/pdf/1/?type=
|
|
50
|
+
GET /api/media/pdf/1/?type=processed - Stream processed PDF
|
|
51
51
|
"""
|
|
52
52
|
|
|
53
53
|
permission_classes = [EnvironmentAwarePermission]
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
def get(self, request, pdf_id: int, *args, **kwargs):
|
|
54
|
+
@xframe_options_exempt
|
|
55
|
+
def get(self, request, pk: int, *args, **kwargs):
|
|
57
56
|
file_type = "raw" # Initialize for error logging
|
|
58
57
|
try:
|
|
59
|
-
pdf_obj = RawPdfFile.objects.filter(pk=
|
|
58
|
+
pdf_obj = RawPdfFile.objects.filter(pk=pk).first()
|
|
60
59
|
if not pdf_obj:
|
|
61
|
-
logger.warning(f"PDF not found: ID {
|
|
60
|
+
logger.warning(f"PDF not found: ID {pk}")
|
|
62
61
|
raise Http404("PDF not found")
|
|
63
62
|
|
|
64
63
|
# Parse query parameters to determine which file to stream
|
|
65
64
|
file_type = request.query_params.get("type", "raw").lower()
|
|
66
|
-
if file_type not in ["raw", "
|
|
65
|
+
if file_type not in ["raw", "processed"]:
|
|
67
66
|
logger.warning(f"Invalid file_type '{file_type}', defaulting to 'raw'")
|
|
68
67
|
file_type = "raw"
|
|
69
68
|
|
|
@@ -71,15 +70,15 @@ class PdfStreamView(APIView):
|
|
|
71
70
|
if file_type == "raw":
|
|
72
71
|
file_field = pdf_obj.file
|
|
73
72
|
if not file_field:
|
|
74
|
-
logger.warning(f"No raw PDF file available for PDF ID {
|
|
73
|
+
logger.warning(f"No raw PDF file available for PDF ID {pk}")
|
|
75
74
|
raise Http404("Raw PDF file not available")
|
|
76
75
|
else: # anonymized
|
|
77
76
|
file_field = pdf_obj.anonymized_file
|
|
78
77
|
if not file_field:
|
|
79
78
|
logger.warning(
|
|
80
|
-
f"No
|
|
79
|
+
f"No processed PDF file available for PDF ID {pk}"
|
|
81
80
|
)
|
|
82
|
-
raise Http404("
|
|
81
|
+
raise Http404("Processed PDF file not available")
|
|
83
82
|
|
|
84
83
|
# Check if file exists on filesystem
|
|
85
84
|
try:
|
|
@@ -92,22 +91,22 @@ class PdfStreamView(APIView):
|
|
|
92
91
|
|
|
93
92
|
file_size = os.path.getsize(file_path)
|
|
94
93
|
except (OSError, IOError, AttributeError) as e:
|
|
95
|
-
logger.error(f"Error accessing {file_type} PDF file {
|
|
94
|
+
logger.error(f"Error accessing {file_type} PDF file {pk}: {e}")
|
|
96
95
|
raise Http404(f"{file_type.capitalize()} PDF file not accessible")
|
|
97
96
|
|
|
98
97
|
# Generate safe filename
|
|
99
98
|
base_filename = (
|
|
100
99
|
os.path.basename(file_field.name)
|
|
101
100
|
if file_field.name
|
|
102
|
-
else f"document_{
|
|
101
|
+
else f"document_{pk}.pdf"
|
|
103
102
|
)
|
|
104
103
|
if not base_filename.endswith(".pdf"):
|
|
105
104
|
base_filename += ".pdf"
|
|
106
105
|
|
|
107
106
|
# Add type indicator to filename for clarity
|
|
108
|
-
if file_type == "
|
|
107
|
+
if file_type == "processed":
|
|
109
108
|
name_parts = base_filename.rsplit(".", 1)
|
|
110
|
-
safe_filename = f"{name_parts[0]}
|
|
109
|
+
safe_filename = f"{name_parts[0]}_processed.{name_parts[1]}"
|
|
111
110
|
else:
|
|
112
111
|
safe_filename = base_filename
|
|
113
112
|
|
|
@@ -115,7 +114,7 @@ class PdfStreamView(APIView):
|
|
|
115
114
|
range_header = request.headers.get("Range")
|
|
116
115
|
if range_header:
|
|
117
116
|
logger.debug(
|
|
118
|
-
f"Range request for {file_type} PDF {
|
|
117
|
+
f"Range request for {file_type} PDF {pk}: {range_header}"
|
|
119
118
|
)
|
|
120
119
|
match = _RANGE_RE.match(range_header)
|
|
121
120
|
if match:
|
|
@@ -139,7 +138,7 @@ class PdfStreamView(APIView):
|
|
|
139
138
|
file_handle.seek(start)
|
|
140
139
|
|
|
141
140
|
logger.debug(
|
|
142
|
-
f"Serving {file_type} PDF {
|
|
141
|
+
f"Serving {file_type} PDF {pk} range {start}-{end}/{file_size}"
|
|
143
142
|
)
|
|
144
143
|
|
|
145
144
|
response = StreamingHttpResponse(
|
|
@@ -164,7 +163,7 @@ class PdfStreamView(APIView):
|
|
|
164
163
|
logger.warning(f"Invalid Range header format: {range_header}")
|
|
165
164
|
|
|
166
165
|
# Serve entire file using FileResponse (automatically handles file closing)
|
|
167
|
-
logger.debug(f"Serving full {file_type} PDF {
|
|
166
|
+
logger.debug(f"Serving full {file_type} PDF {pk} ({file_size} bytes)")
|
|
168
167
|
|
|
169
168
|
try:
|
|
170
169
|
file_handle = open(file_path, "rb")
|
|
@@ -181,7 +180,7 @@ class PdfStreamView(APIView):
|
|
|
181
180
|
|
|
182
181
|
except Exception as e:
|
|
183
182
|
logger.error(
|
|
184
|
-
f"Unexpected error streaming {file_type if 'file_type' in locals() else 'PDF'} {
|
|
183
|
+
f"Unexpected error streaming {file_type if 'file_type' in locals() else 'PDF'} {pk}: {e}",
|
|
185
184
|
exc_info=True,
|
|
186
185
|
)
|
|
187
186
|
raise Http404("Error streaming PDF")
|