endoreg-db 0.8.8.0__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/data/__init__.py +22 -8
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +0 -1
- 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/{colonoscopy_bowel_preparation.yaml → 02_colonoscopy_baseline.yaml} +35 -20
- 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/{_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml → finding_classification_choice/02_colonoscopy_generic.yaml} +1 -1
- endoreg_db/data/finding_classification_choice/{histology_polyp.yaml → 02_colonoscopy_histology.yaml} +1 -1
- endoreg_db/data/{_examples/finding_classification_choice/colonoscopy_location.yaml → finding_classification_choice/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/{_examples/finding_classification_choice/colon_lesion_paris.yaml → finding_classification_choice/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_operator/new_operators.yaml +36 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +0 -2
- endoreg_db/data/requirement_set/90_coloreg.yaml +20 -8
- endoreg_db/exceptions.py +0 -1
- endoreg_db/forms/examination_form.py +1 -1
- endoreg_db/helpers/data_loader.py +124 -52
- 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/import_report.py +130 -65
- endoreg_db/management/commands/import_video_with_classification.py +1 -1
- 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_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 +14 -20
- 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/start_filewatcher.py +46 -37
- endoreg_db/management/commands/validate_video_files.py +1 -5
- endoreg_db/migrations/0001_initial.py +1360 -1812
- endoreg_db/models/administration/person/patient/patient.py +72 -46
- endoreg_db/models/label/__init__.py +2 -2
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +18 -26
- endoreg_db/models/label/label_video_segment/label_video_segment.py +23 -1
- endoreg_db/models/media/pdf/raw_pdf.py +136 -64
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +34 -10
- 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/create_from_file.py +101 -31
- endoreg_db/models/media/video/video_file.py +125 -105
- endoreg_db/models/media/video/video_file_io.py +31 -26
- endoreg_db/models/medical/contraindication/README.md +1 -0
- endoreg_db/models/medical/examination/examination.py +28 -8
- endoreg_db/models/medical/examination/examination_indication.py +13 -79
- endoreg_db/models/medical/examination/examination_time.py +8 -3
- endoreg_db/models/medical/finding/finding.py +5 -12
- endoreg_db/models/medical/finding/finding_classification.py +18 -37
- endoreg_db/models/medical/finding/finding_intervention.py +7 -9
- endoreg_db/models/medical/hardware/endoscope.py +6 -0
- endoreg_db/models/medical/patient/medication_examples.py +5 -1
- endoreg_db/models/medical/patient/patient_finding.py +1 -1
- endoreg_db/models/metadata/pdf_meta.py +22 -10
- endoreg_db/models/metadata/sensitive_meta.py +3 -0
- endoreg_db/models/metadata/sensitive_meta_logic.py +200 -124
- endoreg_db/models/other/information_source.py +27 -6
- endoreg_db/models/report/__init__.py +0 -0
- endoreg_db/models/report/images.py +0 -0
- endoreg_db/models/report/report.py +6 -0
- endoreg_db/models/requirement/requirement.py +59 -399
- endoreg_db/models/requirement/requirement_operator.py +86 -98
- endoreg_db/models/state/audit_ledger.py +4 -5
- endoreg_db/models/state/raw_pdf.py +69 -30
- endoreg_db/models/state/video.py +64 -49
- endoreg_db/models/upload_job.py +33 -9
- endoreg_db/models/utils.py +27 -23
- endoreg_db/queries/__init__.py +3 -1
- endoreg_db/schemas/examination_evaluation.py +1 -1
- endoreg_db/serializers/__init__.py +2 -8
- endoreg_db/serializers/label_video_segment/label_video_segment.py +2 -29
- endoreg_db/serializers/meta/__init__.py +1 -6
- 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/video/video_file_list.py +65 -34
- endoreg_db/services/__old/pdf_import.py +1487 -0
- endoreg_db/services/__old/video_import.py +1306 -0
- endoreg_db/services/anonymization.py +63 -26
- endoreg_db/services/lookup_service.py +28 -28
- endoreg_db/services/lookup_store.py +2 -2
- endoreg_db/services/pdf_import.py +0 -1480
- endoreg_db/services/report_import.py +10 -0
- endoreg_db/services/video_import.py +6 -1165
- endoreg_db/tasks/upload_tasks.py +79 -70
- endoreg_db/tasks/video_ingest.py +8 -4
- endoreg_db/urls/__init__.py +0 -14
- endoreg_db/urls/ai.py +32 -0
- endoreg_db/urls/media.py +21 -24
- endoreg_db/utils/dataloader.py +87 -57
- endoreg_db/utils/paths.py +110 -46
- endoreg_db/utils/pipelines/Readme.md +1 -1
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +97 -0
- endoreg_db/views/__init__.py +85 -173
- endoreg_db/views/ai/__init__.py +8 -0
- endoreg_db/views/ai/label.py +155 -0
- endoreg_db/views/anonymization/media_management.py +8 -7
- endoreg_db/views/anonymization/overview.py +97 -68
- endoreg_db/views/anonymization/validate.py +25 -21
- endoreg_db/views/media/__init__.py +5 -20
- endoreg_db/views/media/pdf_media.py +109 -65
- endoreg_db/views/media/sensitive_metadata.py +163 -148
- 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/report/__init__.py +7 -0
- endoreg_db/views/{pdf → report}/reimport.py +45 -24
- endoreg_db/views/{pdf/pdf_stream.py → report/report_stream.py} +40 -32
- endoreg_db/views/requirement/lookup_store.py +22 -90
- endoreg_db/views/video/__init__.py +23 -22
- 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} +75 -37
- 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.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/METADATA +2 -2
- {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/RECORD +217 -335
- {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/WHEEL +1 -1
- endoreg_db/data/_examples/disease.yaml +0 -55
- endoreg_db/data/_examples/disease_classification.yaml +0 -13
- endoreg_db/data/_examples/disease_classification_choice.yaml +0 -62
- endoreg_db/data/_examples/event.yaml +0 -64
- endoreg_db/data/_examples/examination.yaml +0 -72
- endoreg_db/data/_examples/finding/anatomy_colon.yaml +0 -128
- endoreg_db/data/_examples/finding/colonoscopy.yaml +0 -40
- endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +0 -56
- endoreg_db/data/_examples/finding/complication.yaml +0 -16
- endoreg_db/data/_examples/finding/data.yaml +0 -105
- endoreg_db/data/_examples/finding/examination_setting.yaml +0 -16
- endoreg_db/data/_examples/finding/medication_related.yaml +0 -18
- endoreg_db/data/_examples/finding/outcome.yaml +0 -12
- endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +0 -68
- endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +0 -22
- endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +0 -25
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +0 -20
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +0 -24
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +0 -68
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
- endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +0 -80
- endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +0 -21
- endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +0 -20
- endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +0 -26
- endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +0 -22
- endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +0 -53
- endoreg_db/data/_examples/finding_classification/complication_generic.yaml +0 -25
- endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +0 -40
- endoreg_db/data/_examples/finding_classification/histology_colo.yaml +0 -51
- endoreg_db/data/_examples/finding_classification/intervention_required.yaml +0 -26
- endoreg_db/data/_examples/finding_classification/medication_related.yaml +0 -23
- endoreg_db/data/_examples/finding_classification/visualized.yaml +0 -33
- endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +0 -78
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +0 -32
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +0 -23
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +0 -17
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +0 -49
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +0 -14
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +0 -36
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +0 -82
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +0 -15
- endoreg_db/data/_examples/finding_classification_choice/histology.yaml +0 -24
- endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +0 -20
- endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +0 -19
- endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +0 -11
- endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +0 -48
- endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +0 -43
- endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +0 -168
- endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +0 -128
- endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +0 -32
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +0 -9
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +0 -36
- endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +0 -15
- endoreg_db/data/_examples/finding_type/data.yaml +0 -43
- endoreg_db/data/_examples/requirement/age.yaml +0 -26
- endoreg_db/data/_examples/requirement/gender.yaml +0 -25
- endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +0 -48
- endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +0 -57
- endoreg_db/data/_examples/requirement_set/endoscopy_bleeding_risk.yaml +0 -52
- endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
- 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_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 -38
- endoreg_db/data/finding_classification/colonoscopy_lesion_surface.yaml +0 -20
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +0 -49
- 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 -43
- 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_paris.yaml +0 -57
- 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_location.yaml +0 -229
- endoreg_db/data/finding_classification_choice/colonoscopy_not_complete_reason.yaml +0 -19
- 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/colonoscopy_baseline_austria.yaml +0 -45
- endoreg_db/data/requirement/disease_cardiovascular.yaml +0 -79
- endoreg_db/data/requirement/disease_classification_choice_cardiovascular.yaml +0 -41
- endoreg_db/data/requirement/disease_hepatology.yaml +0 -12
- endoreg_db/data/requirement/disease_misc.yaml +0 -12
- endoreg_db/data/requirement/disease_renal.yaml +0 -96
- endoreg_db/data/requirement/endoscopy_bleeding_risk.yaml +0 -59
- endoreg_db/data/requirement/event_cardiology.yaml +0 -251
- endoreg_db/data/requirement/event_requirements.yaml +0 -145
- endoreg_db/data/requirement/finding_colon_polyp.yaml +0 -50
- endoreg_db/data/requirement/gender.yaml +0 -25
- endoreg_db/data/requirement/lab_value.yaml +0 -441
- endoreg_db/data/requirement/medication.yaml +0 -93
- endoreg_db/data/requirement_operator/age.yaml +0 -13
- endoreg_db/data/requirement_operator/lab_operators.yaml +0 -129
- endoreg_db/data/requirement_operator/model_operators.yaml +0 -96
- 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_requirementset_depends_on.py +0 -18
- endoreg_db/migrations/_old/0001_initial.py +0 -1857
- endoreg_db/migrations/_old/0002_add_video_correction_models.py +0 -52
- endoreg_db/migrations/_old/0003_add_center_display_name.py +0 -30
- endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +0 -68
- endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +0 -77
- endoreg_db/migrations/_old/0005_merge_20251111_1003.py +0 -14
- endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +0 -68
- endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +0 -89
- endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +0 -27
- endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +0 -21
- 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/services/requirements_object.py +0 -147
- endoreg_db/services/storage_aware_video_processor.py +0 -370
- 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/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 -85
- 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/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/data/requirement/{colon_polyp_intervention.yaml → old/colon_polyp_intervention.yaml} +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/colonoscopy_baseline_austria.yaml +0 -0
- /endoreg_db/data/requirement/{coloreg_colon_polyp.yaml → old/coloreg_colon_polyp.yaml} +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_cardiovascular.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_classification_choice_cardiovascular.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_hepatology.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_misc.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/disease_renal.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/endoscopy_bleeding_risk.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/event_cardiology.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/event_requirements.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/finding_colon_polyp.yaml +0 -0
- /endoreg_db/{migrations/__init__.py → data/requirement/old/gender.yaml} +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/lab_value.yaml +0 -0
- /endoreg_db/data/{_examples/requirement → requirement/old}/medication.yaml +0 -0
- /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/age.yaml +0 -0
- /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/lab_operators.yaml +0 -0
- /endoreg_db/data/{_examples/requirement_operator → requirement_operator/_old}/model_operators.yaml +0 -0
- /endoreg_db/{urls/sensitive_meta.py → import_files/pseudonymization/__init__.py} +0 -0
- /endoreg_db/{views/pdf/pdf_stream_views.py → import_files/pseudonymization/pseudonymize.py} +0 -0
- /endoreg_db/utils/requirement_operator_logic/{lab_value_operators.py → _old/lab_value_operators.py} +0 -0
- /endoreg_db/utils/requirement_operator_logic/{model_evaluators.py → _old/model_evaluators.py} +0 -0
- {endoreg_db-0.8.8.0.dist-info → endoreg_db-0.8.8.9.dist-info}/licenses/LICENSE +0 -0
|
@@ -11,30 +11,35 @@ Available Functions from lx_anonymizer (already implemented):
|
|
|
11
11
|
- FrameCleaner.remove_frames_from_video() - Frame removal
|
|
12
12
|
- VideoImportService._get_processor_roi_info() - ROI extraction
|
|
13
13
|
"""
|
|
14
|
-
|
|
15
|
-
from rest_framework.response import Response
|
|
16
|
-
from rest_framework import status
|
|
17
|
-
from django.shortcuts import get_object_or_404
|
|
18
|
-
from django.conf import settings
|
|
19
|
-
from pathlib import Path
|
|
14
|
+
|
|
20
15
|
import json
|
|
21
16
|
import logging
|
|
17
|
+
from pathlib import Path
|
|
22
18
|
|
|
23
|
-
from
|
|
24
|
-
from
|
|
19
|
+
from django.conf import settings
|
|
20
|
+
from django.shortcuts import get_object_or_404
|
|
25
21
|
from lx_anonymizer import FrameCleaner
|
|
22
|
+
from rest_framework import status
|
|
23
|
+
from rest_framework.response import Response
|
|
24
|
+
from rest_framework.views import APIView
|
|
26
25
|
|
|
27
|
-
from endoreg_db.models import
|
|
26
|
+
from endoreg_db.models import (
|
|
27
|
+
LabelVideoSegment,
|
|
28
|
+
VideoFile,
|
|
29
|
+
VideoMetadata,
|
|
30
|
+
VideoProcessingHistory,
|
|
31
|
+
)
|
|
32
|
+
from endoreg_db.serializers import VideoMetaSerializer, VideoProcessingHistorySerializer
|
|
28
33
|
from endoreg_db.serializers.video.video_file_detail import VideoDetailSerializer
|
|
34
|
+
from endoreg_db.utils.paths import ANONYM_VIDEO_DIR, IMPORT_VIDEO_DIR
|
|
29
35
|
from endoreg_db.utils.permissions import EnvironmentAwarePermission
|
|
30
36
|
|
|
31
37
|
logger = logging.getLogger(__name__)
|
|
32
38
|
|
|
39
|
+
|
|
33
40
|
def update_processed_file(video, output_path: Path):
|
|
34
|
-
from endoreg_db.utils import data_paths
|
|
35
|
-
storage_root = Path(data_paths["storage"])
|
|
36
41
|
try:
|
|
37
|
-
rel_path = output_path.relative_to(
|
|
42
|
+
rel_path = output_path.relative_to(ANONYM_VIDEO_DIR)
|
|
38
43
|
except ValueError:
|
|
39
44
|
rel_path = output_path.relative_to(Path(settings.MEDIA_ROOT))
|
|
40
45
|
video.processed_file.name = str(rel_path)
|
|
@@ -45,6 +50,7 @@ class VideoCorrectionView(APIView):
|
|
|
45
50
|
"""
|
|
46
51
|
GET /api/video/media/video-correction/{id}/ - Get video details for correction
|
|
47
52
|
"""
|
|
53
|
+
|
|
48
54
|
permission_classes = [EnvironmentAwarePermission]
|
|
49
55
|
|
|
50
56
|
def get(self, request, pk):
|
|
@@ -52,65 +58,68 @@ class VideoCorrectionView(APIView):
|
|
|
52
58
|
ser = VideoDetailSerializer(video, context={"request": request})
|
|
53
59
|
return Response(ser.data, status=status.HTTP_200_OK)
|
|
54
60
|
|
|
61
|
+
|
|
55
62
|
def update_segments_after_frame_removal(video: VideoFile, removed_frames: list) -> dict:
|
|
56
63
|
"""
|
|
57
64
|
Update LabelVideoSegment frame boundaries after frame removal.
|
|
58
|
-
|
|
65
|
+
|
|
59
66
|
This function shifts segment start/end frames based on which frames were removed.
|
|
60
67
|
Segments are deleted if all their frames are removed.
|
|
61
|
-
|
|
68
|
+
|
|
62
69
|
Args:
|
|
63
70
|
video: VideoFile instance whose segments should be updated
|
|
64
71
|
removed_frames: List of frame numbers that were removed (sorted)
|
|
65
|
-
|
|
72
|
+
|
|
66
73
|
Returns:
|
|
67
74
|
dict: {
|
|
68
75
|
'segments_updated': int,
|
|
69
76
|
'segments_deleted': int,
|
|
70
77
|
'segments_unchanged': int
|
|
71
78
|
}
|
|
72
|
-
|
|
79
|
+
|
|
73
80
|
Algorithm:
|
|
74
81
|
For each segment:
|
|
75
82
|
1. Count frames removed before segment → shift start_frame
|
|
76
83
|
2. Count frames removed within segment → shift end_frame
|
|
77
84
|
3. Delete segment if start_frame >= end_frame (all frames removed)
|
|
78
|
-
|
|
85
|
+
|
|
79
86
|
Example:
|
|
80
87
|
Original segment: frames 100-200
|
|
81
88
|
Removed frames: [50, 75, 120, 150, 180]
|
|
82
|
-
|
|
89
|
+
|
|
83
90
|
Frames before segment (< 100): 2 frames (50, 75)
|
|
84
91
|
Frames within segment (100-200): 3 frames (120, 150, 180)
|
|
85
|
-
|
|
92
|
+
|
|
86
93
|
New segment: frames (100-2) to (200-2-3) = 98-195
|
|
87
94
|
"""
|
|
88
95
|
if not removed_frames:
|
|
89
|
-
return {
|
|
90
|
-
|
|
96
|
+
return {"segments_updated": 0, "segments_deleted": 0, "segments_unchanged": 0}
|
|
97
|
+
|
|
91
98
|
removed_frames = sorted(set(removed_frames)) # Ensure sorted and unique
|
|
92
|
-
segments = LabelVideoSegment.objects.filter(
|
|
93
|
-
|
|
94
|
-
)
|
|
95
|
-
|
|
99
|
+
segments = LabelVideoSegment.objects.filter(video_file=video).order_by(
|
|
100
|
+
"start_frame_number"
|
|
101
|
+
)
|
|
102
|
+
|
|
96
103
|
segments_updated = 0
|
|
97
104
|
segments_deleted = 0
|
|
98
105
|
segments_unchanged = 0
|
|
99
|
-
|
|
106
|
+
|
|
100
107
|
for segment in segments:
|
|
101
108
|
original_start = segment.start_frame_number
|
|
102
109
|
original_end = segment.end_frame_number
|
|
103
|
-
|
|
110
|
+
|
|
104
111
|
# Count frames removed before this segment
|
|
105
112
|
frames_before = sum(1 for f in removed_frames if f < original_start)
|
|
106
|
-
|
|
113
|
+
|
|
107
114
|
# Count frames removed within this segment
|
|
108
|
-
frames_within = sum(
|
|
109
|
-
|
|
115
|
+
frames_within = sum(
|
|
116
|
+
1 for f in removed_frames if original_start <= f <= original_end
|
|
117
|
+
)
|
|
118
|
+
|
|
110
119
|
# Calculate new boundaries
|
|
111
120
|
new_start = original_start - frames_before
|
|
112
121
|
new_end = original_end - frames_before - frames_within
|
|
113
|
-
|
|
122
|
+
|
|
114
123
|
# Delete segment if all frames were removed
|
|
115
124
|
if new_start >= new_end:
|
|
116
125
|
logger.info(
|
|
@@ -133,25 +142,25 @@ def update_segments_after_frame_removal(video: VideoFile, removed_frames: list)
|
|
|
133
142
|
else:
|
|
134
143
|
# No change needed
|
|
135
144
|
segments_unchanged += 1
|
|
136
|
-
|
|
145
|
+
|
|
137
146
|
logger.info(
|
|
138
147
|
f"Segment update complete for video {video.id}: "
|
|
139
148
|
f"{segments_updated} updated, {segments_deleted} deleted, {segments_unchanged} unchanged"
|
|
140
149
|
)
|
|
141
|
-
|
|
150
|
+
|
|
142
151
|
return {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
152
|
+
"segments_updated": segments_updated,
|
|
153
|
+
"segments_deleted": segments_deleted,
|
|
154
|
+
"segments_unchanged": segments_unchanged,
|
|
146
155
|
}
|
|
147
156
|
|
|
148
157
|
|
|
149
|
-
class
|
|
158
|
+
class VideoMetadataStatsView(APIView):
|
|
150
159
|
"""
|
|
151
160
|
GET /api/media/videos/{pk}/metadata/
|
|
152
|
-
|
|
161
|
+
|
|
153
162
|
Retrieve analysis results for a video.
|
|
154
|
-
|
|
163
|
+
|
|
155
164
|
Returns:
|
|
156
165
|
{
|
|
157
166
|
"id": 1,
|
|
@@ -165,24 +174,24 @@ class VideoMetadataView(APIView):
|
|
|
165
174
|
"analyzed_at": "2025-10-09T10:00:00Z"
|
|
166
175
|
}
|
|
167
176
|
"""
|
|
168
|
-
|
|
177
|
+
|
|
169
178
|
def get(self, request, pk):
|
|
170
179
|
"""Get video metadata by video ID."""
|
|
171
180
|
video = get_object_or_404(VideoFile, pk=pk)
|
|
172
|
-
|
|
181
|
+
|
|
173
182
|
# Get or create metadata record
|
|
174
183
|
metadata, created = VideoMetadata.objects.get_or_create(video=video)
|
|
175
|
-
|
|
176
|
-
serializer = VideoMetaSerializer(metadata, context={
|
|
184
|
+
|
|
185
|
+
serializer = VideoMetaSerializer(metadata, context={"request": request})
|
|
177
186
|
return Response(serializer.data)
|
|
178
187
|
|
|
179
188
|
|
|
180
189
|
class VideoProcessingHistoryView(APIView):
|
|
181
190
|
"""
|
|
182
191
|
GET /api/media/videos/{pk}/processing-history/
|
|
183
|
-
|
|
192
|
+
|
|
184
193
|
Retrieve processing history for a video.
|
|
185
|
-
|
|
194
|
+
|
|
186
195
|
Returns list of processing operations:
|
|
187
196
|
[
|
|
188
197
|
{
|
|
@@ -205,20 +214,18 @@ class VideoProcessingHistoryView(APIView):
|
|
|
205
214
|
...
|
|
206
215
|
]
|
|
207
216
|
"""
|
|
208
|
-
|
|
217
|
+
|
|
209
218
|
def get(self, request, pk):
|
|
210
219
|
"""Get processing history for a video."""
|
|
211
220
|
video = get_object_or_404(VideoFile, pk=pk)
|
|
212
|
-
|
|
221
|
+
|
|
213
222
|
# Get all history records, newest first
|
|
214
|
-
history = VideoProcessingHistory.objects.filter(
|
|
215
|
-
|
|
216
|
-
)
|
|
217
|
-
|
|
223
|
+
history = VideoProcessingHistory.objects.filter(video=video).order_by(
|
|
224
|
+
"-created_at"
|
|
225
|
+
)
|
|
226
|
+
|
|
218
227
|
serializer = VideoProcessingHistorySerializer(
|
|
219
|
-
history,
|
|
220
|
-
many=True,
|
|
221
|
-
context={'request': request}
|
|
228
|
+
history, many=True, context={"request": request}
|
|
222
229
|
)
|
|
223
230
|
return Response(serializer.data)
|
|
224
231
|
|
|
@@ -226,9 +233,9 @@ class VideoProcessingHistoryView(APIView):
|
|
|
226
233
|
class VideoApplyMaskView(APIView):
|
|
227
234
|
"""
|
|
228
235
|
POST /api/media/videos/{pk}/apply-mask/
|
|
229
|
-
|
|
236
|
+
|
|
230
237
|
Apply device mask or custom ROI mask to video.
|
|
231
|
-
|
|
238
|
+
|
|
232
239
|
Request body:
|
|
233
240
|
{
|
|
234
241
|
"mask_type": "device", // or "custom"
|
|
@@ -241,7 +248,7 @@ class VideoApplyMaskView(APIView):
|
|
|
241
248
|
},
|
|
242
249
|
"processing_method": "streaming" // or "direct" (default: streaming)
|
|
243
250
|
}
|
|
244
|
-
|
|
251
|
+
|
|
245
252
|
Returns:
|
|
246
253
|
{
|
|
247
254
|
"task_id": "abc-123-def", // Celery task ID (future)
|
|
@@ -249,59 +256,63 @@ class VideoApplyMaskView(APIView):
|
|
|
249
256
|
"message": "Masking complete",
|
|
250
257
|
"processing_time": 125.5
|
|
251
258
|
}
|
|
252
|
-
|
|
259
|
+
|
|
253
260
|
Note: Currently synchronous. Will be converted to Celery task in Phase 1.2.
|
|
254
261
|
"""
|
|
255
|
-
|
|
262
|
+
|
|
256
263
|
def post(self, request, pk):
|
|
257
264
|
"""Apply masking to video."""
|
|
258
265
|
video = get_object_or_404(VideoFile, pk=pk)
|
|
259
|
-
|
|
266
|
+
|
|
260
267
|
# Extract parameters
|
|
261
|
-
mask_type = request.data.get(
|
|
262
|
-
device_name = request.data.get(
|
|
263
|
-
roi = request.data.get(
|
|
264
|
-
processing_method = request.data.get(
|
|
265
|
-
|
|
268
|
+
mask_type = request.data.get("mask_type", "device")
|
|
269
|
+
device_name = request.data.get("device_name")
|
|
270
|
+
roi = request.data.get("roi")
|
|
271
|
+
processing_method = request.data.get("processing_method", "streaming")
|
|
272
|
+
|
|
266
273
|
# Validate required parameters
|
|
267
|
-
if mask_type ==
|
|
274
|
+
if mask_type == "device" and not device_name:
|
|
268
275
|
return Response(
|
|
269
|
-
{
|
|
270
|
-
status=status.HTTP_400_BAD_REQUEST
|
|
276
|
+
{"error": "device_name required for device mask"},
|
|
277
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
271
278
|
)
|
|
272
|
-
|
|
273
|
-
if mask_type ==
|
|
279
|
+
|
|
280
|
+
if mask_type == "custom" and not roi:
|
|
274
281
|
return Response(
|
|
275
|
-
{
|
|
276
|
-
status=status.HTTP_400_BAD_REQUEST
|
|
282
|
+
{"error": "roi required for custom mask"},
|
|
283
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
277
284
|
)
|
|
278
|
-
|
|
285
|
+
|
|
279
286
|
# Create processing history record
|
|
280
287
|
history = VideoProcessingHistory.objects.create(
|
|
281
288
|
video=video,
|
|
282
289
|
operation=VideoProcessingHistory.OPERATION_MASKING,
|
|
283
290
|
status=VideoProcessingHistory.STATUS_PENDING,
|
|
284
291
|
config={
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
}
|
|
292
|
+
"mask_type": mask_type,
|
|
293
|
+
"device_name": device_name,
|
|
294
|
+
"roi": roi,
|
|
295
|
+
"processing_method": processing_method,
|
|
296
|
+
},
|
|
290
297
|
)
|
|
291
|
-
|
|
298
|
+
|
|
292
299
|
try:
|
|
293
300
|
history.mark_running()
|
|
294
|
-
|
|
301
|
+
|
|
295
302
|
# Initialize FrameCleaner
|
|
296
303
|
frame_cleaner = FrameCleaner()
|
|
297
|
-
|
|
304
|
+
|
|
298
305
|
# Get video paths
|
|
299
|
-
video_path =
|
|
300
|
-
|
|
306
|
+
video_path = (
|
|
307
|
+
Path(video.raw_file.path)
|
|
308
|
+
if hasattr(video.raw_file, "path")
|
|
309
|
+
else Path(str(video.raw_file))
|
|
310
|
+
)
|
|
311
|
+
output_path = video.get_output_path()
|
|
301
312
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
302
|
-
|
|
313
|
+
|
|
303
314
|
# Load or create mask config
|
|
304
|
-
if mask_type ==
|
|
315
|
+
if mask_type == "device":
|
|
305
316
|
# Load device-specific mask from lx_anonymizer/masks/
|
|
306
317
|
mask_config = frame_cleaner._load_mask(device_name)
|
|
307
318
|
else: # custom
|
|
@@ -309,58 +320,62 @@ class VideoApplyMaskView(APIView):
|
|
|
309
320
|
mask_config = frame_cleaner._create_mask_config_from_roi(
|
|
310
321
|
endoscope_roi=roi,
|
|
311
322
|
)
|
|
312
|
-
|
|
323
|
+
|
|
313
324
|
# Apply mask (uses existing FrameCleaner._mask_video)
|
|
314
325
|
import time
|
|
326
|
+
|
|
315
327
|
start_time = time.time()
|
|
316
|
-
|
|
328
|
+
|
|
317
329
|
success = frame_cleaner._mask_video(
|
|
318
330
|
input_video=video_path,
|
|
319
331
|
mask_config=mask_config,
|
|
320
|
-
output_video=output_path
|
|
332
|
+
output_video=output_path,
|
|
321
333
|
)
|
|
322
|
-
|
|
334
|
+
|
|
323
335
|
processing_time = time.time() - start_time
|
|
324
|
-
|
|
336
|
+
|
|
325
337
|
if success:
|
|
326
338
|
# Update video record with anonymized file
|
|
327
339
|
from django.core.files import File
|
|
340
|
+
|
|
328
341
|
processed_file_path = output_path
|
|
329
342
|
update_processed_file(video, processed_file_path)
|
|
330
343
|
# Mark history as success
|
|
331
344
|
history.mark_success(
|
|
332
345
|
output_file=str(output_path),
|
|
333
|
-
details=f"Masking completed in {processing_time:.1f}s"
|
|
346
|
+
details=f"Masking completed in {processing_time:.1f}s",
|
|
334
347
|
)
|
|
335
|
-
|
|
348
|
+
|
|
336
349
|
logger.info(f"Video {pk} masked successfully: {output_path}")
|
|
337
|
-
|
|
338
|
-
return Response(
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
350
|
+
|
|
351
|
+
return Response(
|
|
352
|
+
{
|
|
353
|
+
"task_id": None, # Will be Celery task ID in Phase 1.2
|
|
354
|
+
"output_file": str(output_path),
|
|
355
|
+
"message": "Masking complete",
|
|
356
|
+
"processing_time": processing_time,
|
|
357
|
+
}
|
|
358
|
+
)
|
|
344
359
|
else:
|
|
345
360
|
raise Exception("Masking failed - check FFmpeg logs")
|
|
346
|
-
|
|
361
|
+
|
|
347
362
|
except Exception as e:
|
|
348
363
|
logger.error(f"Video masking failed for {pk}: {str(e)}", exc_info=True)
|
|
349
|
-
|
|
364
|
+
|
|
350
365
|
history.mark_failure(str(e))
|
|
351
|
-
|
|
366
|
+
|
|
352
367
|
return Response(
|
|
353
|
-
{
|
|
354
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
368
|
+
{"error": f"Masking failed: {str(e)}"},
|
|
369
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
355
370
|
)
|
|
356
371
|
|
|
357
372
|
|
|
358
373
|
class VideoRemoveFramesView(APIView):
|
|
359
374
|
"""
|
|
360
375
|
POST /api/media/videos/{pk}/remove-frames/
|
|
361
|
-
|
|
376
|
+
|
|
362
377
|
Remove specified frames from video.
|
|
363
|
-
|
|
378
|
+
|
|
364
379
|
Request body:
|
|
365
380
|
{
|
|
366
381
|
"frame_list": [10, 15, 20, 30], // explicit frame numbers
|
|
@@ -368,10 +383,10 @@ class VideoRemoveFramesView(APIView):
|
|
|
368
383
|
"frame_ranges": "10-20,30,45-50", // range string
|
|
369
384
|
// OR
|
|
370
385
|
"detection_method": "automatic", // use analysis results
|
|
371
|
-
|
|
386
|
+
|
|
372
387
|
"processing_method": "streaming" // or "traditional"
|
|
373
388
|
}
|
|
374
|
-
|
|
389
|
+
|
|
375
390
|
Returns:
|
|
376
391
|
{
|
|
377
392
|
"task_id": "abc-123-def",
|
|
@@ -380,28 +395,28 @@ class VideoRemoveFramesView(APIView):
|
|
|
380
395
|
"message": "Frame removal complete",
|
|
381
396
|
"processing_time": 180.3
|
|
382
397
|
}
|
|
383
|
-
|
|
398
|
+
|
|
384
399
|
Note: Currently synchronous. Will be converted to Celery task in Phase 1.2.
|
|
385
400
|
"""
|
|
386
|
-
|
|
401
|
+
|
|
387
402
|
def post(self, request, pk):
|
|
388
403
|
"""Remove frames from video."""
|
|
389
404
|
video = get_object_or_404(VideoFile, pk=pk)
|
|
390
|
-
|
|
405
|
+
|
|
391
406
|
# Extract parameters
|
|
392
|
-
frame_list = request.data.get(
|
|
393
|
-
frame_ranges = request.data.get(
|
|
394
|
-
detection_method = request.data.get(
|
|
395
|
-
processing_method = request.data.get(
|
|
396
|
-
|
|
407
|
+
frame_list = request.data.get("frame_list")
|
|
408
|
+
frame_ranges = request.data.get("frame_ranges")
|
|
409
|
+
detection_method = request.data.get("detection_method")
|
|
410
|
+
processing_method = request.data.get("processing_method", "streaming")
|
|
411
|
+
|
|
397
412
|
# Determine frames to remove
|
|
398
413
|
frames_to_remove = []
|
|
399
|
-
|
|
414
|
+
|
|
400
415
|
if frame_list:
|
|
401
416
|
frames_to_remove = frame_list
|
|
402
417
|
elif frame_ranges:
|
|
403
418
|
frames_to_remove = self._parse_frame_ranges(frame_ranges)
|
|
404
|
-
elif detection_method ==
|
|
419
|
+
elif detection_method == "automatic":
|
|
405
420
|
# Use existing analysis results
|
|
406
421
|
try:
|
|
407
422
|
metadata = VideoMetadata.objects.get(video=video)
|
|
@@ -409,122 +424,136 @@ class VideoRemoveFramesView(APIView):
|
|
|
409
424
|
frames_to_remove = json.loads(metadata.sensitive_frame_ids)
|
|
410
425
|
else:
|
|
411
426
|
return Response(
|
|
412
|
-
{
|
|
413
|
-
status=status.HTTP_400_BAD_REQUEST
|
|
427
|
+
{"error": "No analysis results available. Run analysis first."},
|
|
428
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
414
429
|
)
|
|
415
430
|
except VideoMetadata.DoesNotExist:
|
|
416
431
|
return Response(
|
|
417
|
-
{
|
|
418
|
-
status=status.HTTP_400_BAD_REQUEST
|
|
432
|
+
{"error": "Video not analyzed. Run analysis first."},
|
|
433
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
419
434
|
)
|
|
420
435
|
else:
|
|
421
436
|
return Response(
|
|
422
|
-
{
|
|
423
|
-
|
|
437
|
+
{
|
|
438
|
+
"error": "Must provide frame_list, frame_ranges, or detection_method=automatic"
|
|
439
|
+
},
|
|
440
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
424
441
|
)
|
|
425
|
-
|
|
442
|
+
|
|
426
443
|
if not frames_to_remove:
|
|
427
444
|
return Response(
|
|
428
|
-
{
|
|
429
|
-
status=status.HTTP_400_BAD_REQUEST
|
|
445
|
+
{"error": "No frames to remove"}, status=status.HTTP_400_BAD_REQUEST
|
|
430
446
|
)
|
|
431
|
-
|
|
447
|
+
|
|
432
448
|
# Create processing history record
|
|
433
449
|
history = VideoProcessingHistory.objects.create(
|
|
434
450
|
video=video,
|
|
435
451
|
operation=VideoProcessingHistory.OPERATION_FRAME_REMOVAL,
|
|
436
452
|
status=VideoProcessingHistory.STATUS_PENDING,
|
|
437
453
|
config={
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
}
|
|
454
|
+
"frames_to_remove": frames_to_remove,
|
|
455
|
+
"frame_count": len(frames_to_remove),
|
|
456
|
+
"processing_method": processing_method,
|
|
457
|
+
},
|
|
442
458
|
)
|
|
443
|
-
|
|
459
|
+
|
|
444
460
|
try:
|
|
445
461
|
history.mark_running()
|
|
446
|
-
|
|
462
|
+
|
|
447
463
|
# Initialize FrameCleaner
|
|
448
464
|
frame_cleaner = FrameCleaner()
|
|
449
|
-
|
|
465
|
+
|
|
450
466
|
# Get video paths
|
|
451
|
-
video_path =
|
|
452
|
-
|
|
467
|
+
video_path = (
|
|
468
|
+
Path(video.raw_file.path)
|
|
469
|
+
if hasattr(video.raw_file, "path")
|
|
470
|
+
else Path(str(video.raw_file))
|
|
471
|
+
)
|
|
472
|
+
output_path = (
|
|
473
|
+
Path(settings.MEDIA_ROOT)
|
|
474
|
+
/ "anonym_videos"
|
|
475
|
+
/ f"{video.uuid}_cleaned.mp4"
|
|
476
|
+
)
|
|
453
477
|
output_path.parent.mkdir(parents=True, exist_ok=True)
|
|
454
|
-
|
|
478
|
+
|
|
455
479
|
# Remove frames (uses existing FrameCleaner.remove_frames_from_video)
|
|
456
480
|
import time
|
|
481
|
+
|
|
457
482
|
start_time = time.time()
|
|
458
|
-
|
|
483
|
+
|
|
459
484
|
success = frame_cleaner.remove_frames_from_video(
|
|
460
485
|
original_video=video_path,
|
|
461
486
|
frames_to_remove=frames_to_remove,
|
|
462
|
-
output_video=output_path
|
|
487
|
+
output_video=output_path,
|
|
463
488
|
)
|
|
464
|
-
|
|
489
|
+
|
|
465
490
|
processing_time = time.time() - start_time
|
|
466
|
-
|
|
491
|
+
|
|
467
492
|
if success:
|
|
468
493
|
# Update video record
|
|
469
494
|
update_processed_file(video, output_path)
|
|
470
495
|
|
|
471
|
-
|
|
472
496
|
# Phase 1.4: Update LabelVideoSegments (shift frame numbers)
|
|
473
|
-
segment_update_result = update_segments_after_frame_removal(
|
|
474
|
-
|
|
497
|
+
segment_update_result = update_segments_after_frame_removal(
|
|
498
|
+
video, frames_to_remove
|
|
499
|
+
)
|
|
500
|
+
|
|
475
501
|
# Mark history as success with segment update info
|
|
476
502
|
details_parts = [
|
|
477
503
|
f"Removed {len(frames_to_remove)} frames in {processing_time:.1f}s"
|
|
478
504
|
]
|
|
479
|
-
if segment_update_result[
|
|
505
|
+
if segment_update_result["segments_updated"] > 0:
|
|
480
506
|
details_parts.append(
|
|
481
507
|
f"Updated {segment_update_result['segments_updated']} segments"
|
|
482
508
|
)
|
|
483
|
-
if segment_update_result[
|
|
509
|
+
if segment_update_result["segments_deleted"] > 0:
|
|
484
510
|
details_parts.append(
|
|
485
511
|
f"Deleted {segment_update_result['segments_deleted']} segments (all frames removed)"
|
|
486
512
|
)
|
|
487
|
-
|
|
513
|
+
|
|
488
514
|
history.mark_success(
|
|
489
|
-
output_file=str(output_path),
|
|
490
|
-
|
|
515
|
+
output_file=str(output_path), details="; ".join(details_parts)
|
|
516
|
+
)
|
|
517
|
+
|
|
518
|
+
logger.info(
|
|
519
|
+
f"Video {pk} cleaned: removed {len(frames_to_remove)} frames"
|
|
520
|
+
)
|
|
521
|
+
|
|
522
|
+
return Response(
|
|
523
|
+
{
|
|
524
|
+
"task_id": None, # Will be Celery task ID in Phase 1.2
|
|
525
|
+
"output_file": str(output_path),
|
|
526
|
+
"frames_removed": len(frames_to_remove),
|
|
527
|
+
"segment_updates": segment_update_result,
|
|
528
|
+
"message": "Frame removal complete",
|
|
529
|
+
"processing_time": processing_time,
|
|
530
|
+
}
|
|
491
531
|
)
|
|
492
|
-
|
|
493
|
-
logger.info(f"Video {pk} cleaned: removed {len(frames_to_remove)} frames")
|
|
494
|
-
|
|
495
|
-
return Response({
|
|
496
|
-
'task_id': None, # Will be Celery task ID in Phase 1.2
|
|
497
|
-
'output_file': str(output_path),
|
|
498
|
-
'frames_removed': len(frames_to_remove),
|
|
499
|
-
'segment_updates': segment_update_result,
|
|
500
|
-
'message': 'Frame removal complete',
|
|
501
|
-
'processing_time': processing_time
|
|
502
|
-
})
|
|
503
532
|
else:
|
|
504
533
|
raise Exception("Frame removal failed - check FFmpeg logs")
|
|
505
|
-
|
|
534
|
+
|
|
506
535
|
except Exception as e:
|
|
507
536
|
logger.error(f"Frame removal failed for {pk}: {str(e)}", exc_info=True)
|
|
508
|
-
|
|
537
|
+
|
|
509
538
|
history.mark_failure(str(e))
|
|
510
|
-
|
|
539
|
+
|
|
511
540
|
return Response(
|
|
512
|
-
{
|
|
513
|
-
status=status.HTTP_500_INTERNAL_SERVER_ERROR
|
|
541
|
+
{"error": f"Frame removal failed: {str(e)}"},
|
|
542
|
+
status=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
514
543
|
)
|
|
515
|
-
|
|
544
|
+
|
|
516
545
|
def _parse_frame_ranges(self, ranges_str: str) -> list:
|
|
517
546
|
"""
|
|
518
547
|
Parse frame ranges string to list of frame numbers.
|
|
519
|
-
|
|
548
|
+
|
|
520
549
|
Example: "10-20,30,45-50" -> [10,11,...,20,30,45,...,50]
|
|
521
550
|
"""
|
|
522
551
|
frames = []
|
|
523
|
-
for part in ranges_str.split(
|
|
552
|
+
for part in ranges_str.split(","):
|
|
524
553
|
part = part.strip()
|
|
525
|
-
if
|
|
526
|
-
start, end = map(int, part.split(
|
|
554
|
+
if "-" in part:
|
|
555
|
+
start, end = map(int, part.split("-"))
|
|
527
556
|
frames.extend(range(start, end + 1))
|
|
528
557
|
else:
|
|
529
558
|
frames.append(int(part))
|
|
530
|
-
return sorted(set(frames)) # Remove duplicates and sort
|
|
559
|
+
return sorted(set(frames)) # Remove duplicates and sort
|