endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.9__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 +2 -11
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +3 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/examination/examinations/data.yaml +114 -14
- endoreg_db/data/examination/time-type/data.yaml +0 -3
- endoreg_db/data/examination_indication/endoscopy.yaml +108 -173
- endoreg_db/data/examination_indication_classification/endoscopy.yaml +0 -70
- endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +33 -37
- endoreg_db/data/finding/00_generic.yaml +35 -0
- endoreg_db/data/finding/00_generic_complication.yaml +9 -0
- endoreg_db/data/finding/01_gastroscopy_baseline.yaml +88 -0
- endoreg_db/data/finding/01_gastroscopy_observation.yaml +113 -0
- endoreg_db/data/finding/02_colonoscopy_baseline.yaml +53 -0
- endoreg_db/data/finding/02_colonoscopy_hidden.yaml +119 -0
- endoreg_db/data/finding/02_colonoscopy_observation.yaml +152 -0
- endoreg_db/data/finding_classification/00_generic.yaml +44 -0
- endoreg_db/data/finding_classification/00_generic_histology.yaml +28 -0
- endoreg_db/data/finding_classification/00_generic_lesion.yaml +52 -0
- endoreg_db/data/finding_classification/02_colonoscopy_baseline.yaml +83 -0
- endoreg_db/data/finding_classification/02_colonoscopy_histology.yaml +13 -0
- endoreg_db/data/finding_classification/02_colonoscopy_other.yaml +12 -0
- endoreg_db/data/finding_classification/02_colonoscopy_polyp.yaml +101 -0
- endoreg_db/data/finding_classification_choice/{yes_no_na.yaml → 00_generic.yaml} +5 -1
- endoreg_db/data/finding_classification_choice/{examination_setting_generic_types.yaml → 00_generic_baseline.yaml} +10 -2
- endoreg_db/data/finding_classification_choice/{complication_generic_types.yaml → 00_generic_complication.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{histology.yaml → 00_generic_histology.yaml} +1 -4
- endoreg_db/data/finding_classification_choice/00_generic_lesion.yaml +158 -0
- endoreg_db/data/finding_classification_choice/{bowel_preparation.yaml → 02_colonoscopy_bowel_preparation.yaml} +1 -30
- endoreg_db/data/finding_classification_choice/{colonoscopy_not_complete_reason.yaml → 02_colonoscopy_generic.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{colonoscopy_location.yaml → 02_colonoscopy_location.yaml} +23 -4
- endoreg_db/data/finding_classification_choice/02_colonoscopy_other.yaml +34 -0
- endoreg_db/data/finding_classification_choice/02_colonoscopy_polyp_advanced_imaging.yaml +76 -0
- endoreg_db/data/finding_classification_choice/{colon_lesion_paris.yaml → 02_colonoscopy_polyp_morphology.yaml} +26 -8
- endoreg_db/data/finding_classification_choice/02_colonoscopy_size.yaml +27 -0
- endoreg_db/data/finding_classification_type/{colonoscopy_basic.yaml → 00_generic.yaml} +18 -13
- endoreg_db/data/finding_classification_type/02_colonoscopy.yaml +9 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy.yaml +59 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_ablation.yaml +44 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_bleeding.yaml +55 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_resection.yaml +85 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_stenosis.yaml +17 -0
- endoreg_db/data/finding_intervention/00_generic_endoscopy_stent.yaml +9 -0
- endoreg_db/data/finding_intervention/01_gastroscopy.yaml +19 -0
- endoreg_db/data/finding_intervention/04_eus.yaml +39 -0
- endoreg_db/data/finding_intervention/05_ercp.yaml +3 -0
- endoreg_db/data/finding_type/data.yaml +8 -12
- endoreg_db/data/requirement/01_patient_data.yaml +93 -0
- endoreg_db/data/requirement/old/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/old/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_operator/new_operators.yaml +36 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +29 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/{endoscopy_bleeding_risk.yaml → 02_endoscopy_bleeding_risk.yaml} +0 -6
- endoreg_db/data/requirement_set/90_coloreg.yaml +190 -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 +4 -2
- endoreg_db/forms/examination_form.py +1 -1
- endoreg_db/helpers/data_loader.py +125 -53
- endoreg_db/helpers/default_objects.py +116 -81
- endoreg_db/import_files/__init__.py +27 -0
- endoreg_db/import_files/context/__init__.py +7 -0
- endoreg_db/import_files/context/default_sensitive_meta.py +81 -0
- endoreg_db/import_files/context/ensure_center.py +17 -0
- endoreg_db/import_files/context/file_lock.py +66 -0
- endoreg_db/import_files/context/import_context.py +43 -0
- endoreg_db/import_files/context/validate_directories.py +56 -0
- endoreg_db/import_files/file_storage/__init__.py +15 -0
- endoreg_db/import_files/file_storage/create_report_file.py +76 -0
- endoreg_db/import_files/file_storage/create_video_file.py +75 -0
- endoreg_db/import_files/file_storage/sensitive_meta_storage.py +39 -0
- endoreg_db/import_files/file_storage/state_management.py +400 -0
- endoreg_db/import_files/file_storage/storage.py +36 -0
- endoreg_db/import_files/import_service.md +26 -0
- endoreg_db/import_files/processing/__init__.py +11 -0
- endoreg_db/import_files/processing/report_processing/report_anonymization.py +94 -0
- endoreg_db/import_files/processing/sensitive_meta_adapter.py +51 -0
- endoreg_db/import_files/processing/video_processing/video_anonymization.py +107 -0
- endoreg_db/import_files/processing/video_processing/video_cleanup_on_error.py +119 -0
- endoreg_db/import_files/pseudonymization/fake.py +52 -0
- endoreg_db/import_files/pseudonymization/k_anonymity.py +182 -0
- endoreg_db/import_files/pseudonymization/k_pseudonymity.py +128 -0
- endoreg_db/import_files/report_import_service.py +141 -0
- endoreg_db/import_files/video_import_service.py +150 -0
- 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_report.py +130 -65
- endoreg_db/management/commands/import_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +2 -2
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_ai_model_data.py +5 -5
- endoreg_db/management/commands/load_ai_model_label_data.py +9 -7
- endoreg_db/management/commands/load_base_db_data.py +5 -134
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_contraindication_data.py +14 -16
- endoreg_db/management/commands/load_disease_classification_choices_data.py +15 -18
- endoreg_db/management/commands/load_disease_classification_data.py +15 -18
- endoreg_db/management/commands/load_disease_data.py +25 -28
- endoreg_db/management/commands/load_endoscope_data.py +20 -27
- endoreg_db/management/commands/load_event_data.py +14 -16
- endoreg_db/management/commands/load_examination_data.py +31 -44
- endoreg_db/management/commands/load_examination_indication_data.py +20 -21
- endoreg_db/management/commands/load_finding_data.py +52 -80
- endoreg_db/management/commands/load_information_source.py +21 -23
- endoreg_db/management/commands/load_lab_value_data.py +17 -26
- endoreg_db/management/commands/load_medication_data.py +13 -12
- endoreg_db/management/commands/load_organ_data.py +15 -19
- endoreg_db/management/commands/load_pdf_type_data.py +19 -18
- endoreg_db/management/commands/load_profession_data.py +14 -17
- endoreg_db/management/commands/load_qualification_data.py +20 -23
- endoreg_db/management/commands/load_report_reader_flag_data.py +17 -19
- endoreg_db/management/commands/load_requirement_data.py +62 -39
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/load_risk_data.py +7 -6
- endoreg_db/management/commands/load_shift_data.py +20 -23
- endoreg_db/management/commands/load_tag_data.py +8 -11
- endoreg_db/management/commands/load_unit_data.py +17 -19
- endoreg_db/management/commands/setup_endoreg_db.py +3 -3
- endoreg_db/management/commands/start_filewatcher.py +46 -37
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/management/commands/validate_video_files.py +1 -5
- endoreg_db/migrations/0001_initial.py +297 -250
- 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 +129 -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 +8 -9
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +23 -28
- 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 +98 -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 +194 -194
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +55 -47
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/processing_history/__init__.py +5 -0
- endoreg_db/models/media/processing_history/processing_history.py +96 -0
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +139 -77
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +174 -112
- 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 +113 -61
- 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/README.md +1 -0
- 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 +39 -20
- endoreg_db/models/medical/examination/examination_indication.py +30 -95
- endoreg_db/models/medical/examination/examination_time.py +23 -8
- 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 +32 -40
- endoreg_db/models/medical/finding/finding_classification.py +42 -72
- endoreg_db/models/medical/finding/finding_intervention.py +25 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +26 -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 +6 -6
- 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 +31 -24
- endoreg_db/models/metadata/sensitive_meta.py +105 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +198 -103
- 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 +50 -29
- 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/report/report.py +6 -0
- endoreg_db/models/requirement/requirement.py +329 -361
- 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 +103 -112
- 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 +49 -51
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +101 -68
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +110 -90
- endoreg_db/models/upload_job.py +35 -34
- endoreg_db/models/utils.py +28 -25
- endoreg_db/queries/__init__.py +3 -1
- endoreg_db/root_urls.py +21 -2
- endoreg_db/schemas/examination_evaluation.py +1 -1
- endoreg_db/serializers/__init__.py +2 -10
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
- endoreg_db/serializers/meta/__init__.py +1 -6
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/file_overview.py +11 -99
- endoreg_db/serializers/misc/sensitive_patient_data.py +50 -26
- endoreg_db/serializers/patient_examination/patient_examination.py +3 -3
- endoreg_db/serializers/pdf/anony_text_validation.py +39 -23
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_file_list.py +65 -34
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/services/__old/pdf_import.py +1487 -0
- endoreg_db/services/__old/video_import.py +1306 -0
- endoreg_db/services/anonymization.py +128 -89
- endoreg_db/services/lookup_service.py +65 -52
- endoreg_db/services/lookup_store.py +2 -2
- endoreg_db/services/pdf_import.py +0 -1382
- endoreg_db/services/report_import.py +10 -0
- endoreg_db/services/video_import.py +6 -1255
- endoreg_db/tasks/upload_tasks.py +79 -70
- endoreg_db/tasks/video_ingest.py +8 -4
- endoreg_db/urls/__init__.py +5 -32
- endoreg_db/urls/ai.py +32 -0
- endoreg_db/urls/media.py +121 -83
- 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 +142 -40
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/paths.py +110 -46
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/Readme.md +1 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +655 -0
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
- 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 +85 -183
- endoreg_db/views/ai/__init__.py +8 -0
- endoreg_db/views/ai/label.py +155 -0
- endoreg_db/views/anonymization/media_management.py +202 -166
- endoreg_db/views/anonymization/overview.py +99 -67
- endoreg_db/views/anonymization/validate.py +182 -44
- endoreg_db/views/media/__init__.py +7 -20
- endoreg_db/views/media/pdf_media.py +197 -174
- endoreg_db/views/media/sensitive_metadata.py +193 -138
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/meta/__init__.py +0 -8
- endoreg_db/views/misc/__init__.py +1 -7
- endoreg_db/views/misc/upload_views.py +94 -93
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/report/__init__.py +5 -7
- endoreg_db/views/{pdf → report}/reimport.py +22 -22
- endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +46 -39
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +17 -3
- endoreg_db/views/requirement/lookup_store.py +22 -90
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +23 -24
- endoreg_db/views/video/correction.py +201 -172
- endoreg_db/views/video/reimport.py +1 -1
- endoreg_db/views/{media/video_segments.py → video/segments_crud.py} +77 -40
- endoreg_db/views/video/{video_meta.py → video_meta_stats.py} +2 -2
- endoreg_db/views/video/video_stream.py +7 -8
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +391 -413
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
- endoreg_db/data/finding/anatomy_colon.yaml +0 -128
- endoreg_db/data/finding/colonoscopy.yaml +0 -40
- endoreg_db/data/finding/colonoscopy_bowel_prep.yaml +0 -56
- endoreg_db/data/finding/complication.yaml +0 -16
- endoreg_db/data/finding/data.yaml +0 -105
- endoreg_db/data/finding/examination_setting.yaml +0 -16
- endoreg_db/data/finding/medication_related.yaml +0 -18
- endoreg_db/data/finding/outcome.yaml +0 -12
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +0 -95
- endoreg_db/data/finding_classification/colonoscopy_jnet.yaml +0 -22
- endoreg_db/data/finding_classification/colonoscopy_kudo.yaml +0 -25
- endoreg_db/data/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +0 -68
- endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -80
- endoreg_db/data/finding_classification/colonoscopy_lst.yaml +0 -21
- endoreg_db/data/finding_classification/colonoscopy_nice.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_paris.yaml +0 -26
- endoreg_db/data/finding_classification/colonoscopy_sano.yaml +0 -22
- endoreg_db/data/finding_classification/colonoscopy_summary.yaml +0 -53
- endoreg_db/data/finding_classification/complication_generic.yaml +0 -25
- endoreg_db/data/finding_classification/examination_setting_generic.yaml +0 -40
- endoreg_db/data/finding_classification/histology_colo.yaml +0 -51
- endoreg_db/data/finding_classification/intervention_required.yaml +0 -26
- endoreg_db/data/finding_classification/medication_related.yaml +0 -23
- endoreg_db/data/finding_classification/visualized.yaml +0 -33
- endoreg_db/data/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
- endoreg_db/data/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
- endoreg_db/data/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
- endoreg_db/data/finding_classification_choice/colon_lesion_lst.yaml +0 -15
- endoreg_db/data/finding_classification_choice/colon_lesion_nice.yaml +0 -17
- endoreg_db/data/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
- endoreg_db/data/finding_classification_choice/colon_lesion_sano.yaml +0 -14
- endoreg_db/data/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
- endoreg_db/data/finding_classification_choice/colonoscopy_size.yaml +0 -82
- endoreg_db/data/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
- endoreg_db/data/finding_classification_choice/outcome.yaml +0 -19
- endoreg_db/data/finding_intervention/endoscopy.yaml +0 -43
- endoreg_db/data/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
- endoreg_db/data/finding_intervention/endoscopy_egd.yaml +0 -128
- endoreg_db/data/finding_intervention/endoscopy_ercp.yaml +0 -32
- endoreg_db/data/finding_intervention/endoscopy_eus_lower.yaml +0 -9
- endoreg_db/data/finding_intervention/endoscopy_eus_upper.yaml +0 -36
- endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +0 -79
- endoreg_db/data/requirement/age.yaml +0 -26
- endoreg_db/data/requirement/gender.yaml +0 -25
- endoreg_db/management/commands/init_default_ai_model.py +0 -112
- endoreg_db/management/commands/reset_celery_schedule.py +0 -9
- endoreg_db/management/commands/validate_video.py +0 -204
- endoreg_db/migrations/0002_add_video_correction_models.py +0 -52
- endoreg_db/migrations/0003_add_center_display_name.py +0 -30
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- 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/renames.yml +0 -8
- endoreg_db/serializers/_old/raw_pdf_meta_validation.py +0 -223
- endoreg_db/serializers/_old/raw_video_meta_validation.py +0 -179
- endoreg_db/serializers/_old/video.py +0 -71
- endoreg_db/serializers/meta/pdf_file_meta_extraction.py +0 -115
- endoreg_db/serializers/meta/report_meta.py +0 -53
- endoreg_db/serializers/report/__init__.py +0 -9
- endoreg_db/serializers/report/mixins.py +0 -45
- endoreg_db/serializers/report/report.py +0 -105
- endoreg_db/serializers/report/report_list.py +0 -22
- endoreg_db/serializers/report/secure_file_url.py +0 -26
- endoreg_db/serializers/video/video_metadata.py +0 -105
- endoreg_db/services/requirements_object.py +0 -147
- endoreg_db/services/storage_aware_video_processor.py +0 -344
- endoreg_db/urls/files.py +0 -6
- endoreg_db/urls/label_video_segment_validate.py +0 -33
- endoreg_db/urls/label_video_segments.py +0 -46
- 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/utils/requirement_operator_logic/model_evaluators.py +0 -368
- endoreg_db/views/label/__init__.py +0 -5
- endoreg_db/views/label/label.py +0 -15
- endoreg_db/views/label_video_segment/__init__.py +0 -16
- endoreg_db/views/label_video_segment/create_lvs_from_annotation.py +0 -44
- endoreg_db/views/label_video_segment/get_lvs_by_name_and_video.py +0 -50
- endoreg_db/views/label_video_segment/label_video_segment.py +0 -77
- endoreg_db/views/label_video_segment/label_video_segment_by_label.py +0 -174
- endoreg_db/views/label_video_segment/label_video_segment_detail.py +0 -73
- endoreg_db/views/label_video_segment/update_lvs_from_annotation.py +0 -46
- endoreg_db/views/label_video_segment/validate.py +0 -226
- endoreg_db/views/media/segments.py +0 -71
- endoreg_db/views/meta/available_files_list.py +0 -146
- endoreg_db/views/meta/report_meta.py +0 -53
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -148
- endoreg_db/views/misc/secure_file_serving_view.py +0 -80
- endoreg_db/views/misc/secure_file_url_view.py +0 -84
- endoreg_db/views/misc/secure_url_validate.py +0 -79
- endoreg_db/views/patient_examination/DEPRECATED_video_backup.py +0 -164
- endoreg_db/views/patient_finding_location/__init__.py +0 -5
- endoreg_db/views/patient_finding_location/pfl_create.py +0 -70
- endoreg_db/views/patient_finding_morphology/__init__.py +0 -5
- endoreg_db/views/patient_finding_morphology/pfm_create.py +0 -70
- endoreg_db/views/pdf/__init__.py +0 -8
- 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/segmentation.py +0 -274
- endoreg_db/views/video/task_status.py +0 -49
- endoreg_db/views/video/timeline.py +0 -46
- endoreg_db/views/video/video_analyze.py +0 -52
- endoreg_db/views.py +0 -0
- /endoreg_db/data/requirement/{colonoscopy_baseline_austria.yaml → old/colonoscopy_baseline_austria.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_cardiovascular.yaml → old/disease_cardiovascular.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_classification_choice_cardiovascular.yaml → old/disease_classification_choice_cardiovascular.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_hepatology.yaml → old/disease_hepatology.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_misc.yaml → old/disease_misc.yaml} +0 -0
- /endoreg_db/data/requirement/{disease_renal.yaml → old/disease_renal.yaml} +0 -0
- /endoreg_db/data/requirement/{endoscopy_bleeding_risk.yaml → old/endoscopy_bleeding_risk.yaml} +0 -0
- /endoreg_db/data/requirement/{event_cardiology.yaml → old/event_cardiology.yaml} +0 -0
- /endoreg_db/data/requirement/{event_requirements.yaml → old/event_requirements.yaml} +0 -0
- /endoreg_db/data/requirement/{finding_colon_polyp.yaml → old/finding_colon_polyp.yaml} +0 -0
- /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
- /endoreg_db/data/requirement/{lab_value.yaml → old/lab_value.yaml} +0 -0
- /endoreg_db/data/requirement/{medication.yaml → old/medication.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{age.yaml → _old/age.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{lab_operators.yaml → _old/lab_operators.yaml} +0 -0
- /endoreg_db/data/requirement_operator/{model_operators.yaml → _old/model_operators.yaml} +0 -0
- /endoreg_db/{models/media/video/refactor_plan.md → import_files/pseudonymization/__init__.py} +0 -0
- /endoreg_db/{models/media/video/video_file_frames.py → import_files/pseudonymization/pseudonymize.py} +0 -0
- /endoreg_db/models/{metadata/frame_ocr_result.py → report/__init__.py} +0 -0
- /endoreg_db/{urls/sensitive_meta.py → models/report/images.py} +0 -0
- /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
report Media Management View (Phase 1.2)
|
|
3
3
|
|
|
4
|
-
Provides standardized REST API for
|
|
4
|
+
Provides standardized REST API for report files including listing, detail retrieval,
|
|
5
5
|
and streaming for the media management system.
|
|
6
6
|
|
|
7
7
|
This is separate from the existing pdf.PDFMediaView which handles legacy workflows.
|
|
@@ -9,67 +9,71 @@ 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.views.decorators.clickjacking import xframe_options_exempt
|
|
17
|
-
from django.db.models import Q
|
|
18
20
|
|
|
19
21
|
from endoreg_db.models import RawPdfFile
|
|
20
22
|
from endoreg_db.utils.permissions import EnvironmentAwarePermission
|
|
23
|
+
from endoreg_db.utils.storage import file_exists
|
|
21
24
|
|
|
22
25
|
logger = logging.getLogger(__name__)
|
|
23
26
|
|
|
24
27
|
|
|
25
28
|
class PdfMediaView(APIView):
|
|
26
29
|
"""
|
|
27
|
-
|
|
28
|
-
|
|
30
|
+
report Media Management API for CRUD operations on report files.
|
|
31
|
+
|
|
29
32
|
Endpoints:
|
|
30
33
|
- GET /api/media/pdfs/ - List all PDFs with filtering
|
|
31
|
-
- GET /api/media/pdfs/{id}/ - Get
|
|
32
|
-
- GET /api/media/pdfs/{id}/stream/ - Stream
|
|
33
|
-
- PATCH /api/media/pdfs/{id}/ - Update
|
|
34
|
-
- DELETE /api/media/pdfs/{id}/ - Delete
|
|
35
|
-
|
|
34
|
+
- GET /api/media/pdfs/{id}/ - Get report details
|
|
35
|
+
- GET /api/media/pdfs/{id}/stream/ - Stream report file (same as detail for PDFs)
|
|
36
|
+
- PATCH /api/media/pdfs/{id}/ - Update report metadata (future)
|
|
37
|
+
- DELETE /api/media/pdfs/{id}/ - Delete report (future)
|
|
38
|
+
|
|
36
39
|
Query Parameters:
|
|
37
40
|
- status: Filter by processing status (not_started, done, validated)
|
|
38
41
|
- search: Search in filename
|
|
39
42
|
- limit: Limit results (default: 50)
|
|
40
43
|
- offset: Pagination offset
|
|
41
|
-
|
|
44
|
+
|
|
42
45
|
Examples:
|
|
43
46
|
- GET /api/media/pdfs/?status=done&search=exam
|
|
44
47
|
- GET /api/media/pdfs/123/
|
|
45
48
|
- GET /api/media/pdfs/123/stream/
|
|
46
|
-
|
|
49
|
+
|
|
47
50
|
Phase 1.2 Implementation:
|
|
48
51
|
- List and detail views implemented
|
|
49
|
-
-
|
|
52
|
+
- report streaming functionality
|
|
50
53
|
- Filtering and search functionality
|
|
51
54
|
- Pagination support
|
|
52
55
|
- Error handling with proper HTTP status codes
|
|
53
56
|
"""
|
|
57
|
+
|
|
54
58
|
permission_classes = [EnvironmentAwarePermission]
|
|
55
59
|
|
|
56
60
|
def get(self, request, pk=None):
|
|
57
61
|
"""
|
|
58
|
-
Handle GET requests for
|
|
59
|
-
|
|
62
|
+
Handle GET requests for report listing, detail retrieval, or streaming.
|
|
63
|
+
|
|
60
64
|
Args:
|
|
61
65
|
request: HTTP request object
|
|
62
|
-
pk: Optional
|
|
63
|
-
|
|
66
|
+
pk: Optional report ID for detail view or streaming
|
|
67
|
+
|
|
64
68
|
Returns:
|
|
65
|
-
Response or FileResponse: JSON response with
|
|
66
|
-
|
|
69
|
+
Response or FileResponse: JSON response with report data or report file stream
|
|
70
|
+
|
|
67
71
|
Raises:
|
|
68
|
-
Http404: If specific
|
|
72
|
+
Http404: If specific report not found
|
|
69
73
|
"""
|
|
70
74
|
if pk is not None:
|
|
71
75
|
# Check if this is a streaming request
|
|
72
|
-
if request.path.endswith(
|
|
76
|
+
if request.path.endswith("/stream/"):
|
|
73
77
|
return self._stream_pdf(pk)
|
|
74
78
|
else:
|
|
75
79
|
# Detail view
|
|
@@ -80,174 +84,190 @@ class PdfMediaView(APIView):
|
|
|
80
84
|
|
|
81
85
|
def _get_pdf_detail(self, pk):
|
|
82
86
|
"""
|
|
83
|
-
Get detailed information for a specific
|
|
84
|
-
|
|
87
|
+
Get detailed information for a specific report.
|
|
88
|
+
|
|
85
89
|
Args:
|
|
86
|
-
pk:
|
|
87
|
-
|
|
90
|
+
pk: report primary key
|
|
91
|
+
|
|
88
92
|
Returns:
|
|
89
|
-
Response: JSON response with
|
|
90
|
-
|
|
93
|
+
Response: JSON response with report details
|
|
94
|
+
|
|
91
95
|
Raises:
|
|
92
|
-
Http404: If
|
|
96
|
+
Http404: If report not found
|
|
93
97
|
"""
|
|
94
98
|
try:
|
|
95
99
|
# Validate pdf_id is numeric
|
|
96
100
|
try:
|
|
97
101
|
pdf_id_int = int(pk)
|
|
98
102
|
except (ValueError, TypeError):
|
|
99
|
-
raise Http404("Invalid
|
|
100
|
-
|
|
101
|
-
# Fetch
|
|
102
|
-
pdf = RawPdfFile.objects.select_related(
|
|
103
|
-
|
|
104
|
-
# Build
|
|
103
|
+
raise Http404("Invalid report ID format")
|
|
104
|
+
|
|
105
|
+
# Fetch report with related data
|
|
106
|
+
pdf = RawPdfFile.objects.select_related("sensitive_meta").get(pk=pdf_id_int)
|
|
107
|
+
|
|
108
|
+
# Build report details
|
|
105
109
|
pdf_data = {
|
|
106
|
-
"id": pdf.
|
|
107
|
-
"filename": getattr(pdf.file,
|
|
108
|
-
"file_size": getattr(pdf.file,
|
|
110
|
+
"id": pdf.pk,
|
|
111
|
+
"filename": getattr(pdf.file, "name", "Unknown"),
|
|
112
|
+
"file_size": getattr(pdf.file, "size", 0),
|
|
109
113
|
"pdf_hash": pdf.pdf_hash,
|
|
110
|
-
"uploaded_at": pdf.
|
|
114
|
+
"uploaded_at": pdf.date_created.isoformat()
|
|
115
|
+
if getattr(pdf, "date_created", None)
|
|
116
|
+
else None,
|
|
111
117
|
"anonymized_text": pdf.anonymized_text,
|
|
112
|
-
"has_anonymized_text": bool(
|
|
113
|
-
|
|
114
|
-
|
|
118
|
+
"has_anonymized_text": bool(
|
|
119
|
+
pdf.anonymized_text and pdf.anonymized_text.strip()
|
|
120
|
+
),
|
|
121
|
+
"is_validated": getattr(pdf.sensitive_meta, "is_verified", False)
|
|
122
|
+
if pdf.sensitive_meta
|
|
123
|
+
else False,
|
|
124
|
+
"stream_url": self.request.build_absolute_uri(
|
|
125
|
+
f"/api/media/pdfs/{pdf.pk}/stream/"
|
|
126
|
+
),
|
|
115
127
|
}
|
|
116
|
-
|
|
128
|
+
|
|
117
129
|
# Add patient metadata if available
|
|
118
130
|
if pdf.sensitive_meta:
|
|
119
|
-
pdf_data.update(
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
131
|
+
pdf_data.update(
|
|
132
|
+
{
|
|
133
|
+
"patient_first_name": pdf.sensitive_meta.patient_first_name,
|
|
134
|
+
"patient_last_name": pdf.sensitive_meta.patient_last_name,
|
|
135
|
+
"patient_dob": pdf.sensitive_meta.patient_dob.strftime(
|
|
136
|
+
"%d.%m.%Y"
|
|
137
|
+
)
|
|
138
|
+
if pdf.sensitive_meta.patient_dob
|
|
139
|
+
else None,
|
|
140
|
+
"examination_date": pdf.sensitive_meta.examination_date.strftime(
|
|
141
|
+
"%d.%m.%Y"
|
|
142
|
+
)
|
|
143
|
+
if pdf.sensitive_meta.examination_date
|
|
144
|
+
else None,
|
|
145
|
+
}
|
|
146
|
+
)
|
|
147
|
+
|
|
126
148
|
return Response(pdf_data)
|
|
127
|
-
|
|
149
|
+
|
|
128
150
|
except RawPdfFile.DoesNotExist:
|
|
129
|
-
raise Http404(f"
|
|
130
|
-
|
|
151
|
+
raise Http404(f"report with ID {pk} not found")
|
|
152
|
+
|
|
131
153
|
except Exception as e:
|
|
132
|
-
logger.error(
|
|
154
|
+
logger.error(
|
|
155
|
+
f"Unexpected error in report detail view for ID {pk}: {str(e)}"
|
|
156
|
+
)
|
|
133
157
|
return Response(
|
|
134
|
-
{"error": "Failed to retrieve
|
|
135
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
158
|
+
{"error": "Failed to retrieve report details"},
|
|
159
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
136
160
|
)
|
|
137
|
-
|
|
161
|
+
|
|
138
162
|
@xframe_options_exempt
|
|
139
163
|
def _stream_pdf(self, pk):
|
|
140
164
|
"""
|
|
141
|
-
Stream
|
|
142
|
-
|
|
165
|
+
Stream report file content for viewing/download.
|
|
166
|
+
|
|
143
167
|
Args:
|
|
144
|
-
pk:
|
|
145
|
-
|
|
168
|
+
pk: report primary key
|
|
169
|
+
|
|
146
170
|
Returns:
|
|
147
|
-
FileResponse:
|
|
148
|
-
|
|
171
|
+
FileResponse: report file stream
|
|
172
|
+
|
|
149
173
|
Raises:
|
|
150
|
-
Http404: If
|
|
174
|
+
Http404: If report not found or file cannot be accessed
|
|
151
175
|
"""
|
|
152
176
|
try:
|
|
153
177
|
# Validate pdf_id is numeric
|
|
154
178
|
try:
|
|
155
179
|
pdf_id_int = int(pk)
|
|
156
180
|
except (ValueError, TypeError):
|
|
157
|
-
raise Http404("Invalid
|
|
158
|
-
|
|
159
|
-
# Fetch
|
|
181
|
+
raise Http404("Invalid report ID format")
|
|
182
|
+
|
|
183
|
+
# Fetch report
|
|
160
184
|
pdf = RawPdfFile.objects.get(pk=pdf_id_int)
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
# Create file response
|
|
185
|
+
|
|
186
|
+
file_field = pdf.file
|
|
187
|
+
file_path = file_field.path
|
|
188
|
+
|
|
189
|
+
if not file_field or not file_field.name:
|
|
190
|
+
raise Http404("report file not found")
|
|
191
|
+
if not file_exists(file_field):
|
|
192
|
+
raise Http404("report file does not exist in storage")
|
|
193
|
+
|
|
194
|
+
with open(file_path, "rb") as file_handle:
|
|
173
195
|
response = FileResponse(
|
|
174
|
-
|
|
175
|
-
content_type=
|
|
176
|
-
as_attachment=False
|
|
196
|
+
file_handle,
|
|
197
|
+
content_type="application/pdf",
|
|
198
|
+
as_attachment=False,
|
|
177
199
|
)
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
return response
|
|
189
|
-
|
|
190
|
-
except (OSError, IOError) as e:
|
|
191
|
-
logger.error(f"File access error for PDF {pk}: {str(e)}")
|
|
192
|
-
raise Http404("PDF file cannot be accessed")
|
|
193
|
-
|
|
200
|
+
|
|
201
|
+
filename = Path(file_field.name).name
|
|
202
|
+
response["Content-Disposition"] = f'inline; filename="{filename}"'
|
|
203
|
+
|
|
204
|
+
frontend_origin = os.environ.get("FRONTEND_ORIGIN", "http://localhost:8000")
|
|
205
|
+
response["Access-Control-Allow-Origin"] = frontend_origin
|
|
206
|
+
response["Access-Control-Allow-Credentials"] = "true"
|
|
207
|
+
|
|
208
|
+
return response
|
|
209
|
+
|
|
194
210
|
except RawPdfFile.DoesNotExist:
|
|
195
|
-
raise Http404(f"
|
|
196
|
-
|
|
211
|
+
raise Http404(f"report with ID {pk} not found")
|
|
212
|
+
|
|
197
213
|
except Exception as e:
|
|
198
|
-
logger.error(f"Unexpected error in
|
|
199
|
-
raise Http404("
|
|
214
|
+
logger.error(f"Unexpected error in report streaming for ID {pk}: {str(e)}")
|
|
215
|
+
raise Http404("report file cannot be streamed")
|
|
200
216
|
|
|
201
217
|
def _list_pdfs(self, request):
|
|
202
218
|
"""
|
|
203
219
|
List PDFs with filtering, search, and pagination.
|
|
204
|
-
|
|
220
|
+
|
|
205
221
|
Args:
|
|
206
222
|
request: HTTP request with query parameters
|
|
207
|
-
|
|
223
|
+
|
|
208
224
|
Returns:
|
|
209
|
-
Response: JSON response with paginated
|
|
225
|
+
Response: JSON response with paginated report list
|
|
210
226
|
"""
|
|
211
227
|
try:
|
|
212
228
|
# Start with all PDFs
|
|
213
|
-
queryset = RawPdfFile.objects.select_related(
|
|
214
|
-
|
|
229
|
+
queryset = RawPdfFile.objects.select_related("sensitive_meta").all()
|
|
230
|
+
|
|
215
231
|
# Apply filters
|
|
216
232
|
queryset = self._apply_filters(queryset, request.query_params)
|
|
217
|
-
|
|
233
|
+
|
|
218
234
|
# Apply search
|
|
219
|
-
search = request.query_params.get(
|
|
235
|
+
search = request.query_params.get("search", "").strip()
|
|
220
236
|
if search:
|
|
221
|
-
queryset = queryset.filter(
|
|
222
|
-
|
|
223
|
-
)
|
|
224
|
-
|
|
237
|
+
queryset = queryset.filter(Q(file__icontains=search))
|
|
238
|
+
|
|
225
239
|
# Order by upload date (newest first) or id if no upload date
|
|
226
|
-
if hasattr(queryset.model,
|
|
227
|
-
queryset = queryset.order_by(
|
|
240
|
+
if hasattr(queryset.model, "date_created"):
|
|
241
|
+
queryset = queryset.order_by("-date_created")
|
|
228
242
|
else:
|
|
229
|
-
queryset = queryset.order_by(
|
|
230
|
-
|
|
243
|
+
queryset = queryset.order_by("-pk")
|
|
244
|
+
|
|
231
245
|
# Apply pagination
|
|
232
|
-
limit = min(int(request.query_params.get(
|
|
233
|
-
offset = int(request.query_params.get(
|
|
234
|
-
|
|
246
|
+
limit = min(int(request.query_params.get("limit", 50)), 100)
|
|
247
|
+
offset = int(request.query_params.get("offset", 0))
|
|
248
|
+
|
|
235
249
|
total_count = queryset.count()
|
|
236
|
-
pdfs = queryset[offset:offset + limit]
|
|
237
|
-
|
|
250
|
+
pdfs = queryset[offset : offset + limit]
|
|
251
|
+
|
|
238
252
|
# Serialize PDFs manually (no dedicated serializer yet)
|
|
239
253
|
results = []
|
|
240
254
|
for pdf in pdfs:
|
|
241
255
|
pdf_item = {
|
|
242
|
-
"id": pdf.
|
|
243
|
-
"filename": getattr(pdf.file,
|
|
256
|
+
"id": pdf.pk,
|
|
257
|
+
"filename": getattr(pdf.file, "name", "Unknown"),
|
|
244
258
|
"file_size": self._safe_get_file_size(pdf.file),
|
|
245
259
|
"pdf_hash": pdf.pdf_hash,
|
|
246
|
-
"has_anonymized_text": bool(
|
|
247
|
-
|
|
248
|
-
|
|
260
|
+
"has_anonymized_text": bool(
|
|
261
|
+
pdf.anonymized_text and pdf.anonymized_text.strip()
|
|
262
|
+
),
|
|
263
|
+
"is_validated": getattr(pdf.sensitive_meta, "is_verified", False)
|
|
264
|
+
if pdf.sensitive_meta
|
|
265
|
+
else False,
|
|
266
|
+
"stream_url": request.build_absolute_uri(
|
|
267
|
+
f"/api/media/pdfs/{pdf.pk}/stream/"
|
|
268
|
+
),
|
|
249
269
|
}
|
|
250
|
-
|
|
270
|
+
|
|
251
271
|
# Determine status based on anonymization and validation
|
|
252
272
|
if not pdf.anonymized_text or not pdf.anonymized_text.strip():
|
|
253
273
|
pdf_item["status"] = "not_started"
|
|
@@ -255,42 +275,44 @@ class PdfMediaView(APIView):
|
|
|
255
275
|
pdf_item["status"] = "validated"
|
|
256
276
|
else:
|
|
257
277
|
pdf_item["status"] = "done"
|
|
258
|
-
|
|
278
|
+
|
|
259
279
|
results.append(pdf_item)
|
|
260
|
-
|
|
261
|
-
return Response(
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
280
|
+
|
|
281
|
+
return Response(
|
|
282
|
+
{
|
|
283
|
+
"count": total_count,
|
|
284
|
+
"next": self._get_next_url(request, offset, limit, total_count),
|
|
285
|
+
"previous": self._get_previous_url(request, offset, limit),
|
|
286
|
+
"results": results,
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
|
|
268
290
|
except ValueError as e:
|
|
269
291
|
return Response(
|
|
270
292
|
{"error": f"Invalid query parameter: {str(e)}"},
|
|
271
|
-
status=status.HTTP_400_BAD_REQUEST
|
|
293
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
272
294
|
)
|
|
273
|
-
|
|
295
|
+
|
|
274
296
|
except Exception as e:
|
|
275
|
-
logger.error(f"Unexpected error in
|
|
297
|
+
logger.error(f"Unexpected error in report list view: {str(e)}")
|
|
276
298
|
return Response(
|
|
277
|
-
{"error": "Failed to retrieve
|
|
278
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
299
|
+
{"error": "Failed to retrieve report list"},
|
|
300
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
279
301
|
)
|
|
280
302
|
|
|
281
303
|
def _safe_get_file_size(self, file_field):
|
|
282
304
|
"""
|
|
283
305
|
Safely get file size without causing errors if file doesn't exist.
|
|
284
|
-
|
|
306
|
+
|
|
285
307
|
Args:
|
|
286
308
|
file_field: Django FileField
|
|
287
|
-
|
|
309
|
+
|
|
288
310
|
Returns:
|
|
289
311
|
int: File size in bytes, or 0 if file doesn't exist
|
|
290
312
|
"""
|
|
291
313
|
if not file_field or not file_field.name:
|
|
292
314
|
return 0
|
|
293
|
-
|
|
315
|
+
|
|
294
316
|
try:
|
|
295
317
|
return file_field.size
|
|
296
318
|
except (OSError, IOError, ValueError):
|
|
@@ -299,45 +321,46 @@ class PdfMediaView(APIView):
|
|
|
299
321
|
|
|
300
322
|
def _apply_filters(self, queryset, query_params):
|
|
301
323
|
"""
|
|
302
|
-
Apply status and other filters to
|
|
303
|
-
|
|
324
|
+
Apply status and other filters to report queryset.
|
|
325
|
+
|
|
304
326
|
Args:
|
|
305
327
|
queryset: Base queryset to filter
|
|
306
328
|
query_params: Request query parameters
|
|
307
|
-
|
|
329
|
+
|
|
308
330
|
Returns:
|
|
309
331
|
QuerySet: Filtered queryset
|
|
310
332
|
"""
|
|
311
|
-
status_filter = query_params.get(
|
|
312
|
-
|
|
333
|
+
status_filter = query_params.get("status", "").strip().lower()
|
|
334
|
+
|
|
313
335
|
if status_filter:
|
|
314
|
-
if status_filter ==
|
|
336
|
+
if status_filter == "not_started":
|
|
315
337
|
# PDFs without anonymized text
|
|
316
338
|
queryset = queryset.filter(
|
|
317
|
-
Q(anonymized_text__isnull=True) | Q(anonymized_text__exact=
|
|
339
|
+
Q(anonymized_text__isnull=True) | Q(anonymized_text__exact="")
|
|
318
340
|
)
|
|
319
|
-
elif status_filter ==
|
|
341
|
+
elif status_filter == "done":
|
|
320
342
|
# PDFs with anonymized text but not validated
|
|
321
343
|
queryset = queryset.filter(
|
|
322
344
|
~Q(anonymized_text__isnull=True),
|
|
323
|
-
~Q(anonymized_text__exact=
|
|
324
|
-
Q(sensitive_meta__is_verified=False)
|
|
345
|
+
~Q(anonymized_text__exact=""),
|
|
346
|
+
Q(sensitive_meta__is_verified=False)
|
|
347
|
+
| Q(sensitive_meta__isnull=True),
|
|
325
348
|
)
|
|
326
|
-
elif status_filter ==
|
|
349
|
+
elif status_filter == "validated":
|
|
327
350
|
# PDFs with anonymized text and validated
|
|
328
351
|
queryset = queryset.filter(
|
|
329
352
|
~Q(anonymized_text__isnull=True),
|
|
330
|
-
~Q(anonymized_text__exact=
|
|
331
|
-
sensitive_meta__is_verified=True
|
|
353
|
+
~Q(anonymized_text__exact=""),
|
|
354
|
+
sensitive_meta__is_verified=True,
|
|
332
355
|
)
|
|
333
|
-
|
|
356
|
+
|
|
334
357
|
return queryset
|
|
335
358
|
|
|
336
359
|
def _get_next_url(self, request, offset, limit, total_count):
|
|
337
360
|
"""Generate next page URL for pagination."""
|
|
338
361
|
if offset + limit >= total_count:
|
|
339
362
|
return None
|
|
340
|
-
|
|
363
|
+
|
|
341
364
|
next_offset = offset + limit
|
|
342
365
|
return self._build_paginated_url(request, next_offset, limit)
|
|
343
366
|
|
|
@@ -345,16 +368,16 @@ class PdfMediaView(APIView):
|
|
|
345
368
|
"""Generate previous page URL for pagination."""
|
|
346
369
|
if offset <= 0:
|
|
347
370
|
return None
|
|
348
|
-
|
|
371
|
+
|
|
349
372
|
prev_offset = max(0, offset - limit)
|
|
350
373
|
return self._build_paginated_url(request, prev_offset, limit)
|
|
351
374
|
|
|
352
375
|
def _build_paginated_url(self, request, offset, limit):
|
|
353
376
|
"""Build URL with pagination parameters."""
|
|
354
377
|
params = request.query_params.copy()
|
|
355
|
-
params[
|
|
356
|
-
params[
|
|
357
|
-
|
|
378
|
+
params["offset"] = offset
|
|
379
|
+
params["limit"] = limit
|
|
380
|
+
|
|
358
381
|
base_url = request.build_absolute_uri(request.path)
|
|
359
382
|
if params:
|
|
360
383
|
return f"{base_url}?{params.urlencode()}"
|
|
@@ -363,26 +386,26 @@ class PdfMediaView(APIView):
|
|
|
363
386
|
# Future implementation placeholders
|
|
364
387
|
def patch(self, request, pk):
|
|
365
388
|
"""
|
|
366
|
-
Update
|
|
367
|
-
|
|
389
|
+
Update report metadata (Phase 1.2+ future enhancement).
|
|
390
|
+
|
|
368
391
|
Currently returns 501 Not Implemented.
|
|
369
392
|
"""
|
|
370
393
|
return Response(
|
|
371
|
-
{"error": "
|
|
372
|
-
status=status.HTTP_501_NOT_IMPLEMENTED
|
|
394
|
+
{"error": "report metadata updates not yet implemented"},
|
|
395
|
+
status=status.HTTP_501_NOT_IMPLEMENTED,
|
|
373
396
|
)
|
|
374
397
|
|
|
375
398
|
def delete(self, request, pk):
|
|
376
399
|
"""
|
|
377
|
-
Delete
|
|
378
|
-
|
|
400
|
+
Delete report file (Phase 1.2+ future enhancement).
|
|
401
|
+
|
|
379
402
|
Currently returns 501 Not Implemented.
|
|
380
403
|
Use /api/media-management/force-remove/{id}/ instead.
|
|
381
404
|
"""
|
|
382
405
|
return Response(
|
|
383
406
|
{
|
|
384
|
-
"error": "
|
|
385
|
-
"alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead"
|
|
407
|
+
"error": "report deletion not yet implemented",
|
|
408
|
+
"alternative": f"Use DELETE /api/media-management/force-remove/{pk}/ instead",
|
|
386
409
|
},
|
|
387
|
-
status=status.HTTP_501_NOT_IMPLEMENTED
|
|
410
|
+
status=status.HTTP_501_NOT_IMPLEMENTED,
|
|
388
411
|
)
|