endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/authz/auth.py +74 -0
- endoreg_db/authz/backends.py +168 -0
- endoreg_db/authz/management/commands/list_routes.py +18 -0
- endoreg_db/authz/middleware.py +83 -0
- endoreg_db/authz/permissions.py +127 -0
- endoreg_db/authz/policy.py +218 -0
- endoreg_db/authz/views_auth.py +66 -0
- endoreg_db/config/env.py +13 -8
- endoreg_db/data/__init__.py +8 -31
- endoreg_db/data/_examples/disease.yaml +55 -0
- endoreg_db/data/_examples/disease_classification.yaml +13 -0
- endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
- endoreg_db/data/_examples/event.yaml +64 -0
- endoreg_db/data/_examples/examination.yaml +72 -0
- endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
- endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
- endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
- endoreg_db/data/_examples/finding/complication.yaml +16 -0
- endoreg_db/data/_examples/finding/data.yaml +105 -0
- endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
- endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
- endoreg_db/data/_examples/finding/outcome.yaml +12 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
- endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
- endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
- endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
- endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
- endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
- endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
- endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
- endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
- endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/_examples/finding_type/data.yaml +43 -0
- endoreg_db/data/_examples/requirement/age.yaml +26 -0
- endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
- endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
- endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
- endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
- endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
- endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
- endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
- endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
- endoreg_db/data/_examples/requirement/gender.yaml +25 -0
- endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
- endoreg_db/data/_examples/requirement/medication.yaml +93 -0
- endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
- endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
- endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
- endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
- endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
- endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
- endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
- endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
- endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
- endoreg_db/data/requirement_set/_old_ +109 -0
- endoreg_db/data/requirement_set_type/data.yaml +21 -0
- endoreg_db/data/setup_config.yaml +4 -4
- endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
- endoreg_db/exceptions.py +5 -2
- endoreg_db/helpers/data_loader.py +1 -1
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
- endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
- endoreg_db/management/commands/import_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +1 -1
- endoreg_db/management/commands/init_default_ai_model.py +1 -1
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_requirement_data.py +60 -31
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/setup_endoreg_db.py +3 -3
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/migrations/0001_initial.py +1799 -1300
- endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
- endoreg_db/migrations/_old/0001_initial.py +1857 -0
- endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
- endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
- endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
- endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
- endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
- endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
- endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
- endoreg_db/models/__init__.py +78 -123
- endoreg_db/models/administration/__init__.py +21 -42
- endoreg_db/models/administration/ai/active_model.py +2 -2
- endoreg_db/models/administration/ai/ai_model.py +7 -6
- endoreg_db/models/administration/case/__init__.py +1 -15
- endoreg_db/models/administration/case/case.py +3 -3
- endoreg_db/models/administration/case/case_template/__init__.py +2 -14
- endoreg_db/models/administration/case/case_template/case_template.py +2 -124
- endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
- endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
- endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
- endoreg_db/models/administration/center/center.py +33 -19
- endoreg_db/models/administration/center/center_product.py +12 -9
- endoreg_db/models/administration/center/center_resource.py +25 -19
- endoreg_db/models/administration/center/center_shift.py +21 -17
- endoreg_db/models/administration/center/center_waste.py +16 -8
- endoreg_db/models/administration/person/__init__.py +2 -0
- endoreg_db/models/administration/person/employee/employee.py +10 -5
- endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
- endoreg_db/models/administration/person/employee/employee_type.py +12 -6
- endoreg_db/models/administration/person/examiner/examiner.py +13 -11
- endoreg_db/models/administration/person/patient/__init__.py +2 -0
- endoreg_db/models/administration/person/patient/patient.py +103 -100
- endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
- endoreg_db/models/administration/person/person.py +4 -0
- endoreg_db/models/administration/person/profession/__init__.py +8 -4
- endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
- endoreg_db/models/administration/product/product.py +20 -15
- endoreg_db/models/administration/product/product_material.py +17 -18
- endoreg_db/models/administration/product/product_weight.py +12 -8
- endoreg_db/models/administration/product/reference_product.py +23 -55
- endoreg_db/models/administration/qualification/qualification.py +7 -3
- endoreg_db/models/administration/qualification/qualification_type.py +7 -3
- endoreg_db/models/administration/shift/scheduled_days.py +8 -5
- endoreg_db/models/administration/shift/shift.py +16 -12
- endoreg_db/models/administration/shift/shift_type.py +23 -31
- endoreg_db/models/label/__init__.py +7 -8
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
- endoreg_db/models/label/label.py +15 -15
- endoreg_db/models/label/label_set.py +19 -6
- endoreg_db/models/label/label_type.py +1 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
- endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
- endoreg_db/models/label/video_segmentation_label.py +4 -0
- endoreg_db/models/label/video_segmentation_labelset.py +4 -3
- endoreg_db/models/media/frame/frame.py +22 -22
- endoreg_db/models/media/pdf/raw_pdf.py +110 -182
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +48 -56
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +150 -108
- endoreg_db/models/media/video/video_file_ai.py +288 -74
- endoreg_db/models/media/video/video_file_anonymize.py +38 -38
- endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
- endoreg_db/models/media/video/video_file_io.py +109 -62
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
- endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
- endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
- endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
- endoreg_db/models/media/video/video_file_segments.py +24 -17
- endoreg_db/models/media/video/video_metadata.py +19 -35
- endoreg_db/models/media/video/video_processing.py +96 -95
- endoreg_db/models/medical/contraindication/__init__.py +13 -3
- endoreg_db/models/medical/disease.py +22 -16
- endoreg_db/models/medical/event.py +31 -18
- endoreg_db/models/medical/examination/__init__.py +13 -6
- endoreg_db/models/medical/examination/examination.py +17 -18
- endoreg_db/models/medical/examination/examination_indication.py +26 -25
- endoreg_db/models/medical/examination/examination_time.py +16 -6
- endoreg_db/models/medical/examination/examination_time_type.py +9 -6
- endoreg_db/models/medical/examination/examination_type.py +3 -4
- endoreg_db/models/medical/finding/finding.py +38 -39
- endoreg_db/models/medical/finding/finding_classification.py +37 -48
- endoreg_db/models/medical/finding/finding_intervention.py +27 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +20 -26
- endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
- endoreg_db/models/medical/laboratory/lab_value.py +62 -91
- endoreg_db/models/medical/medication/medication.py +22 -10
- endoreg_db/models/medical/medication/medication_indication.py +29 -3
- endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
- endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
- endoreg_db/models/medical/medication/medication_schedule.py +27 -16
- endoreg_db/models/medical/organ/__init__.py +15 -12
- endoreg_db/models/medical/patient/medication_examples.py +1 -5
- endoreg_db/models/medical/patient/patient_disease.py +20 -23
- endoreg_db/models/medical/patient/patient_event.py +19 -22
- endoreg_db/models/medical/patient/patient_examination.py +48 -54
- endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
- endoreg_db/models/medical/patient/patient_finding.py +122 -139
- endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
- endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
- endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
- endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
- endoreg_db/models/medical/patient/patient_medication.py +27 -38
- endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
- endoreg_db/models/medical/risk/risk.py +7 -6
- endoreg_db/models/medical/risk/risk_type.py +8 -5
- endoreg_db/models/metadata/model_meta.py +60 -29
- endoreg_db/models/metadata/model_meta_logic.py +125 -18
- endoreg_db/models/metadata/pdf_meta.py +19 -24
- endoreg_db/models/metadata/sensitive_meta.py +102 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +192 -173
- endoreg_db/models/metadata/video_meta.py +51 -31
- endoreg_db/models/metadata/video_prediction_logic.py +16 -23
- endoreg_db/models/metadata/video_prediction_meta.py +29 -33
- endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
- endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
- endoreg_db/models/other/emission/emission_factor.py +18 -8
- endoreg_db/models/other/gender.py +10 -5
- endoreg_db/models/other/information_source.py +25 -25
- endoreg_db/models/other/material.py +9 -5
- endoreg_db/models/other/resource.py +6 -4
- endoreg_db/models/other/tag.py +10 -5
- endoreg_db/models/other/transport_route.py +13 -8
- endoreg_db/models/other/unit.py +10 -6
- endoreg_db/models/other/waste.py +6 -5
- endoreg_db/models/requirement/requirement.py +580 -272
- endoreg_db/models/requirement/requirement_error.py +85 -0
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
- endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
- endoreg_db/models/requirement/requirement_operator.py +36 -33
- endoreg_db/models/requirement/requirement_set.py +74 -57
- endoreg_db/models/state/__init__.py +4 -4
- endoreg_db/models/state/abstract.py +2 -2
- endoreg_db/models/state/anonymization.py +12 -0
- endoreg_db/models/state/audit_ledger.py +46 -47
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +40 -46
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +58 -53
- endoreg_db/models/upload_job.py +32 -55
- endoreg_db/models/utils.py +1 -2
- endoreg_db/root_urls.py +21 -2
- endoreg_db/serializers/__init__.py +0 -2
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/meta/report_meta.py +1 -1
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/file_overview.py +11 -99
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/services/anonymization.py +75 -73
- endoreg_db/services/lookup_service.py +37 -24
- endoreg_db/services/pdf_import.py +166 -68
- endoreg_db/services/storage_aware_video_processor.py +140 -114
- endoreg_db/services/video_import.py +193 -283
- endoreg_db/urls/__init__.py +7 -20
- endoreg_db/urls/media.py +108 -67
- endoreg_db/urls/root_urls.py +29 -0
- endoreg_db/utils/__init__.py +15 -5
- endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
- endoreg_db/utils/case_generator/__init__.py +3 -0
- endoreg_db/utils/dataloader.py +88 -16
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
- endoreg_db/utils/setup_config.py +8 -5
- endoreg_db/utils/storage.py +115 -0
- endoreg_db/utils/validate_endo_roi.py +8 -2
- endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
- endoreg_db/views/__init__.py +0 -10
- endoreg_db/views/anonymization/media_management.py +198 -163
- endoreg_db/views/anonymization/overview.py +4 -1
- endoreg_db/views/anonymization/validate.py +174 -40
- endoreg_db/views/media/__init__.py +2 -0
- endoreg_db/views/media/pdf_media.py +131 -152
- endoreg_db/views/media/sensitive_metadata.py +46 -6
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/media/video_segments.py +2 -3
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/pdf/pdf_stream.py +20 -21
- endoreg_db/views/pdf/reimport.py +11 -32
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +17 -3
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +0 -2
- endoreg_db/views/video/correction.py +2 -2
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +341 -245
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/media/video/video_file_frames.py +0 -0
- endoreg_db/models/metadata/frame_ocr_result.py +0 -0
- endoreg_db/models/rule/__init__.py +0 -13
- endoreg_db/models/rule/rule.py +0 -27
- endoreg_db/models/rule/rule_applicator.py +0 -224
- endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
- endoreg_db/models/rule/rule_type.py +0 -20
- endoreg_db/models/rule/ruleset.py +0 -17
- endoreg_db/serializers/video/video_metadata.py +0 -105
- endoreg_db/urls/report.py +0 -48
- endoreg_db/urls/video.py +0 -61
- endoreg_db/utils/case_generator/case_generator.py +0 -159
- endoreg_db/utils/case_generator/utils.py +0 -30
- endoreg_db/views/report/__init__.py +0 -9
- endoreg_db/views/report/report_list.py +0 -112
- endoreg_db/views/report/report_with_secure_url.py +0 -28
- endoreg_db/views/report/start_examination.py +0 -7
- endoreg_db/views.py +0 -0
- /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
- /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
- /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
- /endoreg_db/{models/media/video/refactor_plan.md → views/pdf/pdf_stream_views.py} +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
endoreg_db/views/__init__.py
CHANGED
|
@@ -108,17 +108,9 @@ from .patient_finding_classification import (
|
|
|
108
108
|
)
|
|
109
109
|
|
|
110
110
|
from .pdf import (
|
|
111
|
-
PdfMediaView,
|
|
112
111
|
PdfReimportView,
|
|
113
112
|
PdfStreamView,
|
|
114
113
|
)
|
|
115
|
-
|
|
116
|
-
from .report import (
|
|
117
|
-
ReportListView,
|
|
118
|
-
ReportWithSecureUrlView,
|
|
119
|
-
start_examination,
|
|
120
|
-
)
|
|
121
|
-
|
|
122
114
|
from .requirement import (
|
|
123
115
|
evaluate_requirements,
|
|
124
116
|
LookupViewSet,
|
|
@@ -126,7 +118,6 @@ from .requirement import (
|
|
|
126
118
|
|
|
127
119
|
from .video import (
|
|
128
120
|
# Video Correction (Phase 1.1) - Implemented
|
|
129
|
-
VideoMetadataView,
|
|
130
121
|
VideoProcessingHistoryView,
|
|
131
122
|
VideoApplyMaskView,
|
|
132
123
|
VideoRemoveFramesView,
|
|
@@ -138,7 +129,6 @@ from .video import (
|
|
|
138
129
|
VideoLabelView,
|
|
139
130
|
UpdateLabelSegmentsView,
|
|
140
131
|
rerun_segmentation,
|
|
141
|
-
video_timeline_view,
|
|
142
132
|
VideoExaminationViewSet,
|
|
143
133
|
VideoCorrectionView,
|
|
144
134
|
)
|
|
@@ -1,18 +1,19 @@
|
|
|
1
1
|
# endoreg_db/views/media_management.py
|
|
2
2
|
|
|
3
3
|
import logging
|
|
4
|
-
from
|
|
4
|
+
from datetime import timedelta
|
|
5
|
+
from typing import Any, Dict
|
|
6
|
+
|
|
5
7
|
from django.db import transaction
|
|
6
8
|
from django.db.models import Q
|
|
9
|
+
from django.utils import timezone
|
|
7
10
|
from numpy import delete
|
|
8
11
|
from rest_framework import status
|
|
9
12
|
from rest_framework.decorators import api_view, permission_classes
|
|
10
13
|
from rest_framework.response import Response
|
|
11
14
|
from rest_framework.views import APIView
|
|
12
|
-
from django.utils import timezone
|
|
13
|
-
from datetime import timedelta
|
|
14
15
|
|
|
15
|
-
from endoreg_db.models import
|
|
16
|
+
from endoreg_db.models import RawPdfFile, VideoFile, VideoState
|
|
16
17
|
from endoreg_db.utils.permissions import DEBUG_PERMISSIONS
|
|
17
18
|
|
|
18
19
|
logger = logging.getLogger(__name__)
|
|
@@ -21,10 +22,12 @@ logger = logging.getLogger(__name__)
|
|
|
21
22
|
# Media Cleanup and Management API
|
|
22
23
|
# ---------------------------------------------------------------------------
|
|
23
24
|
|
|
25
|
+
|
|
24
26
|
class MediaManagementView(APIView):
|
|
25
27
|
"""
|
|
26
28
|
Comprehensive Media Management API for cleanup and maintenance operations
|
|
27
29
|
"""
|
|
30
|
+
|
|
28
31
|
permission_classes = DEBUG_PERMISSIONS
|
|
29
32
|
|
|
30
33
|
def get(self, request):
|
|
@@ -38,8 +41,8 @@ class MediaManagementView(APIView):
|
|
|
38
41
|
except Exception as e:
|
|
39
42
|
logger.error(f"Error getting media status overview: {e}")
|
|
40
43
|
return Response(
|
|
41
|
-
{"error": "Failed to get status overview"},
|
|
42
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
44
|
+
{"error": "Failed to get status overview"},
|
|
45
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
43
46
|
)
|
|
44
47
|
|
|
45
48
|
def delete(self, request):
|
|
@@ -47,10 +50,10 @@ class MediaManagementView(APIView):
|
|
|
47
50
|
DELETE /api/media-management/cleanup/
|
|
48
51
|
Cleanup unfinished, failed, or stale media processing entries
|
|
49
52
|
"""
|
|
50
|
-
cleanup_type = request.query_params.get(
|
|
51
|
-
force = request.query_params.get(
|
|
52
|
-
media_type = request.query_params.get(
|
|
53
|
-
file_id = request.query_params.get(
|
|
53
|
+
cleanup_type = request.query_params.get("type", "unfinished")
|
|
54
|
+
force = request.query_params.get("force", "false").lower() == "true"
|
|
55
|
+
media_type = request.query_params.get("file_type", "all")
|
|
56
|
+
file_id = request.query_params.get("file_id", None)
|
|
54
57
|
|
|
55
58
|
try:
|
|
56
59
|
result = self._perform_cleanup(cleanup_type, force, media_type, file_id)
|
|
@@ -59,40 +62,40 @@ class MediaManagementView(APIView):
|
|
|
59
62
|
except Exception as e:
|
|
60
63
|
logger.error(f"Error during media cleanup: {e}")
|
|
61
64
|
return Response(
|
|
62
|
-
{"error": "Cleanup operation failed"},
|
|
63
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
65
|
+
{"error": "Cleanup operation failed"},
|
|
66
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
64
67
|
)
|
|
65
68
|
|
|
66
69
|
def _get_status_overview(self) -> Dict[str, Any]:
|
|
67
70
|
"""Get comprehensive status overview"""
|
|
68
71
|
video_stats = self._get_video_stats()
|
|
69
72
|
pdf_stats = self._get_pdf_stats()
|
|
70
|
-
|
|
73
|
+
|
|
71
74
|
# Stale processing detection (older than 2 hours)
|
|
72
75
|
stale_threshold = timezone.now() - timedelta(hours=2)
|
|
73
76
|
# Use VideoState boolean fields instead of non-existent name field
|
|
74
77
|
stale_videos = VideoFile.objects.filter(
|
|
75
78
|
uploaded_at__lt=stale_threshold,
|
|
76
79
|
state__frames_extracted=True,
|
|
77
|
-
state__sensitive_meta_processed=False
|
|
80
|
+
state__sensitive_meta_processed=False,
|
|
78
81
|
).count()
|
|
79
|
-
|
|
82
|
+
|
|
80
83
|
return {
|
|
81
84
|
"videos": video_stats,
|
|
82
85
|
"pdfs": pdf_stats,
|
|
83
86
|
"cleanup_opportunities": {
|
|
84
87
|
"stale_processing": stale_videos,
|
|
85
88
|
"failed_videos": video_stats["failed"],
|
|
86
|
-
"unfinished_total": video_stats["unfinished"] + pdf_stats["unfinished"]
|
|
89
|
+
"unfinished_total": video_stats["unfinished"] + pdf_stats["unfinished"],
|
|
87
90
|
},
|
|
88
91
|
"total_files": video_stats["total"] + pdf_stats["total"],
|
|
89
|
-
"timestamp": timezone.now().isoformat()
|
|
92
|
+
"timestamp": timezone.now().isoformat(),
|
|
90
93
|
}
|
|
91
94
|
|
|
92
95
|
def _get_video_stats(self) -> Dict[str, int]:
|
|
93
96
|
"""Get video file statistics using VideoState boolean fields"""
|
|
94
|
-
videos = VideoFile.objects.select_related(
|
|
95
|
-
|
|
97
|
+
videos = VideoFile.objects.select_related("state").all()
|
|
98
|
+
|
|
96
99
|
stats = {
|
|
97
100
|
"total": videos.count(),
|
|
98
101
|
"not_started": 0,
|
|
@@ -100,30 +103,33 @@ class MediaManagementView(APIView):
|
|
|
100
103
|
"done": 0,
|
|
101
104
|
"failed": 0,
|
|
102
105
|
"validated": 0,
|
|
103
|
-
"unfinished": 0
|
|
106
|
+
"unfinished": 0,
|
|
104
107
|
}
|
|
105
|
-
|
|
108
|
+
|
|
106
109
|
for video in videos:
|
|
107
110
|
if not video.state:
|
|
108
111
|
stats["not_started"] += 1
|
|
109
112
|
stats["unfinished"] += 1
|
|
110
113
|
continue
|
|
111
|
-
|
|
114
|
+
|
|
112
115
|
# Use the anonymization_status property
|
|
113
116
|
video_status = video.state.anonymization_status
|
|
114
|
-
|
|
115
|
-
if video_status.value ==
|
|
117
|
+
|
|
118
|
+
if video_status.value == "not_started":
|
|
116
119
|
stats["not_started"] += 1
|
|
117
120
|
stats["unfinished"] += 1
|
|
118
|
-
elif video_status.value in [
|
|
121
|
+
elif video_status.value in [
|
|
122
|
+
"extracting_frames",
|
|
123
|
+
"processing_anonymization",
|
|
124
|
+
]:
|
|
119
125
|
stats["processing"] += 1
|
|
120
126
|
stats["unfinished"] += 1
|
|
121
|
-
elif video_status.value ==
|
|
127
|
+
elif video_status.value == "done_processing_anonymization":
|
|
122
128
|
stats["done"] += 1
|
|
123
|
-
elif video_status.value ==
|
|
129
|
+
elif video_status.value == "failed":
|
|
124
130
|
stats["failed"] += 1
|
|
125
131
|
stats["unfinished"] += 1
|
|
126
|
-
elif video_status.value ==
|
|
132
|
+
elif video_status.value == "validated":
|
|
127
133
|
stats["validated"] += 1
|
|
128
134
|
else:
|
|
129
135
|
stats["unfinished"] += 1
|
|
@@ -132,7 +138,7 @@ class MediaManagementView(APIView):
|
|
|
132
138
|
def _get_pdf_stats(self) -> Dict[str, int]:
|
|
133
139
|
"""Get PDF file statistics"""
|
|
134
140
|
pdfs = RawPdfFile.objects.all()
|
|
135
|
-
|
|
141
|
+
|
|
136
142
|
stats = {
|
|
137
143
|
"total": pdfs.count(),
|
|
138
144
|
"not_started": 0,
|
|
@@ -140,14 +146,18 @@ class MediaManagementView(APIView):
|
|
|
140
146
|
"done": 0,
|
|
141
147
|
"failed": 0,
|
|
142
148
|
"validated": 0,
|
|
143
|
-
"unfinished": 0
|
|
149
|
+
"unfinished": 0,
|
|
144
150
|
}
|
|
145
|
-
|
|
151
|
+
|
|
146
152
|
for pdf in pdfs:
|
|
147
153
|
# PDF status logic based on anonymized_text presence and validation
|
|
148
154
|
has_anonymized = bool(pdf.anonymized_text and pdf.anonymized_text.strip())
|
|
149
|
-
is_validated =
|
|
150
|
-
|
|
155
|
+
is_validated = (
|
|
156
|
+
getattr(pdf.sensitive_meta, "is_verified", False)
|
|
157
|
+
if pdf.sensitive_meta
|
|
158
|
+
else False
|
|
159
|
+
)
|
|
160
|
+
|
|
151
161
|
if not has_anonymized:
|
|
152
162
|
stats["not_started"] += 1
|
|
153
163
|
stats["unfinished"] += 1
|
|
@@ -157,22 +167,24 @@ class MediaManagementView(APIView):
|
|
|
157
167
|
stats["done"] += 1
|
|
158
168
|
else:
|
|
159
169
|
stats["unfinished"] += 1
|
|
160
|
-
|
|
170
|
+
|
|
161
171
|
return stats
|
|
162
172
|
|
|
163
|
-
def _perform_cleanup(
|
|
173
|
+
def _perform_cleanup(
|
|
174
|
+
self, cleanup_type: str, force: bool, media_type: str, file_id: int
|
|
175
|
+
) -> Dict[str, Any]:
|
|
164
176
|
"""Perform the actual cleanup operations"""
|
|
165
|
-
|
|
177
|
+
|
|
166
178
|
result = {
|
|
167
179
|
"cleanup_type": cleanup_type,
|
|
168
180
|
"force": force,
|
|
169
181
|
"removed_items": [],
|
|
170
|
-
"summary": {}
|
|
182
|
+
"summary": {},
|
|
171
183
|
}
|
|
172
|
-
|
|
173
|
-
if media_type not in [
|
|
184
|
+
|
|
185
|
+
if media_type not in ["video", "pdf", "all"]:
|
|
174
186
|
raise ValueError(f"Unknown media type: {media_type}")
|
|
175
|
-
|
|
187
|
+
|
|
176
188
|
if file_id is not None:
|
|
177
189
|
try:
|
|
178
190
|
file_id = int(file_id)
|
|
@@ -181,81 +193,91 @@ class MediaManagementView(APIView):
|
|
|
181
193
|
|
|
182
194
|
video_file_obj = None
|
|
183
195
|
pdf_file_obj = None
|
|
184
|
-
|
|
185
|
-
if media_type == 'video':
|
|
186
|
-
video_file_obj = VideoFile.get_video_by_id(self, video_id=file_id) if file_id else None
|
|
187
|
-
elif media_type == 'pdf':
|
|
188
|
-
pdf_file_obj = RawPdfFile.get_pdf_by_id(file_id) if file_id else None
|
|
189
196
|
|
|
197
|
+
if media_type == "video":
|
|
198
|
+
video_file_obj = (
|
|
199
|
+
VideoFile.get_video_by_id(self, video_id=file_id) if file_id else None
|
|
200
|
+
)
|
|
201
|
+
elif media_type == "pdf":
|
|
202
|
+
pdf_file_obj = RawPdfFile.get_pdf_by_id(file_id) if file_id else None
|
|
190
203
|
|
|
191
204
|
with transaction.atomic():
|
|
192
205
|
if video_file_obj:
|
|
193
206
|
video_file_obj.delete()
|
|
194
207
|
if pdf_file_obj:
|
|
195
208
|
pdf_file_obj.delete()
|
|
196
|
-
|
|
197
|
-
if cleanup_type ==
|
|
209
|
+
|
|
210
|
+
if cleanup_type == "unfinished":
|
|
198
211
|
result.update(self._cleanup_unfinished_media(force))
|
|
199
|
-
elif cleanup_type ==
|
|
212
|
+
elif cleanup_type == "failed":
|
|
200
213
|
result.update(self._cleanup_failed_media(force))
|
|
201
|
-
elif cleanup_type ==
|
|
214
|
+
elif cleanup_type == "stale":
|
|
202
215
|
result.update(self._cleanup_stale_processing(force))
|
|
203
|
-
elif cleanup_type ==
|
|
216
|
+
elif cleanup_type == "all":
|
|
204
217
|
unfinished = self._cleanup_unfinished_media(force)
|
|
205
218
|
failed = self._cleanup_failed_media(force)
|
|
206
219
|
stale = self._cleanup_stale_processing(force)
|
|
207
|
-
|
|
220
|
+
|
|
208
221
|
result["removed_items"] = (
|
|
209
|
-
unfinished.get("removed_items", [])
|
|
210
|
-
failed.get("removed_items", [])
|
|
211
|
-
stale.get("removed_items", [])
|
|
222
|
+
unfinished.get("removed_items", [])
|
|
223
|
+
+ failed.get("removed_items", [])
|
|
224
|
+
+ stale.get("removed_items", [])
|
|
212
225
|
)
|
|
213
226
|
result["summary"] = {
|
|
214
227
|
"unfinished": unfinished.get("summary", {}),
|
|
215
228
|
"failed": failed.get("summary", {}),
|
|
216
|
-
"stale": stale.get("summary", {})
|
|
229
|
+
"stale": stale.get("summary", {}),
|
|
217
230
|
}
|
|
218
231
|
else:
|
|
219
232
|
raise ValueError(f"Unknown cleanup type: {cleanup_type}")
|
|
220
|
-
|
|
233
|
+
|
|
221
234
|
return result
|
|
222
235
|
|
|
223
236
|
def _cleanup_unfinished_media(self, force: bool) -> Dict[str, Any]:
|
|
224
237
|
"""Remove unfinished media processing entries"""
|
|
225
238
|
removed_videos = []
|
|
226
239
|
removed_pdfs = []
|
|
227
|
-
|
|
240
|
+
|
|
228
241
|
# Find unfinished videos using VideoState boolean fields
|
|
229
|
-
unfinished_videos = VideoFile.objects.select_related(
|
|
230
|
-
|
|
242
|
+
unfinished_videos = VideoFile.objects.select_related("state").all()
|
|
243
|
+
|
|
231
244
|
for video in unfinished_videos:
|
|
232
245
|
if not video.state:
|
|
233
246
|
if force: # Only remove videos without state if force=True
|
|
234
|
-
removed_videos.append(
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
247
|
+
removed_videos.append(
|
|
248
|
+
{
|
|
249
|
+
"id": video.id,
|
|
250
|
+
"type": "video",
|
|
251
|
+
"filename": video.original_file_name,
|
|
252
|
+
"status": "no_state",
|
|
253
|
+
"uploaded_at": video.uploaded_at.isoformat(),
|
|
254
|
+
}
|
|
255
|
+
)
|
|
241
256
|
video.delete()
|
|
242
257
|
continue
|
|
243
|
-
|
|
258
|
+
|
|
244
259
|
video_status = video.state.anonymization_status
|
|
245
|
-
is_unfinished = video_status.value in [
|
|
246
|
-
|
|
260
|
+
is_unfinished = video_status.value in [
|
|
261
|
+
"not_started",
|
|
262
|
+
"extracting_frames",
|
|
263
|
+
"processing_anonymization",
|
|
264
|
+
"failed",
|
|
265
|
+
]
|
|
266
|
+
|
|
247
267
|
# Remove unfinished videos
|
|
248
|
-
if is_unfinished and (force or video_status.value !=
|
|
249
|
-
removed_videos.append(
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
268
|
+
if is_unfinished and (force or video_status.value != "not_started"):
|
|
269
|
+
removed_videos.append(
|
|
270
|
+
{
|
|
271
|
+
"id": video.id,
|
|
272
|
+
"type": "video",
|
|
273
|
+
"filename": video.original_file_name,
|
|
274
|
+
"status": video_status.value,
|
|
275
|
+
"uploaded_at": video.uploaded_at.isoformat(),
|
|
276
|
+
}
|
|
277
|
+
)
|
|
256
278
|
if force:
|
|
257
279
|
video.delete()
|
|
258
|
-
|
|
280
|
+
|
|
259
281
|
# Return the results
|
|
260
282
|
return {
|
|
261
283
|
"removed_items": removed_videos + removed_pdfs,
|
|
@@ -263,29 +285,31 @@ class MediaManagementView(APIView):
|
|
|
263
285
|
"videos_removed": len(removed_videos),
|
|
264
286
|
"pdfs_removed": len(removed_pdfs),
|
|
265
287
|
"total_removed": len(removed_videos) + len(removed_pdfs),
|
|
266
|
-
"dry_run": not force
|
|
267
|
-
}
|
|
288
|
+
"dry_run": not force,
|
|
289
|
+
},
|
|
268
290
|
}
|
|
269
291
|
|
|
270
292
|
def _cleanup_failed_media(self, force: bool) -> Dict[str, Any]:
|
|
271
293
|
"""Remove failed media processing entries"""
|
|
272
294
|
removed_items = []
|
|
273
|
-
|
|
295
|
+
|
|
274
296
|
# Find failed videos using VideoState boolean fields
|
|
275
|
-
failed_videos = VideoFile.objects.select_related(
|
|
276
|
-
|
|
297
|
+
failed_videos = VideoFile.objects.select_related("state").all()
|
|
298
|
+
|
|
277
299
|
for video in failed_videos:
|
|
278
|
-
if video.state and video.state.anonymization_status.value ==
|
|
279
|
-
removed_items.append(
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
300
|
+
if video.state and video.state.anonymization_status.value == "failed":
|
|
301
|
+
removed_items.append(
|
|
302
|
+
{
|
|
303
|
+
"id": video.id,
|
|
304
|
+
"type": "video",
|
|
305
|
+
"filename": video.original_file_name,
|
|
306
|
+
"status": "failed",
|
|
307
|
+
"uploaded_at": video.uploaded_at.isoformat(),
|
|
308
|
+
}
|
|
309
|
+
)
|
|
286
310
|
if force:
|
|
287
311
|
video.delete()
|
|
288
|
-
|
|
312
|
+
|
|
289
313
|
if force:
|
|
290
314
|
# Count actual deletions
|
|
291
315
|
videos_deleted = len([v for v in removed_items if v["type"] == "video"])
|
|
@@ -294,8 +318,8 @@ class MediaManagementView(APIView):
|
|
|
294
318
|
"summary": {
|
|
295
319
|
"videos_removed": videos_deleted,
|
|
296
320
|
"total_removed": videos_deleted,
|
|
297
|
-
"dry_run": False
|
|
298
|
-
}
|
|
321
|
+
"dry_run": False,
|
|
322
|
+
},
|
|
299
323
|
}
|
|
300
324
|
else:
|
|
301
325
|
return {
|
|
@@ -303,33 +327,40 @@ class MediaManagementView(APIView):
|
|
|
303
327
|
"summary": {
|
|
304
328
|
"videos_removed": len(removed_items),
|
|
305
329
|
"total_removed": len(removed_items),
|
|
306
|
-
"dry_run": True
|
|
307
|
-
}
|
|
330
|
+
"dry_run": True,
|
|
331
|
+
},
|
|
308
332
|
}
|
|
309
333
|
|
|
310
334
|
def _cleanup_stale_processing(self, force: bool) -> Dict[str, Any]:
|
|
311
335
|
"""Remove stale processing entries (older than 2 hours)"""
|
|
312
336
|
stale_threshold = timezone.now() - timedelta(hours=2)
|
|
313
337
|
removed_items = []
|
|
314
|
-
|
|
338
|
+
|
|
315
339
|
# Find stale videos using VideoState boolean fields
|
|
316
340
|
stale_videos = VideoFile.objects.filter(
|
|
317
341
|
uploaded_at__lt=stale_threshold,
|
|
318
342
|
state__frames_extracted=True,
|
|
319
|
-
state__sensitive_meta_processed=False
|
|
320
|
-
).select_related(
|
|
321
|
-
|
|
343
|
+
state__sensitive_meta_processed=False,
|
|
344
|
+
).select_related("state")
|
|
345
|
+
|
|
322
346
|
for video in stale_videos:
|
|
323
|
-
video_status =
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
347
|
+
video_status = (
|
|
348
|
+
video.state.anonymization_status if video.state else "no_state"
|
|
349
|
+
)
|
|
350
|
+
removed_items.append(
|
|
351
|
+
{
|
|
352
|
+
"id": video.id,
|
|
353
|
+
"type": "video",
|
|
354
|
+
"filename": video.original_file_name,
|
|
355
|
+
"status": f"stale_{video_status.value if hasattr(video_status, 'value') else video_status}",
|
|
356
|
+
"uploaded_at": video.uploaded_at.isoformat(),
|
|
357
|
+
"stale_duration_hours": (
|
|
358
|
+
timezone.now() - video.uploaded_at
|
|
359
|
+
).total_seconds()
|
|
360
|
+
/ 3600,
|
|
361
|
+
}
|
|
362
|
+
)
|
|
363
|
+
|
|
333
364
|
if force:
|
|
334
365
|
videos_deleted = stale_videos.delete()[0]
|
|
335
366
|
return {
|
|
@@ -337,8 +368,8 @@ class MediaManagementView(APIView):
|
|
|
337
368
|
"summary": {
|
|
338
369
|
"stale_videos_removed": videos_deleted,
|
|
339
370
|
"total_removed": videos_deleted,
|
|
340
|
-
"dry_run": False
|
|
341
|
-
}
|
|
371
|
+
"dry_run": False,
|
|
372
|
+
},
|
|
342
373
|
}
|
|
343
374
|
else:
|
|
344
375
|
return {
|
|
@@ -346,12 +377,12 @@ class MediaManagementView(APIView):
|
|
|
346
377
|
"summary": {
|
|
347
378
|
"stale_videos_removed": len(removed_items),
|
|
348
379
|
"total_removed": len(removed_items),
|
|
349
|
-
"dry_run": True
|
|
350
|
-
}
|
|
380
|
+
"dry_run": True,
|
|
381
|
+
},
|
|
351
382
|
}
|
|
352
383
|
|
|
353
384
|
|
|
354
|
-
@api_view([
|
|
385
|
+
@api_view(["DELETE"])
|
|
355
386
|
@permission_classes(DEBUG_PERMISSIONS)
|
|
356
387
|
def force_remove_media(request, file_id: int):
|
|
357
388
|
"""
|
|
@@ -364,43 +395,44 @@ def force_remove_media(request, file_id: int):
|
|
|
364
395
|
video = VideoFile.objects.get(id=file_id)
|
|
365
396
|
filename = video.original_file_name
|
|
366
397
|
video.delete()
|
|
367
|
-
|
|
368
|
-
return Response(
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
398
|
+
|
|
399
|
+
return Response(
|
|
400
|
+
{
|
|
401
|
+
"detail": f"Video file '{filename}' (ID: {file_id}) removed successfully",
|
|
402
|
+
"file_type": "video",
|
|
403
|
+
"file_id": file_id,
|
|
404
|
+
}
|
|
405
|
+
)
|
|
373
406
|
except VideoFile.DoesNotExist:
|
|
374
407
|
pass
|
|
375
|
-
|
|
408
|
+
|
|
376
409
|
# Try to find and delete from RawPdfFile
|
|
377
410
|
try:
|
|
378
411
|
pdf = RawPdfFile.objects.get(id=file_id)
|
|
379
|
-
filename = getattr(pdf.file,
|
|
412
|
+
filename = getattr(pdf.file, "name", "Unknown")
|
|
380
413
|
pdf.delete()
|
|
381
|
-
|
|
382
|
-
return Response(
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
414
|
+
|
|
415
|
+
return Response(
|
|
416
|
+
{
|
|
417
|
+
"detail": f"PDF file '{filename}' (ID: {file_id}) removed successfully",
|
|
418
|
+
"file_type": "pdf",
|
|
419
|
+
"file_id": file_id,
|
|
420
|
+
}
|
|
421
|
+
)
|
|
387
422
|
except RawPdfFile.DoesNotExist:
|
|
388
423
|
pass
|
|
389
|
-
|
|
390
|
-
return Response(
|
|
391
|
-
|
|
392
|
-
status=status.HTTP_404_NOT_FOUND
|
|
393
|
-
)
|
|
394
|
-
|
|
424
|
+
|
|
425
|
+
return Response({"detail": "File not found"}, status=status.HTTP_404_NOT_FOUND)
|
|
426
|
+
|
|
395
427
|
except Exception as e:
|
|
396
428
|
logger.error(f"Error force removing media {file_id}: {e}")
|
|
397
429
|
return Response(
|
|
398
|
-
{"error": "Force removal failed"},
|
|
399
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
430
|
+
{"error": "Force removal failed"},
|
|
431
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
400
432
|
)
|
|
401
433
|
|
|
402
434
|
|
|
403
|
-
@api_view([
|
|
435
|
+
@api_view(["POST"])
|
|
404
436
|
@permission_classes(DEBUG_PERMISSIONS)
|
|
405
437
|
def reset_processing_status(request, file_id: int):
|
|
406
438
|
"""
|
|
@@ -411,44 +443,47 @@ def reset_processing_status(request, file_id: int):
|
|
|
411
443
|
# Try VideoFile first
|
|
412
444
|
try:
|
|
413
445
|
video = VideoFile.objects.get(id=file_id)
|
|
414
|
-
|
|
446
|
+
|
|
415
447
|
# Reset to 'not_started' state
|
|
416
|
-
not_started_state, created = VideoState.objects.get_or_create(
|
|
448
|
+
not_started_state, created = VideoState.objects.get_or_create(
|
|
449
|
+
name="not_started"
|
|
450
|
+
)
|
|
417
451
|
video.state = not_started_state
|
|
418
452
|
video.save()
|
|
419
|
-
|
|
420
|
-
return Response(
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
453
|
+
|
|
454
|
+
return Response(
|
|
455
|
+
{
|
|
456
|
+
"detail": "Video file status reset to 'not_started'",
|
|
457
|
+
"file_type": "video",
|
|
458
|
+
"file_id": file_id,
|
|
459
|
+
"new_status": "not_started",
|
|
460
|
+
}
|
|
461
|
+
)
|
|
426
462
|
except VideoFile.DoesNotExist:
|
|
427
463
|
pass
|
|
428
|
-
|
|
464
|
+
|
|
429
465
|
# PDF files don't have state, but we can clear anonymized_text
|
|
430
466
|
try:
|
|
431
467
|
pdf = RawPdfFile.objects.get(id=file_id)
|
|
432
468
|
pdf.anonymized_text = ""
|
|
433
469
|
pdf.save()
|
|
434
|
-
|
|
435
|
-
return Response(
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
470
|
+
|
|
471
|
+
return Response(
|
|
472
|
+
{
|
|
473
|
+
"detail": "PDF file processing reset",
|
|
474
|
+
"file_type": "pdf",
|
|
475
|
+
"file_id": file_id,
|
|
476
|
+
"new_status": "not_started",
|
|
477
|
+
}
|
|
478
|
+
)
|
|
441
479
|
except RawPdfFile.DoesNotExist:
|
|
442
480
|
pass
|
|
443
|
-
|
|
444
|
-
return Response(
|
|
445
|
-
|
|
446
|
-
status=status.HTTP_404_NOT_FOUND
|
|
447
|
-
)
|
|
448
|
-
|
|
481
|
+
|
|
482
|
+
return Response({"detail": "File not found"}, status=status.HTTP_404_NOT_FOUND)
|
|
483
|
+
|
|
449
484
|
except Exception as e:
|
|
450
485
|
logger.error(f"Error resetting status for media {file_id}: {e}")
|
|
451
486
|
return Response(
|
|
452
|
-
{"error": "Status reset failed"},
|
|
453
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
487
|
+
{"error": "Status reset failed"},
|
|
488
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
454
489
|
)
|