endoreg-db 0.8.4.4__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_ai_model_data.py +2 -1
- 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 +14 -10
- 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 +249 -177
- 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_1.py +30 -33
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +359 -204
- 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 +139 -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 +383 -43
- 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 +26 -57
- 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/__init__.py +1 -1
- endoreg_db/serializers/misc/file_overview.py +33 -91
- endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
- 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/serializers/video_examination.py +198 -0
- endoreg_db/services/anonymization.py +75 -73
- endoreg_db/services/lookup_service.py +256 -73
- endoreg_db/services/lookup_store.py +174 -30
- endoreg_db/services/pdf_import.py +711 -310
- endoreg_db/services/storage_aware_video_processor.py +140 -114
- endoreg_db/services/video_import.py +266 -117
- endoreg_db/urls/__init__.py +27 -27
- endoreg_db/urls/label_video_segments.py +2 -0
- endoreg_db/urls/media.py +108 -66
- 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 +5 -12
- 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 -150
- 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 +187 -260
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/pdf/__init__.py +5 -8
- endoreg_db/views/pdf/pdf_stream.py +186 -0
- endoreg_db/views/pdf/pdf_stream_views.py +0 -127
- endoreg_db/views/pdf/reimport.py +86 -91
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +186 -288
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +0 -4
- endoreg_db/views/video/correction.py +2 -2
- endoreg_db/views/video/video_examination_viewset.py +202 -289
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +350 -255
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/media/video/refactor_plan.md +0 -0
- 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/pdf/pdf_media.py +0 -239
- 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/video/video_media.py +0 -158
- 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-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -9,14 +9,18 @@ This is separate from the existing pdf.PDFMediaView which handles legacy workflo
|
|
|
9
9
|
|
|
10
10
|
import logging
|
|
11
11
|
import os
|
|
12
|
-
from
|
|
12
|
+
from pathlib import Path
|
|
13
|
+
|
|
14
|
+
from django.db.models import Q
|
|
15
|
+
from django.http import FileResponse, Http404
|
|
16
|
+
from django.views.decorators.clickjacking import xframe_options_exempt
|
|
13
17
|
from rest_framework import status
|
|
14
18
|
from rest_framework.response import Response
|
|
15
19
|
from rest_framework.views import APIView
|
|
16
|
-
from django.db.models import Q
|
|
17
20
|
|
|
18
21
|
from endoreg_db.models import RawPdfFile
|
|
19
22
|
from endoreg_db.utils.permissions import EnvironmentAwarePermission
|
|
23
|
+
from endoreg_db.utils.storage import file_exists
|
|
20
24
|
|
|
21
25
|
logger = logging.getLogger(__name__)
|
|
22
26
|
|
|
@@ -24,25 +28,25 @@ logger = logging.getLogger(__name__)
|
|
|
24
28
|
class PdfMediaView(APIView):
|
|
25
29
|
"""
|
|
26
30
|
PDF Media Management API for CRUD operations on PDF files.
|
|
27
|
-
|
|
31
|
+
|
|
28
32
|
Endpoints:
|
|
29
33
|
- GET /api/media/pdfs/ - List all PDFs with filtering
|
|
30
34
|
- GET /api/media/pdfs/{id}/ - Get PDF details
|
|
31
35
|
- GET /api/media/pdfs/{id}/stream/ - Stream PDF file (same as detail for PDFs)
|
|
32
36
|
- PATCH /api/media/pdfs/{id}/ - Update PDF metadata (future)
|
|
33
37
|
- DELETE /api/media/pdfs/{id}/ - Delete PDF (future)
|
|
34
|
-
|
|
38
|
+
|
|
35
39
|
Query Parameters:
|
|
36
40
|
- status: Filter by processing status (not_started, done, 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/pdfs/?status=done&search=exam
|
|
43
47
|
- GET /api/media/pdfs/123/
|
|
44
48
|
- GET /api/media/pdfs/123/stream/
|
|
45
|
-
|
|
49
|
+
|
|
46
50
|
Phase 1.2 Implementation:
|
|
47
51
|
- List and detail views implemented
|
|
48
52
|
- PDF streaming functionality
|
|
@@ -50,25 +54,26 @@ class PdfMediaView(APIView):
|
|
|
50
54
|
- Pagination support
|
|
51
55
|
- Error handling with proper HTTP status codes
|
|
52
56
|
"""
|
|
57
|
+
|
|
53
58
|
permission_classes = [EnvironmentAwarePermission]
|
|
54
59
|
|
|
55
60
|
def get(self, request, pk=None):
|
|
56
61
|
"""
|
|
57
62
|
Handle GET requests for PDF listing, detail retrieval, or streaming.
|
|
58
|
-
|
|
63
|
+
|
|
59
64
|
Args:
|
|
60
65
|
request: HTTP request object
|
|
61
66
|
pk: Optional PDF ID for detail view or streaming
|
|
62
|
-
|
|
67
|
+
|
|
63
68
|
Returns:
|
|
64
69
|
Response or FileResponse: JSON response with PDF data or PDF file stream
|
|
65
|
-
|
|
70
|
+
|
|
66
71
|
Raises:
|
|
67
72
|
Http404: If specific PDF not found
|
|
68
73
|
"""
|
|
69
74
|
if pk is not None:
|
|
70
75
|
# Check if this is a streaming request
|
|
71
|
-
if request.path.endswith(
|
|
76
|
+
if request.path.endswith("/stream/"):
|
|
72
77
|
return self._stream_pdf(pk)
|
|
73
78
|
else:
|
|
74
79
|
# Detail view
|
|
@@ -80,13 +85,13 @@ class PdfMediaView(APIView):
|
|
|
80
85
|
def _get_pdf_detail(self, pk):
|
|
81
86
|
"""
|
|
82
87
|
Get detailed information for a specific PDF.
|
|
83
|
-
|
|
88
|
+
|
|
84
89
|
Args:
|
|
85
90
|
pk: PDF primary key
|
|
86
|
-
|
|
91
|
+
|
|
87
92
|
Returns:
|
|
88
93
|
Response: JSON response with PDF details
|
|
89
|
-
|
|
94
|
+
|
|
90
95
|
Raises:
|
|
91
96
|
Http404: If PDF not found
|
|
92
97
|
"""
|
|
@@ -96,54 +101,54 @@ class PdfMediaView(APIView):
|
|
|
96
101
|
pdf_id_int = int(pk)
|
|
97
102
|
except (ValueError, TypeError):
|
|
98
103
|
raise Http404("Invalid PDF ID format")
|
|
99
|
-
|
|
104
|
+
|
|
100
105
|
# Fetch PDF with related data
|
|
101
|
-
pdf = RawPdfFile.objects.select_related(
|
|
102
|
-
|
|
106
|
+
pdf = RawPdfFile.objects.select_related("sensitive_meta").get(pk=pdf_id_int)
|
|
107
|
+
|
|
103
108
|
# Build PDF details
|
|
104
109
|
pdf_data = {
|
|
105
|
-
"id": pdf.
|
|
106
|
-
"filename": getattr(pdf.file,
|
|
107
|
-
"file_size": getattr(pdf.file,
|
|
110
|
+
"id": pdf.pk,
|
|
111
|
+
"filename": getattr(pdf.file, "name", "Unknown"),
|
|
112
|
+
"file_size": getattr(pdf.file, "size", 0),
|
|
108
113
|
"pdf_hash": pdf.pdf_hash,
|
|
109
|
-
"uploaded_at": pdf.
|
|
114
|
+
"uploaded_at": pdf.date_created.isoformat() if getattr(pdf, "date_created", None) else None,
|
|
110
115
|
"anonymized_text": pdf.anonymized_text,
|
|
111
116
|
"has_anonymized_text": bool(pdf.anonymized_text and pdf.anonymized_text.strip()),
|
|
112
|
-
"is_validated": getattr(pdf.sensitive_meta,
|
|
113
|
-
"stream_url": self.request.build_absolute_uri(f"/api/media/pdfs/{pdf.
|
|
117
|
+
"is_validated": getattr(pdf.sensitive_meta, "is_verified", False) if pdf.sensitive_meta else False,
|
|
118
|
+
"stream_url": self.request.build_absolute_uri(f"/api/media/pdfs/{pdf.pk}/stream/"),
|
|
114
119
|
}
|
|
115
|
-
|
|
120
|
+
|
|
116
121
|
# Add patient metadata if available
|
|
117
122
|
if pdf.sensitive_meta:
|
|
118
|
-
pdf_data.update(
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
123
|
+
pdf_data.update(
|
|
124
|
+
{
|
|
125
|
+
"patient_first_name": pdf.sensitive_meta.patient_first_name,
|
|
126
|
+
"patient_last_name": pdf.sensitive_meta.patient_last_name,
|
|
127
|
+
"patient_dob": pdf.sensitive_meta.patient_dob.strftime("%d.%m.%Y") if pdf.sensitive_meta.patient_dob else None,
|
|
128
|
+
"examination_date": pdf.sensitive_meta.examination_date.strftime("%d.%m.%Y") if pdf.sensitive_meta.examination_date else None,
|
|
129
|
+
}
|
|
130
|
+
)
|
|
131
|
+
|
|
125
132
|
return Response(pdf_data)
|
|
126
|
-
|
|
133
|
+
|
|
127
134
|
except RawPdfFile.DoesNotExist:
|
|
128
135
|
raise Http404(f"PDF with ID {pk} not found")
|
|
129
|
-
|
|
136
|
+
|
|
130
137
|
except Exception as e:
|
|
131
138
|
logger.error(f"Unexpected error in PDF detail view for ID {pk}: {str(e)}")
|
|
132
|
-
return Response(
|
|
133
|
-
{"error": "Failed to retrieve PDF details"},
|
|
134
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
135
|
-
)
|
|
139
|
+
return Response({"error": "Failed to retrieve PDF details"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
136
140
|
|
|
141
|
+
@xframe_options_exempt
|
|
137
142
|
def _stream_pdf(self, pk):
|
|
138
143
|
"""
|
|
139
144
|
Stream PDF file content for viewing/download.
|
|
140
|
-
|
|
145
|
+
|
|
141
146
|
Args:
|
|
142
147
|
pk: PDF primary key
|
|
143
|
-
|
|
148
|
+
|
|
144
149
|
Returns:
|
|
145
150
|
FileResponse: PDF file stream
|
|
146
|
-
|
|
151
|
+
|
|
147
152
|
Raises:
|
|
148
153
|
Http404: If PDF not found or file cannot be accessed
|
|
149
154
|
"""
|
|
@@ -153,45 +158,41 @@ class PdfMediaView(APIView):
|
|
|
153
158
|
pdf_id_int = int(pk)
|
|
154
159
|
except (ValueError, TypeError):
|
|
155
160
|
raise Http404("Invalid PDF ID format")
|
|
156
|
-
|
|
161
|
+
|
|
157
162
|
# Fetch PDF
|
|
158
163
|
pdf = RawPdfFile.objects.get(pk=pdf_id_int)
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
164
|
+
|
|
165
|
+
file_field = pdf.file
|
|
166
|
+
|
|
167
|
+
if not file_field or not file_field.name:
|
|
162
168
|
raise Http404("PDF file not found")
|
|
163
|
-
|
|
169
|
+
if not file_exists(file_field):
|
|
170
|
+
raise Http404("PDF file does not exist in storage")
|
|
171
|
+
|
|
164
172
|
try:
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
raise Http404("PDF file does not exist on disk")
|
|
169
|
-
|
|
170
|
-
# Create file response
|
|
171
|
-
response = FileResponse(
|
|
172
|
-
open(file_path, 'rb'),
|
|
173
|
-
content_type='application/pdf',
|
|
174
|
-
as_attachment=False # View in browser, not download
|
|
175
|
-
)
|
|
176
|
-
|
|
177
|
-
# Set filename for browser
|
|
178
|
-
filename = os.path.basename(pdf.file.name)
|
|
179
|
-
response['Content-Disposition'] = f'inline; filename="{filename}"'
|
|
180
|
-
|
|
181
|
-
# CORS headers for frontend access
|
|
182
|
-
frontend_origin = os.environ.get('FRONTEND_ORIGIN', 'http://localhost:8000')
|
|
183
|
-
response['Access-Control-Allow-Origin'] = frontend_origin
|
|
184
|
-
response['Access-Control-Allow-Credentials'] = 'true'
|
|
185
|
-
|
|
186
|
-
return response
|
|
187
|
-
|
|
188
|
-
except (OSError, IOError) as e:
|
|
189
|
-
logger.error(f"File access error for PDF {pk}: {str(e)}")
|
|
173
|
+
file_field.open("rb")
|
|
174
|
+
except Exception as exc: # pragma: no cover - backend-specific failure
|
|
175
|
+
logger.error("File access error for PDF %s: %s", pk, exc)
|
|
190
176
|
raise Http404("PDF file cannot be accessed")
|
|
191
|
-
|
|
177
|
+
|
|
178
|
+
response = FileResponse(
|
|
179
|
+
file_field,
|
|
180
|
+
content_type="application/pdf",
|
|
181
|
+
as_attachment=False,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
filename = Path(file_field.name).name
|
|
185
|
+
response["Content-Disposition"] = f'inline; filename="{filename}"'
|
|
186
|
+
|
|
187
|
+
frontend_origin = os.environ.get("FRONTEND_ORIGIN", "http://localhost:8000")
|
|
188
|
+
response["Access-Control-Allow-Origin"] = frontend_origin
|
|
189
|
+
response["Access-Control-Allow-Credentials"] = "true"
|
|
190
|
+
|
|
191
|
+
return response
|
|
192
|
+
|
|
192
193
|
except RawPdfFile.DoesNotExist:
|
|
193
194
|
raise Http404(f"PDF with ID {pk} not found")
|
|
194
|
-
|
|
195
|
+
|
|
195
196
|
except Exception as e:
|
|
196
197
|
logger.error(f"Unexpected error in PDF streaming for ID {pk}: {str(e)}")
|
|
197
198
|
raise Http404("PDF file cannot be streamed")
|
|
@@ -199,53 +200,51 @@ class PdfMediaView(APIView):
|
|
|
199
200
|
def _list_pdfs(self, request):
|
|
200
201
|
"""
|
|
201
202
|
List PDFs with filtering, search, and pagination.
|
|
202
|
-
|
|
203
|
+
|
|
203
204
|
Args:
|
|
204
205
|
request: HTTP request with query parameters
|
|
205
|
-
|
|
206
|
+
|
|
206
207
|
Returns:
|
|
207
208
|
Response: JSON response with paginated PDF list
|
|
208
209
|
"""
|
|
209
210
|
try:
|
|
210
211
|
# Start with all PDFs
|
|
211
|
-
queryset = RawPdfFile.objects.select_related(
|
|
212
|
-
|
|
212
|
+
queryset = RawPdfFile.objects.select_related("sensitive_meta").all()
|
|
213
|
+
|
|
213
214
|
# Apply filters
|
|
214
215
|
queryset = self._apply_filters(queryset, request.query_params)
|
|
215
|
-
|
|
216
|
+
|
|
216
217
|
# Apply search
|
|
217
|
-
search = request.query_params.get(
|
|
218
|
+
search = request.query_params.get("search", "").strip()
|
|
218
219
|
if search:
|
|
219
|
-
queryset = queryset.filter(
|
|
220
|
-
|
|
221
|
-
)
|
|
222
|
-
|
|
220
|
+
queryset = queryset.filter(Q(file__icontains=search))
|
|
221
|
+
|
|
223
222
|
# Order by upload date (newest first) or id if no upload date
|
|
224
|
-
if hasattr(queryset.model,
|
|
225
|
-
queryset = queryset.order_by(
|
|
223
|
+
if hasattr(queryset.model, "date_created"):
|
|
224
|
+
queryset = queryset.order_by("-date_created")
|
|
226
225
|
else:
|
|
227
|
-
queryset = queryset.order_by(
|
|
228
|
-
|
|
226
|
+
queryset = queryset.order_by("-pk")
|
|
227
|
+
|
|
229
228
|
# Apply pagination
|
|
230
|
-
limit = min(int(request.query_params.get(
|
|
231
|
-
offset = int(request.query_params.get(
|
|
232
|
-
|
|
229
|
+
limit = min(int(request.query_params.get("limit", 50)), 100)
|
|
230
|
+
offset = int(request.query_params.get("offset", 0))
|
|
231
|
+
|
|
233
232
|
total_count = queryset.count()
|
|
234
|
-
pdfs = queryset[offset:offset + limit]
|
|
235
|
-
|
|
233
|
+
pdfs = queryset[offset : offset + limit]
|
|
234
|
+
|
|
236
235
|
# Serialize PDFs manually (no dedicated serializer yet)
|
|
237
236
|
results = []
|
|
238
237
|
for pdf in pdfs:
|
|
239
238
|
pdf_item = {
|
|
240
|
-
"id": pdf.
|
|
241
|
-
"filename": getattr(pdf.file,
|
|
239
|
+
"id": pdf.pk,
|
|
240
|
+
"filename": getattr(pdf.file, "name", "Unknown"),
|
|
242
241
|
"file_size": self._safe_get_file_size(pdf.file),
|
|
243
242
|
"pdf_hash": pdf.pdf_hash,
|
|
244
243
|
"has_anonymized_text": bool(pdf.anonymized_text and pdf.anonymized_text.strip()),
|
|
245
|
-
"is_validated": getattr(pdf.sensitive_meta,
|
|
246
|
-
"stream_url": request.build_absolute_uri(f"/api/media/pdfs/{pdf.
|
|
244
|
+
"is_validated": getattr(pdf.sensitive_meta, "is_verified", False) if pdf.sensitive_meta else False,
|
|
245
|
+
"stream_url": request.build_absolute_uri(f"/api/media/pdfs/{pdf.pk}/stream/"),
|
|
247
246
|
}
|
|
248
|
-
|
|
247
|
+
|
|
249
248
|
# Determine status based on anonymization and validation
|
|
250
249
|
if not pdf.anonymized_text or not pdf.anonymized_text.strip():
|
|
251
250
|
pdf_item["status"] = "not_started"
|
|
@@ -253,42 +252,38 @@ class PdfMediaView(APIView):
|
|
|
253
252
|
pdf_item["status"] = "validated"
|
|
254
253
|
else:
|
|
255
254
|
pdf_item["status"] = "done"
|
|
256
|
-
|
|
255
|
+
|
|
257
256
|
results.append(pdf_item)
|
|
258
|
-
|
|
259
|
-
return Response({
|
|
260
|
-
"count": total_count,
|
|
261
|
-
"next": self._get_next_url(request, offset, limit, total_count),
|
|
262
|
-
"previous": self._get_previous_url(request, offset, limit),
|
|
263
|
-
"results": results
|
|
264
|
-
})
|
|
265
|
-
|
|
266
|
-
except ValueError as e:
|
|
257
|
+
|
|
267
258
|
return Response(
|
|
268
|
-
{
|
|
269
|
-
|
|
259
|
+
{
|
|
260
|
+
"count": total_count,
|
|
261
|
+
"next": self._get_next_url(request, offset, limit, total_count),
|
|
262
|
+
"previous": self._get_previous_url(request, offset, limit),
|
|
263
|
+
"results": results,
|
|
264
|
+
}
|
|
270
265
|
)
|
|
271
|
-
|
|
266
|
+
|
|
267
|
+
except ValueError as e:
|
|
268
|
+
return Response({"error": f"Invalid query parameter: {str(e)}"}, status=status.HTTP_400_BAD_REQUEST)
|
|
269
|
+
|
|
272
270
|
except Exception as e:
|
|
273
271
|
logger.error(f"Unexpected error in PDF list view: {str(e)}")
|
|
274
|
-
return Response(
|
|
275
|
-
{"error": "Failed to retrieve PDF list"},
|
|
276
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
277
|
-
)
|
|
272
|
+
return Response({"error": "Failed to retrieve PDF list"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
278
273
|
|
|
279
274
|
def _safe_get_file_size(self, file_field):
|
|
280
275
|
"""
|
|
281
276
|
Safely get file size without causing errors if file doesn't exist.
|
|
282
|
-
|
|
277
|
+
|
|
283
278
|
Args:
|
|
284
279
|
file_field: Django FileField
|
|
285
|
-
|
|
280
|
+
|
|
286
281
|
Returns:
|
|
287
282
|
int: File size in bytes, or 0 if file doesn't exist
|
|
288
283
|
"""
|
|
289
284
|
if not file_field or not file_field.name:
|
|
290
285
|
return 0
|
|
291
|
-
|
|
286
|
+
|
|
292
287
|
try:
|
|
293
288
|
return file_field.size
|
|
294
289
|
except (OSError, IOError, ValueError):
|
|
@@ -298,44 +293,36 @@ class PdfMediaView(APIView):
|
|
|
298
293
|
def _apply_filters(self, queryset, query_params):
|
|
299
294
|
"""
|
|
300
295
|
Apply status and other filters to PDF queryset.
|
|
301
|
-
|
|
296
|
+
|
|
302
297
|
Args:
|
|
303
298
|
queryset: Base queryset to filter
|
|
304
299
|
query_params: Request query parameters
|
|
305
|
-
|
|
300
|
+
|
|
306
301
|
Returns:
|
|
307
302
|
QuerySet: Filtered queryset
|
|
308
303
|
"""
|
|
309
|
-
status_filter = query_params.get(
|
|
310
|
-
|
|
304
|
+
status_filter = query_params.get("status", "").strip().lower()
|
|
305
|
+
|
|
311
306
|
if status_filter:
|
|
312
|
-
if status_filter ==
|
|
307
|
+
if status_filter == "not_started":
|
|
313
308
|
# PDFs without anonymized text
|
|
314
|
-
queryset = queryset.filter(
|
|
315
|
-
|
|
316
|
-
)
|
|
317
|
-
elif status_filter == 'done':
|
|
309
|
+
queryset = queryset.filter(Q(anonymized_text__isnull=True) | Q(anonymized_text__exact=""))
|
|
310
|
+
elif status_filter == "done":
|
|
318
311
|
# PDFs with anonymized text but not validated
|
|
319
312
|
queryset = queryset.filter(
|
|
320
|
-
~Q(anonymized_text__isnull=True),
|
|
321
|
-
~Q(anonymized_text__exact=''),
|
|
322
|
-
Q(sensitive_meta__is_verified=False) | Q(sensitive_meta__isnull=True)
|
|
313
|
+
~Q(anonymized_text__isnull=True), ~Q(anonymized_text__exact=""), Q(sensitive_meta__is_verified=False) | Q(sensitive_meta__isnull=True)
|
|
323
314
|
)
|
|
324
|
-
elif status_filter ==
|
|
315
|
+
elif status_filter == "validated":
|
|
325
316
|
# PDFs with anonymized text and validated
|
|
326
|
-
queryset = queryset.filter(
|
|
327
|
-
|
|
328
|
-
~Q(anonymized_text__exact=''),
|
|
329
|
-
sensitive_meta__is_verified=True
|
|
330
|
-
)
|
|
331
|
-
|
|
317
|
+
queryset = queryset.filter(~Q(anonymized_text__isnull=True), ~Q(anonymized_text__exact=""), sensitive_meta__is_verified=True)
|
|
318
|
+
|
|
332
319
|
return queryset
|
|
333
320
|
|
|
334
321
|
def _get_next_url(self, request, offset, limit, total_count):
|
|
335
322
|
"""Generate next page URL for pagination."""
|
|
336
323
|
if offset + limit >= total_count:
|
|
337
324
|
return None
|
|
338
|
-
|
|
325
|
+
|
|
339
326
|
next_offset = offset + limit
|
|
340
327
|
return self._build_paginated_url(request, next_offset, limit)
|
|
341
328
|
|
|
@@ -343,16 +330,16 @@ class PdfMediaView(APIView):
|
|
|
343
330
|
"""Generate previous page URL for pagination."""
|
|
344
331
|
if offset <= 0:
|
|
345
332
|
return None
|
|
346
|
-
|
|
333
|
+
|
|
347
334
|
prev_offset = max(0, offset - limit)
|
|
348
335
|
return self._build_paginated_url(request, prev_offset, limit)
|
|
349
336
|
|
|
350
337
|
def _build_paginated_url(self, request, offset, limit):
|
|
351
338
|
"""Build URL with pagination parameters."""
|
|
352
339
|
params = request.query_params.copy()
|
|
353
|
-
params[
|
|
354
|
-
params[
|
|
355
|
-
|
|
340
|
+
params["offset"] = offset
|
|
341
|
+
params["limit"] = limit
|
|
342
|
+
|
|
356
343
|
base_url = request.build_absolute_uri(request.path)
|
|
357
344
|
if params:
|
|
358
345
|
return f"{base_url}?{params.urlencode()}"
|
|
@@ -362,25 +349,19 @@ class PdfMediaView(APIView):
|
|
|
362
349
|
def patch(self, request, pk):
|
|
363
350
|
"""
|
|
364
351
|
Update PDF metadata (Phase 1.2+ future enhancement).
|
|
365
|
-
|
|
352
|
+
|
|
366
353
|
Currently returns 501 Not Implemented.
|
|
367
354
|
"""
|
|
368
|
-
return Response(
|
|
369
|
-
{"error": "PDF metadata updates not yet implemented"},
|
|
370
|
-
status=status.HTTP_501_NOT_IMPLEMENTED
|
|
371
|
-
)
|
|
355
|
+
return Response({"error": "PDF metadata updates not yet implemented"}, status=status.HTTP_501_NOT_IMPLEMENTED)
|
|
372
356
|
|
|
373
357
|
def delete(self, request, pk):
|
|
374
358
|
"""
|
|
375
359
|
Delete PDF file (Phase 1.2+ future enhancement).
|
|
376
|
-
|
|
360
|
+
|
|
377
361
|
Currently returns 501 Not Implemented.
|
|
378
362
|
Use /api/media-management/force-remove/{id}/ instead.
|
|
379
363
|
"""
|
|
380
364
|
return Response(
|
|
381
|
-
{
|
|
382
|
-
|
|
383
|
-
"alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead"
|
|
384
|
-
},
|
|
385
|
-
status=status.HTTP_501_NOT_IMPLEMENTED
|
|
365
|
+
{"error": "PDF deletion not yet implemented", "alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead"},
|
|
366
|
+
status=status.HTTP_501_NOT_IMPLEMENTED,
|
|
386
367
|
)
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
# Modern Media Framework: Sensitive Metadata Management
|
|
2
|
+
import string
|
|
3
|
+
from numpy import number
|
|
2
4
|
from rest_framework.decorators import api_view, permission_classes
|
|
3
5
|
from rest_framework.response import Response
|
|
4
6
|
from rest_framework import status
|
|
@@ -14,6 +16,46 @@ from endoreg_db.serializers.meta import (
|
|
|
14
16
|
|
|
15
17
|
# === VIDEO SENSITIVE METADATA ===
|
|
16
18
|
|
|
19
|
+
@api_view(['GET'])
|
|
20
|
+
@permission_classes([EnvironmentAwarePermission])
|
|
21
|
+
def get_sensitive_metadata_pk(request, pk: number, mediaType: str) -> Response | None:
|
|
22
|
+
"""
|
|
23
|
+
A route to get the sensitive meta pk for a media type quickly.
|
|
24
|
+
|
|
25
|
+
GET api/media/sensitive-media-id/<pk>/<str:mediaType>
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
request (_type_): _description_
|
|
29
|
+
id (_type_): _description_
|
|
30
|
+
|
|
31
|
+
Returns:
|
|
32
|
+
Response | None: _description_
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
if mediaType == 'video':
|
|
36
|
+
video = get_object_or_404(VideoFile, pk=pk)
|
|
37
|
+
if not video.sensitive_meta:
|
|
38
|
+
return Response(
|
|
39
|
+
{"error": f"No sensitive metadata found for video {pk}"},
|
|
40
|
+
status=status.HTTP_404_NOT_FOUND
|
|
41
|
+
)
|
|
42
|
+
sm_id = video.sensitive_meta.pk
|
|
43
|
+
return Response({
|
|
44
|
+
"sm": sm_id
|
|
45
|
+
})
|
|
46
|
+
if mediaType == 'pdf':
|
|
47
|
+
pdf = get_object_or_404(RawPdfFile, pk=pk)
|
|
48
|
+
if not pdf.sensitive_meta:
|
|
49
|
+
return Response(
|
|
50
|
+
{"error": f"No sensitive metadata found for PDF {pk}"},
|
|
51
|
+
status=status.HTTP_404_NOT_FOUND
|
|
52
|
+
)
|
|
53
|
+
sm_id = pdf.sensitive_meta.pk
|
|
54
|
+
return Response({
|
|
55
|
+
"sm": sm_id
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
|
|
17
59
|
@api_view(['GET', 'PATCH'])
|
|
18
60
|
@permission_classes([EnvironmentAwarePermission])
|
|
19
61
|
def video_sensitive_metadata(request, pk):
|
|
@@ -24,16 +66,15 @@ def video_sensitive_metadata(request, pk):
|
|
|
24
66
|
Get or update sensitive metadata for a video.
|
|
25
67
|
Video-scoped: Uses video ID to locate related sensitive metadata.
|
|
26
68
|
"""
|
|
27
|
-
|
|
69
|
+
sensitive_meta = get_object_or_404(SensitiveMeta, pk=pk)
|
|
28
70
|
|
|
29
71
|
# Get related sensitive metadata
|
|
30
|
-
if not
|
|
72
|
+
if not sensitive_meta:
|
|
31
73
|
return Response(
|
|
32
74
|
{"error": f"No sensitive metadata found for video {pk}"},
|
|
33
75
|
status=status.HTTP_404_NOT_FOUND
|
|
34
76
|
)
|
|
35
77
|
|
|
36
|
-
sensitive_meta = video.sensitive_meta
|
|
37
78
|
|
|
38
79
|
if request.method == 'GET':
|
|
39
80
|
serializer = SensitiveMetaDetailSerializer(sensitive_meta)
|
|
@@ -123,16 +164,15 @@ def pdf_sensitive_metadata(request, pk):
|
|
|
123
164
|
Get or update sensitive metadata for a PDF.
|
|
124
165
|
PDF-scoped: Uses PDF ID to locate related sensitive metadata.
|
|
125
166
|
"""
|
|
126
|
-
|
|
167
|
+
sensitive_meta = get_object_or_404(SensitiveMeta, pk=pk)
|
|
127
168
|
|
|
128
169
|
# Get related sensitive metadata
|
|
129
|
-
if not
|
|
170
|
+
if not sensitive_meta:
|
|
130
171
|
return Response(
|
|
131
172
|
{"error": f"No sensitive metadata found for PDF {pk}"},
|
|
132
173
|
status=status.HTTP_404_NOT_FOUND
|
|
133
174
|
)
|
|
134
175
|
|
|
135
|
-
sensitive_meta = pdf.sensitive_meta
|
|
136
176
|
|
|
137
177
|
if request.method == 'GET':
|
|
138
178
|
serializer = SensitiveMetaDetailSerializer(sensitive_meta)
|