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
|
@@ -4,15 +4,18 @@ import logging
|
|
|
4
4
|
import os
|
|
5
5
|
import uuid
|
|
6
6
|
from pathlib import Path
|
|
7
|
-
from typing import TYPE_CHECKING, Optional, Union, cast
|
|
7
|
+
from typing import TYPE_CHECKING, Optional, Self, Union, cast
|
|
8
8
|
|
|
9
9
|
from django.core.files import File
|
|
10
10
|
from django.core.validators import FileExtensionValidator
|
|
11
11
|
from django.db import models
|
|
12
12
|
from django.db.models import F
|
|
13
13
|
from django.db.models.fields.files import FieldFile
|
|
14
|
+
from librosa import frames_to_samples
|
|
15
|
+
from pandas.core import frame
|
|
14
16
|
|
|
15
17
|
from endoreg_db.utils.calc_duration_seconds import _calc_duration_vf
|
|
18
|
+
from endoreg_db.utils.video.ffmpeg_wrapper import assemble_video_from_frames
|
|
16
19
|
|
|
17
20
|
from ...label import Label, LabelVideoSegment
|
|
18
21
|
from ...state import VideoState
|
|
@@ -23,11 +26,7 @@ from .create_from_file import _create_from_file
|
|
|
23
26
|
from .pipe_1 import _pipe_1, _test_after_pipe_1
|
|
24
27
|
from .pipe_2 import _pipe_2
|
|
25
28
|
from .video_file_ai import _extract_text_from_video_frames, _predict_video_pipeline
|
|
26
|
-
from .video_file_anonymize import
|
|
27
|
-
_anonymize,
|
|
28
|
-
_cleanup_raw_assets,
|
|
29
|
-
_create_anonymized_frame_files,
|
|
30
|
-
)
|
|
29
|
+
from .video_file_anonymize import _anonymize, _censor_outside_frames, _cleanup_raw_assets, _create_anonymized_frame_files
|
|
31
30
|
from .video_file_frames import (
|
|
32
31
|
_bulk_create_frames,
|
|
33
32
|
_create_frame_object,
|
|
@@ -72,6 +71,8 @@ from .video_file_meta import (
|
|
|
72
71
|
logger = logging.getLogger(__name__) # Changed from "video_file"
|
|
73
72
|
|
|
74
73
|
if TYPE_CHECKING:
|
|
74
|
+
from django.db.models.fields.files import FieldFile
|
|
75
|
+
|
|
75
76
|
from endoreg_db.models import (
|
|
76
77
|
Center,
|
|
77
78
|
EndoscopyProcessor,
|
|
@@ -84,6 +85,7 @@ if TYPE_CHECKING:
|
|
|
84
85
|
VideoImportMeta,
|
|
85
86
|
VideoMeta,
|
|
86
87
|
VideoState,
|
|
88
|
+
SensitiveMeta
|
|
87
89
|
)
|
|
88
90
|
|
|
89
91
|
|
|
@@ -125,9 +127,7 @@ class VideoFile(models.Model):
|
|
|
125
127
|
blank=True,
|
|
126
128
|
)
|
|
127
129
|
|
|
128
|
-
video_hash = models.CharField(
|
|
129
|
-
max_length=255, unique=True, help_text="Hash of the raw video file."
|
|
130
|
-
)
|
|
130
|
+
video_hash = models.CharField(max_length=255, unique=True, help_text="Hash of the raw video file.")
|
|
131
131
|
processed_video_hash = models.CharField(
|
|
132
132
|
max_length=255,
|
|
133
133
|
unique=True,
|
|
@@ -142,45 +142,39 @@ class VideoFile(models.Model):
|
|
|
142
142
|
null=True,
|
|
143
143
|
blank=True,
|
|
144
144
|
related_name="video_file",
|
|
145
|
-
)
|
|
146
|
-
center = models.ForeignKey("Center", on_delete=models.PROTECT)
|
|
147
|
-
processor = models.ForeignKey(
|
|
148
|
-
"EndoscopyProcessor", on_delete=models.PROTECT, blank=True, null=True
|
|
149
|
-
) # type: ignore
|
|
145
|
+
)
|
|
146
|
+
center = models.ForeignKey("Center", on_delete=models.PROTECT)
|
|
147
|
+
processor = models.ForeignKey("EndoscopyProcessor", on_delete=models.PROTECT, blank=True, null=True)
|
|
150
148
|
video_meta = models.OneToOneField(
|
|
151
149
|
"VideoMeta",
|
|
152
150
|
on_delete=models.SET_NULL,
|
|
153
151
|
null=True,
|
|
154
152
|
blank=True,
|
|
155
153
|
related_name="video_file",
|
|
156
|
-
)
|
|
154
|
+
)
|
|
157
155
|
examination = models.ForeignKey(
|
|
158
156
|
"PatientExamination",
|
|
159
157
|
on_delete=models.SET_NULL,
|
|
160
158
|
blank=True,
|
|
161
159
|
null=True,
|
|
162
160
|
related_name="video_files",
|
|
163
|
-
)
|
|
161
|
+
)
|
|
164
162
|
patient = models.ForeignKey(
|
|
165
163
|
"Patient",
|
|
166
164
|
on_delete=models.SET_NULL,
|
|
167
165
|
blank=True,
|
|
168
166
|
null=True,
|
|
169
167
|
related_name="video_files",
|
|
170
|
-
)
|
|
171
|
-
ai_model_meta = models.ForeignKey(
|
|
172
|
-
"ModelMeta", on_delete=models.SET_NULL, blank=True, null=True
|
|
173
|
-
) # type: ignore
|
|
168
|
+
)
|
|
169
|
+
ai_model_meta = models.ForeignKey("ModelMeta", on_delete=models.SET_NULL, blank=True, null=True)
|
|
174
170
|
state = models.OneToOneField(
|
|
175
171
|
"VideoState",
|
|
176
172
|
on_delete=models.SET_NULL,
|
|
177
173
|
null=True,
|
|
178
174
|
blank=True,
|
|
179
175
|
related_name="video_file",
|
|
180
|
-
)
|
|
181
|
-
import_meta = models.OneToOneField(
|
|
182
|
-
"VideoImportMeta", on_delete=models.CASCADE, blank=True, null=True
|
|
183
|
-
) # type: ignore
|
|
176
|
+
)
|
|
177
|
+
import_meta = models.OneToOneField("VideoImportMeta", on_delete=models.CASCADE, blank=True, null=True)
|
|
184
178
|
|
|
185
179
|
original_file_name = models.CharField(max_length=255, blank=True, null=True)
|
|
186
180
|
uploaded_at = models.DateTimeField(auto_now_add=True)
|
|
@@ -206,17 +200,25 @@ class VideoFile(models.Model):
|
|
|
206
200
|
date_modified = models.DateTimeField(auto_now=True)
|
|
207
201
|
|
|
208
202
|
if TYPE_CHECKING:
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
203
|
+
from django.db.models.manager import RelatedManager
|
|
204
|
+
|
|
205
|
+
@property
|
|
206
|
+
def label_video_segments(self) -> RelatedManager[LabelVideoSegment]: ...
|
|
207
|
+
|
|
208
|
+
@property
|
|
209
|
+
def frames(self) -> RelatedManager[Frame]: ...
|
|
210
|
+
|
|
211
|
+
center: models.ForeignKey["Center"]
|
|
212
|
+
processor: models.ForeignKey["EndoscopyProcessor | None"]
|
|
213
|
+
video_meta: models.OneToOneField["VideoMeta | None"]
|
|
214
|
+
examination: models.ForeignKey["PatientExamination | None"]
|
|
215
|
+
patient: models.ForeignKey["Patient | None"]
|
|
216
|
+
sensitive_meta: models.OneToOneField["SensitiveMeta | None"]
|
|
217
|
+
state: models.OneToOneField["VideoState | None"]
|
|
218
|
+
ai_model_meta: models.ForeignKey["ModelMeta | None"]
|
|
219
|
+
import_meta: models.OneToOneField["VideoImportMeta | None"]
|
|
220
|
+
raw_file = cast(FieldFile, raw_file)
|
|
221
|
+
processed_file = cast(FieldFile, processed_file)
|
|
220
222
|
|
|
221
223
|
@property
|
|
222
224
|
def ffmpeg_meta(self) -> "FFMpegMeta":
|
|
@@ -257,7 +259,10 @@ class VideoFile(models.Model):
|
|
|
257
259
|
assert _file is not None, self.NO_ACTIVE_FILE
|
|
258
260
|
if not _file or not _file.name:
|
|
259
261
|
raise ValueError(self.NO_FILE_ASSOCIATED)
|
|
260
|
-
|
|
262
|
+
url = getattr(_file, "url", None)
|
|
263
|
+
if not url:
|
|
264
|
+
raise ValueError("Active raw file URL could not be resolved.")
|
|
265
|
+
return str(url)
|
|
261
266
|
|
|
262
267
|
# Pipeline Functions
|
|
263
268
|
pipe_1 = _pipe_1
|
|
@@ -286,9 +291,7 @@ class VideoFile(models.Model):
|
|
|
286
291
|
bulk_create_frames = _bulk_create_frames
|
|
287
292
|
|
|
288
293
|
# Define new methods that call the helper functions
|
|
289
|
-
def extract_specific_frame_range(
|
|
290
|
-
self, start_frame: int, end_frame: int, overwrite: bool = False, **kwargs
|
|
291
|
-
) -> bool:
|
|
294
|
+
def extract_specific_frame_range(self, start_frame: int, end_frame: int, overwrite: bool = False, **kwargs) -> bool:
|
|
292
295
|
"""
|
|
293
296
|
Extract frames from the video within the specified frame range.
|
|
294
297
|
|
|
@@ -311,13 +314,9 @@ class VideoFile(models.Model):
|
|
|
311
314
|
|
|
312
315
|
# Log if unexpected kwargs are passed, beyond those used by the helper
|
|
313
316
|
expected_helper_kwargs = {"quality", "ext", "verbose"}
|
|
314
|
-
unexpected_kwargs = {
|
|
315
|
-
k: v for k, v in kwargs.items() if k not in expected_helper_kwargs
|
|
316
|
-
}
|
|
317
|
+
unexpected_kwargs = {k: v for k, v in kwargs.items() if k not in expected_helper_kwargs}
|
|
317
318
|
if unexpected_kwargs:
|
|
318
|
-
logger.warning(
|
|
319
|
-
f"Unexpected keyword arguments for extract_specific_frame_range, will be ignored by helper: {unexpected_kwargs}"
|
|
320
|
-
)
|
|
319
|
+
logger.warning(f"Unexpected keyword arguments for extract_specific_frame_range, will be ignored by helper: {unexpected_kwargs}")
|
|
321
320
|
|
|
322
321
|
return _extract_frame_range_helper(
|
|
323
322
|
video=self,
|
|
@@ -333,9 +332,7 @@ class VideoFile(models.Model):
|
|
|
333
332
|
"""
|
|
334
333
|
Deletes frame files for a specific range [start_frame, end_frame).
|
|
335
334
|
"""
|
|
336
|
-
_delete_frame_range_helper(
|
|
337
|
-
video=self, start_frame=start_frame, end_frame=end_frame
|
|
338
|
-
)
|
|
335
|
+
_delete_frame_range_helper(video=self, start_frame=start_frame, end_frame=end_frame)
|
|
339
336
|
|
|
340
337
|
delete_with_file = _delete_with_file
|
|
341
338
|
get_base_frame_dir = _get_base_frame_dir
|
|
@@ -390,9 +387,7 @@ class VideoFile(models.Model):
|
|
|
390
387
|
if isinstance(raw, FieldFile) and raw.name:
|
|
391
388
|
return raw
|
|
392
389
|
|
|
393
|
-
raise ValueError(
|
|
394
|
-
"No active file available. VideoFile has neither raw nor processed file."
|
|
395
|
-
)
|
|
390
|
+
raise ValueError("No active file available. VideoFile has neither raw nor processed file.")
|
|
396
391
|
|
|
397
392
|
@property
|
|
398
393
|
def active_file_path(self) -> Path:
|
|
@@ -411,18 +406,35 @@ class VideoFile(models.Model):
|
|
|
411
406
|
elif active is self.raw_file:
|
|
412
407
|
path = _get_raw_file_path(self)
|
|
413
408
|
else:
|
|
414
|
-
raise ValueError(
|
|
415
|
-
"No active file path available. VideoFile has neither raw nor processed file."
|
|
416
|
-
)
|
|
409
|
+
raise ValueError("No active file path available. VideoFile has neither raw nor processed file.")
|
|
417
410
|
|
|
418
411
|
if path is None:
|
|
419
|
-
raise ValueError("Active file path could not be resolved.")
|
|
412
|
+
raise ValueError("Active file path could not be resolved. VideoFile raw file is missing.")
|
|
420
413
|
return path
|
|
421
414
|
|
|
415
|
+
@property
|
|
416
|
+
def active_file_url(self) -> str:
|
|
417
|
+
"""Return the URL of the active video file, if available."""
|
|
418
|
+
file_obj = self.active_file
|
|
419
|
+
if not isinstance(file_obj, FieldFile):
|
|
420
|
+
raise ValueError("Active file is not a valid Django FieldFile instance.")
|
|
421
|
+
try:
|
|
422
|
+
url = getattr(file_obj, "url", None)
|
|
423
|
+
except Exception as exc: # storage backends may raise when missing
|
|
424
|
+
logger.warning(
|
|
425
|
+
"Active file URL unavailable for video %s: %s",
|
|
426
|
+
self.uuid,
|
|
427
|
+
exc,
|
|
428
|
+
)
|
|
429
|
+
raise ValueError("Active file URL could not be resolved for this VideoFile.") from exc
|
|
430
|
+
|
|
431
|
+
if not url:
|
|
432
|
+
raise ValueError("Active file URL is empty for this VideoFile.")
|
|
433
|
+
|
|
434
|
+
return str(url)
|
|
435
|
+
|
|
422
436
|
@classmethod
|
|
423
|
-
def create_from_file(
|
|
424
|
-
cls, file_path: Union[str, Path], center_name: str, **kwargs
|
|
425
|
-
) -> Optional["VideoFile"]:
|
|
437
|
+
def create_from_file(cls, file_path: Union[str, Path], center_name: str, **kwargs) -> Optional["VideoFile"]:
|
|
426
438
|
# Ensure file_path is a Path object
|
|
427
439
|
if isinstance(file_path, str):
|
|
428
440
|
file_path = Path(file_path)
|
|
@@ -431,9 +443,7 @@ class VideoFile(models.Model):
|
|
|
431
443
|
try:
|
|
432
444
|
center_name = os.environ["CENTER_NAME"]
|
|
433
445
|
except KeyError:
|
|
434
|
-
logger.error(
|
|
435
|
-
"Center name must be provided to create VideoFile from file. You can set CENTER_NAME in environment variables."
|
|
436
|
-
)
|
|
446
|
+
logger.error("Center name must be provided to create VideoFile from file. You can set CENTER_NAME in environment variables.")
|
|
437
447
|
return None
|
|
438
448
|
return _create_from_file(cls, file_path, center_name=center_name, **kwargs)
|
|
439
449
|
|
|
@@ -478,19 +488,22 @@ class VideoFile(models.Model):
|
|
|
478
488
|
_delete_frames(self)
|
|
479
489
|
|
|
480
490
|
# Call the original delete method to remove the instance from the database
|
|
481
|
-
|
|
482
|
-
|
|
491
|
+
try:
|
|
492
|
+
active_path = self.active_file_path
|
|
493
|
+
logger.info(f"Deleting VideoFile: {self.uuid} - {active_path}")
|
|
494
|
+
|
|
495
|
+
except ValueError:
|
|
496
|
+
logger.info(f"Deleting VideoFile: {self.uuid} - No active file path found.")
|
|
497
|
+
active_path = None
|
|
483
498
|
|
|
484
499
|
# Delete associated files if they exist
|
|
485
|
-
if active_path.exists():
|
|
500
|
+
if active_path and active_path.exists():
|
|
486
501
|
active_path.unlink(missing_ok=True)
|
|
487
502
|
|
|
488
503
|
# Delete file storage
|
|
489
504
|
if self.raw_file and self.raw_file.storage.exists(self.raw_file.name):
|
|
490
505
|
self.raw_file.storage.delete(self.raw_file.name)
|
|
491
|
-
if self.processed_file and self.processed_file.storage.exists(
|
|
492
|
-
self.processed_file.name
|
|
493
|
-
):
|
|
506
|
+
if self.processed_file and self.processed_file.storage.exists(self.processed_file.name):
|
|
494
507
|
self.processed_file.storage.delete(self.processed_file.name)
|
|
495
508
|
|
|
496
509
|
# Use proper database connection
|
|
@@ -517,9 +530,7 @@ class VideoFile(models.Model):
|
|
|
517
530
|
logger.error(f"Error deleting VideoFile {self.uuid}: {e}")
|
|
518
531
|
raise
|
|
519
532
|
|
|
520
|
-
def validate_metadata_annotation(
|
|
521
|
-
self, extracted_data_dict: Optional[dict] = None
|
|
522
|
-
) -> bool:
|
|
533
|
+
def validate_metadata_annotation(self, extracted_data_dict: Optional[dict] = None) -> bool:
|
|
523
534
|
"""
|
|
524
535
|
Validate the metadata of the VideoFile instance.
|
|
525
536
|
|
|
@@ -531,57 +542,44 @@ class VideoFile(models.Model):
|
|
|
531
542
|
**IMPORTANT:** Only the raw video is deleted. The processed (anonymized)
|
|
532
543
|
video is preserved as the final validated output.
|
|
533
544
|
"""
|
|
534
|
-
from datetime import date as dt_date
|
|
535
|
-
|
|
536
|
-
from endoreg_db.models import SensitiveMeta
|
|
537
545
|
|
|
538
546
|
if not self.sensitive_meta:
|
|
539
|
-
#
|
|
540
|
-
|
|
541
|
-
"patient_first_name": "Patient",
|
|
542
|
-
"patient_last_name": "Unknown",
|
|
543
|
-
"patient_dob": dt_date(1990, 1, 1),
|
|
544
|
-
"examination_date": dt_date.today(),
|
|
545
|
-
"center": self.center,
|
|
546
|
-
}
|
|
547
|
-
self.sensitive_meta = SensitiveMeta.create_from_dict(default_data)
|
|
548
|
-
|
|
547
|
+
# Ensure a SensitiveMeta exists so validation can proceed.
|
|
548
|
+
self.sensitive_meta = self.get_or_create_sensitive_meta()
|
|
549
549
|
# CRITICAL FIX: Delete RAW video file, not the processed (anonymized) one
|
|
550
550
|
# CRITICAL: Update metadata BEFORE deleting raw video
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
551
|
+
if extracted_data_dict:
|
|
552
|
+
self.sensitive_meta.update_from_dict(extracted_data_dict)
|
|
553
|
+
else:
|
|
554
|
+
return False
|
|
555
555
|
|
|
556
556
|
# After validation and metadata update, only the anonymized video should remain
|
|
557
557
|
from .video_file_io import _get_raw_file_path
|
|
558
558
|
|
|
559
559
|
raw_path = _get_raw_file_path(self)
|
|
560
|
+
|
|
560
561
|
if raw_path and raw_path.exists():
|
|
561
562
|
logger.info(f"Deleting raw video file after validation: {raw_path}")
|
|
562
563
|
raw_path.unlink(missing_ok=True)
|
|
563
564
|
# Clear the raw_file field in database (use delete() to avoid save issues)
|
|
564
565
|
if self.raw_file:
|
|
565
566
|
self.raw_file.delete(save=False)
|
|
566
|
-
logger.info(
|
|
567
|
-
f"Raw video deleted for {self.uuid}. Anonymized video preserved."
|
|
568
|
-
)
|
|
567
|
+
logger.info(f"Raw video deleted for {self.uuid}. Anonymized video preserved.")
|
|
569
568
|
else:
|
|
570
|
-
logger.warning(
|
|
569
|
+
logger.warning(
|
|
570
|
+
"Raw video file not found for deletion during validation %s.",
|
|
571
|
+
self.uuid,
|
|
572
|
+
)
|
|
571
573
|
|
|
572
|
-
if sensitive_meta:
|
|
574
|
+
if self.sensitive_meta:
|
|
573
575
|
# Mark as processed after validation
|
|
574
|
-
self.get_or_create_state().
|
|
576
|
+
self.get_or_create_state().mark_anonymization_validated(save=True)
|
|
575
577
|
# Save the VideoFile instance to persist changes
|
|
576
578
|
self.save()
|
|
577
|
-
logger.info(
|
|
578
|
-
f"Metadata annotation validated and saved for video {self.uuid}."
|
|
579
|
-
)
|
|
579
|
+
logger.info(f"Metadata annotation validated and saved for video {self.uuid}.")
|
|
580
580
|
return True
|
|
581
581
|
else:
|
|
582
|
-
logger.error(
|
|
583
|
-
f"Failed to validate metadata annotation for video {self.uuid}."
|
|
584
|
-
)
|
|
582
|
+
logger.error(f"Failed to validate metadata annotation for video {self.uuid}.")
|
|
585
583
|
return False
|
|
586
584
|
|
|
587
585
|
def initialize(self):
|
|
@@ -615,9 +613,7 @@ class VideoFile(models.Model):
|
|
|
615
613
|
"""
|
|
616
614
|
active_path = self.active_file_path
|
|
617
615
|
file_name = active_path.name if active_path else "No file"
|
|
618
|
-
state = (
|
|
619
|
-
"Processed" if self.is_processed else ("Raw" if self.has_raw else "No File")
|
|
620
|
-
)
|
|
616
|
+
state = "Processed" if self.is_processed else ("Raw" if self.has_raw else "No File")
|
|
621
617
|
return f"VideoFile ({state}): {file_name} (UUID: {self.uuid})"
|
|
622
618
|
|
|
623
619
|
# --- Convenience state/meta helpers used in tests and admin workflows ---
|
|
@@ -734,9 +730,7 @@ class VideoFile(models.Model):
|
|
|
734
730
|
# Do not mark state as processed here; it will be set after extraction/validation steps
|
|
735
731
|
return self.sensitive_meta
|
|
736
732
|
|
|
737
|
-
def get_outside_segments(
|
|
738
|
-
self, only_validated: bool = False
|
|
739
|
-
) -> models.QuerySet["LabelVideoSegment"]:
|
|
733
|
+
def get_outside_segments(self, only_validated: bool = False) -> models.QuerySet["LabelVideoSegment"]:
|
|
740
734
|
"""
|
|
741
735
|
Return all video segments labeled as "outside" for this video.
|
|
742
736
|
|
|
@@ -767,6 +761,56 @@ class VideoFile(models.Model):
|
|
|
767
761
|
)
|
|
768
762
|
return self.label_video_segments.none()
|
|
769
763
|
|
|
764
|
+
@classmethod
|
|
765
|
+
def create_video_without_outside_frames(cls, instance: "VideoFile", only_validated: bool = False) -> bool:
|
|
766
|
+
"""
|
|
767
|
+
Creates a new video by excluding frames that belong to 'outside' segments.
|
|
768
|
+
|
|
769
|
+
Parameters:
|
|
770
|
+
only_validated (bool): If True, only validated segments are considered for frame exclusion.
|
|
771
|
+
|
|
772
|
+
Returns:
|
|
773
|
+
VideoFile: A new VideoFile instance with the frames excluding those labeled as 'outside'.
|
|
774
|
+
"""
|
|
775
|
+
video = instance
|
|
776
|
+
|
|
777
|
+
if not video:
|
|
778
|
+
logger.warning("No processed video file available for VideoFile %s.", cls.uuid)
|
|
779
|
+
return False
|
|
780
|
+
try:
|
|
781
|
+
extracted = video.extract_frames(quality=2, overwrite=False, ext="jpg", verbose=False, from_processed=True)
|
|
782
|
+
assert extracted is True
|
|
783
|
+
except AssertionError:
|
|
784
|
+
# Use default anonymization here
|
|
785
|
+
video.anonymize
|
|
786
|
+
extracted = video.extract_frames(quality=2, overwrite=False, ext="jpg", verbose=False, from_processed=True)
|
|
787
|
+
assert extracted is True
|
|
788
|
+
try:
|
|
789
|
+
# Step 1: Get the "outside" labeled frames
|
|
790
|
+
censored = _censor_outside_frames(video)
|
|
791
|
+
frames = [instance.get_frame_dir_path()]
|
|
792
|
+
assert len(frames) != 0
|
|
793
|
+
fps = video.fps if video.fps else 120.0 # Default to 30 FPS if fps is not set
|
|
794
|
+
assert fps is not None
|
|
795
|
+
assert video.width is not None
|
|
796
|
+
assert video.height is not None
|
|
797
|
+
|
|
798
|
+
# Step 2: Reassemble the video with frames excluding the 'outside' labeled frames
|
|
799
|
+
output_video_path = Path(f"/path/to/output/{cls.uuid}_filtered.mp4")
|
|
800
|
+
fps = cls.fps if cls.fps else 30.0 # Default to 30 FPS if fps is not set
|
|
801
|
+
new_video_file = assemble_video_from_frames(frames, output_video_path, fps, width=video.width, height=video.height)
|
|
802
|
+
video.processed_file = new_video_file
|
|
803
|
+
return True
|
|
804
|
+
except AssertionError as ae:
|
|
805
|
+
logger.error(f"Assertion error while creating video without 'outside' frames for VideoFile {cls.uuid}: {ae}", exc_info=True)
|
|
806
|
+
return False
|
|
807
|
+
except Label.DoesNotExist:
|
|
808
|
+
logger.warning("Outside label not found in the database.")
|
|
809
|
+
return False
|
|
810
|
+
except Exception as e:
|
|
811
|
+
logger.error(f"Error creating video without 'outside' frames for VideoFile {cls.uuid}: {e}", exc_info=True)
|
|
812
|
+
return False
|
|
813
|
+
|
|
770
814
|
@classmethod
|
|
771
815
|
def get_all_videos(cls) -> models.QuerySet["VideoFile"]:
|
|
772
816
|
"""
|
|
@@ -784,9 +828,7 @@ class VideoFile(models.Model):
|
|
|
784
828
|
int: The count of VideoFile records, excluding this instance, where the modification timestamp matches the creation timestamp.
|
|
785
829
|
"""
|
|
786
830
|
return (
|
|
787
|
-
VideoFile.objects.filter(
|
|
788
|
-
date_modified=F("date_created")
|
|
789
|
-
) # compare the two fields in SQL
|
|
831
|
+
VideoFile.objects.filter(date_modified=F("date_created")) # compare the two fields in SQL
|
|
790
832
|
.exclude(pk=self.pk) # exclude this instance
|
|
791
833
|
.count() # run a fast COUNT(*) on the filtered set
|
|
792
834
|
)
|