endoreg-db 0.8.9.2__py3-none-any.whl → 0.8.9.10__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/admin.py +10 -5
- endoreg_db/apps.py +4 -7
- endoreg_db/authz/auth.py +1 -0
- endoreg_db/authz/backends.py +1 -1
- endoreg_db/authz/management/commands/list_routes.py +2 -0
- endoreg_db/authz/middleware.py +8 -7
- endoreg_db/authz/permissions.py +21 -10
- endoreg_db/authz/policy.py +14 -19
- endoreg_db/authz/views_auth.py +14 -10
- endoreg_db/codemods/rename_datetime_fields.py +8 -1
- endoreg_db/exceptions.py +5 -2
- endoreg_db/forms/__init__.py +0 -1
- endoreg_db/forms/examination_form.py +4 -3
- endoreg_db/forms/patient_finding_intervention_form.py +30 -8
- endoreg_db/forms/patient_form.py +9 -13
- endoreg_db/forms/questionnaires/__init__.py +1 -1
- endoreg_db/forms/settings/__init__.py +4 -1
- endoreg_db/forms/unit.py +2 -1
- endoreg_db/helpers/count_db.py +17 -14
- endoreg_db/helpers/default_objects.py +2 -1
- endoreg_db/helpers/download_segmentation_model.py +4 -3
- endoreg_db/helpers/interact.py +0 -5
- endoreg_db/helpers/test_video_helper.py +33 -25
- endoreg_db/import_files/__init__.py +1 -1
- endoreg_db/import_files/context/__init__.py +1 -1
- endoreg_db/import_files/context/default_sensitive_meta.py +11 -9
- endoreg_db/import_files/context/ensure_center.py +4 -4
- endoreg_db/import_files/context/file_lock.py +3 -3
- endoreg_db/import_files/context/import_context.py +11 -12
- endoreg_db/import_files/context/validate_directories.py +1 -0
- endoreg_db/import_files/file_storage/create_report_file.py +57 -34
- endoreg_db/import_files/file_storage/create_video_file.py +64 -35
- endoreg_db/import_files/file_storage/sensitive_meta_storage.py +5 -2
- endoreg_db/import_files/file_storage/state_management.py +89 -122
- endoreg_db/import_files/file_storage/storage.py +5 -1
- endoreg_db/import_files/processing/report_processing/report_anonymization.py +24 -19
- endoreg_db/import_files/processing/sensitive_meta_adapter.py +3 -3
- endoreg_db/import_files/processing/video_processing/video_anonymization.py +18 -18
- endoreg_db/import_files/pseudonymization/k_anonymity.py +8 -9
- endoreg_db/import_files/pseudonymization/k_pseudonymity.py +16 -5
- endoreg_db/import_files/report_import_service.py +36 -30
- endoreg_db/import_files/video_import_service.py +27 -23
- endoreg_db/logger_conf.py +56 -40
- endoreg_db/management/__init__.py +1 -1
- endoreg_db/management/commands/__init__.py +1 -1
- endoreg_db/management/commands/check_auth.py +45 -38
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +53 -2
- endoreg_db/management/commands/create_multilabel_model_meta.py +54 -19
- endoreg_db/management/commands/fix_missing_patient_data.py +105 -71
- endoreg_db/management/commands/fix_video_paths.py +75 -54
- endoreg_db/management/commands/import_report.py +1 -3
- endoreg_db/management/commands/list_routes.py +2 -0
- endoreg_db/management/commands/load_ai_model_data.py +8 -2
- endoreg_db/management/commands/load_ai_model_label_data.py +0 -1
- endoreg_db/management/commands/load_center_data.py +3 -3
- endoreg_db/management/commands/load_distribution_data.py +35 -38
- endoreg_db/management/commands/load_endoscope_data.py +0 -3
- endoreg_db/management/commands/load_examination_data.py +20 -4
- endoreg_db/management/commands/load_finding_data.py +18 -3
- endoreg_db/management/commands/load_gender_data.py +17 -24
- endoreg_db/management/commands/load_green_endoscopy_wuerzburg_data.py +95 -85
- endoreg_db/management/commands/load_information_source.py +0 -3
- endoreg_db/management/commands/load_lab_value_data.py +14 -3
- endoreg_db/management/commands/load_legacy_data.py +303 -0
- endoreg_db/management/commands/load_name_data.py +1 -2
- endoreg_db/management/commands/load_pdf_type_data.py +4 -8
- endoreg_db/management/commands/load_profession_data.py +0 -1
- endoreg_db/management/commands/load_report_reader_flag_data.py +0 -4
- endoreg_db/management/commands/load_requirement_data.py +6 -2
- endoreg_db/management/commands/load_unit_data.py +0 -4
- endoreg_db/management/commands/load_user_groups.py +5 -7
- endoreg_db/management/commands/model_input.py +169 -0
- endoreg_db/management/commands/register_ai_model.py +22 -16
- endoreg_db/management/commands/setup_endoreg_db.py +110 -32
- endoreg_db/management/commands/storage_management.py +14 -8
- endoreg_db/management/commands/summarize_db_content.py +154 -63
- endoreg_db/management/commands/train_image_multilabel_model.py +144 -0
- endoreg_db/management/commands/validate_video_files.py +82 -50
- endoreg_db/management/commands/video_validation.py +4 -6
- endoreg_db/migrations/0001_initial.py +112 -63
- endoreg_db/models/__init__.py +8 -0
- endoreg_db/models/administration/ai/active_model.py +5 -5
- endoreg_db/models/administration/ai/ai_model.py +41 -18
- endoreg_db/models/administration/ai/model_type.py +1 -0
- endoreg_db/models/administration/case/case.py +22 -22
- endoreg_db/models/administration/center/__init__.py +5 -5
- endoreg_db/models/administration/center/center.py +6 -2
- endoreg_db/models/administration/center/center_resource.py +18 -4
- endoreg_db/models/administration/center/center_shift.py +3 -1
- endoreg_db/models/administration/center/center_waste.py +6 -2
- endoreg_db/models/administration/person/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/__init__.py +1 -1
- endoreg_db/models/administration/person/employee/employee_type.py +3 -1
- endoreg_db/models/administration/person/examiner/__init__.py +1 -1
- endoreg_db/models/administration/person/examiner/examiner.py +10 -2
- endoreg_db/models/administration/person/names/first_name.py +6 -4
- endoreg_db/models/administration/person/names/last_name.py +4 -3
- endoreg_db/models/administration/person/patient/__init__.py +1 -1
- endoreg_db/models/administration/person/patient/patient.py +0 -1
- endoreg_db/models/administration/person/patient/patient_external_id.py +0 -1
- endoreg_db/models/administration/person/person.py +1 -1
- endoreg_db/models/administration/product/__init__.py +7 -6
- endoreg_db/models/administration/product/product.py +6 -2
- endoreg_db/models/administration/product/product_group.py +9 -7
- endoreg_db/models/administration/product/product_material.py +9 -2
- endoreg_db/models/administration/product/reference_product.py +64 -15
- endoreg_db/models/administration/qualification/qualification.py +3 -1
- endoreg_db/models/administration/shift/shift.py +3 -1
- endoreg_db/models/administration/shift/shift_type.py +12 -4
- endoreg_db/models/aidataset/__init__.py +5 -0
- endoreg_db/models/aidataset/aidataset.py +193 -0
- endoreg_db/models/label/__init__.py +1 -1
- endoreg_db/models/label/label.py +10 -2
- endoreg_db/models/label/label_set.py +3 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +6 -2
- endoreg_db/models/label/label_video_segment/label_video_segment.py +148 -44
- endoreg_db/models/media/__init__.py +12 -5
- endoreg_db/models/media/frame/__init__.py +1 -1
- endoreg_db/models/media/frame/frame.py +34 -8
- endoreg_db/models/media/pdf/__init__.py +2 -1
- endoreg_db/models/media/pdf/raw_pdf.py +11 -4
- endoreg_db/models/media/pdf/report_file.py +6 -2
- endoreg_db/models/media/pdf/report_reader/__init__.py +3 -3
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +15 -5
- endoreg_db/models/media/video/create_from_file.py +20 -41
- endoreg_db/models/media/video/pipe_1.py +75 -30
- endoreg_db/models/media/video/pipe_2.py +37 -12
- endoreg_db/models/media/video/video_file.py +36 -24
- endoreg_db/models/media/video/video_file_ai.py +235 -70
- endoreg_db/models/media/video/video_file_anonymize.py +240 -65
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -1
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +30 -9
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +95 -29
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +13 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -1
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +15 -3
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +7 -2
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +109 -23
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +111 -27
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +46 -13
- endoreg_db/models/media/video/video_file_io.py +85 -33
- endoreg_db/models/media/video/video_file_meta/__init__.py +6 -6
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +17 -4
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +28 -7
- endoreg_db/models/media/video/video_file_meta/get_fps.py +46 -13
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +81 -20
- endoreg_db/models/media/video/video_file_meta/text_meta.py +61 -20
- endoreg_db/models/media/video/video_file_meta/video_meta.py +40 -12
- endoreg_db/models/media/video/video_file_segments.py +118 -27
- endoreg_db/models/media/video/video_metadata.py +25 -6
- endoreg_db/models/media/video/video_processing.py +54 -15
- endoreg_db/models/medical/__init__.py +3 -13
- endoreg_db/models/medical/contraindication/__init__.py +3 -1
- endoreg_db/models/medical/disease.py +18 -6
- endoreg_db/models/medical/event.py +6 -2
- endoreg_db/models/medical/examination/__init__.py +5 -1
- endoreg_db/models/medical/examination/examination.py +22 -6
- endoreg_db/models/medical/examination/examination_indication.py +23 -7
- endoreg_db/models/medical/examination/examination_time.py +6 -2
- endoreg_db/models/medical/finding/__init__.py +3 -1
- endoreg_db/models/medical/finding/finding.py +37 -12
- endoreg_db/models/medical/finding/finding_classification.py +27 -8
- endoreg_db/models/medical/finding/finding_intervention.py +19 -6
- endoreg_db/models/medical/finding/finding_type.py +3 -1
- endoreg_db/models/medical/hardware/__init__.py +1 -1
- endoreg_db/models/medical/hardware/endoscope.py +14 -2
- endoreg_db/models/medical/laboratory/__init__.py +1 -1
- endoreg_db/models/medical/laboratory/lab_value.py +139 -39
- endoreg_db/models/medical/medication/__init__.py +7 -3
- endoreg_db/models/medical/medication/medication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication.py +3 -1
- endoreg_db/models/medical/medication/medication_indication_type.py +11 -3
- endoreg_db/models/medical/medication/medication_intake_time.py +3 -1
- endoreg_db/models/medical/medication/medication_schedule.py +3 -1
- endoreg_db/models/medical/patient/__init__.py +2 -10
- endoreg_db/models/medical/patient/medication_examples.py +3 -14
- endoreg_db/models/medical/patient/patient_disease.py +17 -5
- endoreg_db/models/medical/patient/patient_event.py +12 -4
- endoreg_db/models/medical/patient/patient_examination.py +52 -15
- endoreg_db/models/medical/patient/patient_examination_indication.py +15 -4
- endoreg_db/models/medical/patient/patient_finding.py +105 -29
- endoreg_db/models/medical/patient/patient_finding_classification.py +41 -12
- endoreg_db/models/medical/patient/patient_finding_intervention.py +11 -3
- endoreg_db/models/medical/patient/patient_lab_sample.py +6 -2
- endoreg_db/models/medical/patient/patient_lab_value.py +42 -10
- endoreg_db/models/medical/patient/patient_medication.py +25 -7
- endoreg_db/models/medical/patient/patient_medication_schedule.py +34 -10
- endoreg_db/models/metadata/model_meta.py +40 -12
- endoreg_db/models/metadata/model_meta_logic.py +51 -16
- endoreg_db/models/metadata/sensitive_meta.py +65 -28
- endoreg_db/models/metadata/sensitive_meta_logic.py +28 -26
- endoreg_db/models/metadata/video_meta.py +146 -39
- endoreg_db/models/metadata/video_prediction_logic.py +70 -21
- endoreg_db/models/metadata/video_prediction_meta.py +80 -27
- endoreg_db/models/operation_log.py +63 -0
- endoreg_db/models/other/__init__.py +10 -10
- endoreg_db/models/other/distribution/__init__.py +9 -7
- endoreg_db/models/other/distribution/base_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/date_value_distribution.py +19 -5
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +3 -1
- endoreg_db/models/other/distribution/numeric_value_distribution.py +34 -9
- endoreg_db/models/other/emission/__init__.py +1 -1
- endoreg_db/models/other/emission/emission_factor.py +9 -3
- endoreg_db/models/other/information_source.py +15 -5
- endoreg_db/models/other/material.py +3 -1
- endoreg_db/models/other/transport_route.py +3 -1
- endoreg_db/models/other/unit.py +6 -2
- endoreg_db/models/report/report.py +0 -1
- endoreg_db/models/requirement/requirement.py +84 -27
- endoreg_db/models/requirement/requirement_error.py +5 -6
- endoreg_db/models/requirement/requirement_evaluation/__init__.py +1 -1
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +8 -8
- endoreg_db/models/requirement/requirement_evaluation/get_values.py +3 -3
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +24 -8
- endoreg_db/models/requirement/requirement_operator.py +28 -8
- endoreg_db/models/requirement/requirement_set.py +34 -11
- endoreg_db/models/state/__init__.py +1 -0
- endoreg_db/models/state/audit_ledger.py +9 -2
- endoreg_db/models/{media → state}/processing_history/__init__.py +1 -3
- endoreg_db/models/state/processing_history/processing_history.py +136 -0
- endoreg_db/models/state/raw_pdf.py +0 -1
- endoreg_db/models/state/video.py +2 -4
- endoreg_db/models/utils.py +4 -2
- endoreg_db/queries/__init__.py +2 -6
- endoreg_db/queries/annotations/__init__.py +1 -3
- endoreg_db/queries/annotations/legacy.py +37 -26
- endoreg_db/root_urls.py +3 -4
- endoreg_db/schemas/examination_evaluation.py +3 -0
- endoreg_db/serializers/Frames_NICE_and_PARIS_classifications.py +249 -163
- endoreg_db/serializers/__init__.py +2 -8
- endoreg_db/serializers/administration/__init__.py +1 -2
- endoreg_db/serializers/administration/ai/__init__.py +0 -1
- endoreg_db/serializers/administration/ai/active_model.py +3 -1
- endoreg_db/serializers/administration/ai/ai_model.py +5 -3
- endoreg_db/serializers/administration/ai/model_type.py +3 -1
- endoreg_db/serializers/administration/center.py +7 -2
- endoreg_db/serializers/administration/gender.py +4 -2
- endoreg_db/serializers/anonymization.py +13 -13
- endoreg_db/serializers/evaluation/examination_evaluation.py +0 -1
- endoreg_db/serializers/examination/__init__.py +1 -1
- endoreg_db/serializers/examination/base.py +12 -13
- endoreg_db/serializers/examination/dropdown.py +6 -7
- endoreg_db/serializers/examination_serializer.py +3 -6
- endoreg_db/serializers/finding/__init__.py +1 -1
- endoreg_db/serializers/finding/finding.py +14 -7
- endoreg_db/serializers/finding_classification/__init__.py +3 -3
- endoreg_db/serializers/finding_classification/choice.py +3 -3
- endoreg_db/serializers/finding_classification/classification.py +2 -4
- endoreg_db/serializers/label_video_segment/__init__.py +5 -3
- endoreg_db/serializers/{label → label_video_segment}/image_classification_annotation.py +5 -5
- endoreg_db/serializers/label_video_segment/label/__init__.py +6 -0
- endoreg_db/serializers/{label → label_video_segment/label}/label.py +1 -1
- endoreg_db/serializers/label_video_segment/label_video_segment.py +338 -228
- endoreg_db/serializers/meta/__init__.py +1 -2
- endoreg_db/serializers/meta/sensitive_meta_detail.py +28 -13
- endoreg_db/serializers/meta/sensitive_meta_update.py +51 -46
- endoreg_db/serializers/meta/sensitive_meta_verification.py +19 -16
- endoreg_db/serializers/misc/__init__.py +2 -2
- endoreg_db/serializers/misc/file_overview.py +11 -7
- endoreg_db/serializers/misc/stats.py +10 -8
- endoreg_db/serializers/misc/translatable_field_mix_in.py +6 -6
- endoreg_db/serializers/misc/upload_job.py +32 -29
- endoreg_db/serializers/patient/__init__.py +2 -1
- endoreg_db/serializers/patient/patient.py +32 -15
- endoreg_db/serializers/patient/patient_dropdown.py +11 -3
- endoreg_db/serializers/patient_examination/__init__.py +1 -1
- endoreg_db/serializers/patient_examination/patient_examination.py +67 -40
- endoreg_db/serializers/patient_finding/__init__.py +1 -1
- endoreg_db/serializers/patient_finding/patient_finding.py +2 -1
- endoreg_db/serializers/patient_finding/patient_finding_classification.py +17 -9
- endoreg_db/serializers/patient_finding/patient_finding_detail.py +26 -17
- endoreg_db/serializers/patient_finding/patient_finding_intervention.py +7 -5
- endoreg_db/serializers/patient_finding/patient_finding_list.py +10 -11
- endoreg_db/serializers/patient_finding/patient_finding_write.py +36 -27
- endoreg_db/serializers/pdf/__init__.py +1 -3
- endoreg_db/serializers/requirements/requirement_schema.py +1 -6
- endoreg_db/serializers/sensitive_meta_serializer.py +100 -81
- endoreg_db/serializers/video/__init__.py +2 -2
- endoreg_db/serializers/video/{segmentation.py → video_file.py} +66 -47
- endoreg_db/serializers/video/video_file_brief.py +6 -2
- endoreg_db/serializers/video/video_file_detail.py +36 -23
- endoreg_db/serializers/video/video_file_list.py +4 -2
- endoreg_db/serializers/video/video_processing_history.py +54 -50
- endoreg_db/services/__init__.py +1 -1
- endoreg_db/services/anonymization.py +2 -2
- endoreg_db/services/examination_evaluation.py +40 -17
- endoreg_db/services/model_meta_from_hf.py +76 -0
- endoreg_db/services/polling_coordinator.py +101 -70
- endoreg_db/services/pseudonym_service.py +27 -22
- endoreg_db/services/report_import.py +6 -3
- endoreg_db/services/segment_sync.py +75 -59
- endoreg_db/services/video_import.py +6 -7
- endoreg_db/urls/__init__.py +2 -2
- endoreg_db/urls/ai.py +7 -25
- endoreg_db/urls/anonymization.py +61 -15
- endoreg_db/urls/auth.py +4 -4
- endoreg_db/urls/classification.py +4 -9
- endoreg_db/urls/examination.py +27 -18
- endoreg_db/urls/media.py +27 -34
- endoreg_db/urls/patient.py +11 -7
- endoreg_db/urls/requirements.py +3 -1
- endoreg_db/urls/root_urls.py +2 -3
- endoreg_db/urls/stats.py +24 -16
- endoreg_db/urls/upload.py +3 -11
- endoreg_db/utils/__init__.py +14 -15
- endoreg_db/utils/ai/__init__.py +1 -1
- endoreg_db/utils/ai/data_loader_for_model_input.py +262 -0
- endoreg_db/utils/ai/data_loader_for_model_training.py +262 -0
- endoreg_db/utils/ai/get.py +2 -1
- endoreg_db/utils/ai/inference_dataset.py +14 -15
- endoreg_db/utils/ai/model_training/config.py +117 -0
- endoreg_db/utils/ai/model_training/dataset.py +74 -0
- endoreg_db/utils/ai/model_training/losses.py +68 -0
- endoreg_db/utils/ai/model_training/metrics.py +78 -0
- endoreg_db/utils/ai/model_training/model_backbones.py +155 -0
- endoreg_db/utils/ai/model_training/model_gastronet_resnet.py +118 -0
- endoreg_db/utils/ai/model_training/trainer_gastronet_multilabel.py +771 -0
- endoreg_db/utils/ai/multilabel_classification_net.py +21 -6
- endoreg_db/utils/ai/predict.py +4 -4
- endoreg_db/utils/ai/preprocess.py +19 -11
- endoreg_db/utils/calc_duration_seconds.py +4 -4
- endoreg_db/utils/case_generator/lab_sample_factory.py +3 -4
- endoreg_db/utils/check_video_files.py +74 -47
- endoreg_db/utils/cropping.py +10 -9
- endoreg_db/utils/dataloader.py +11 -3
- endoreg_db/utils/dates.py +3 -4
- endoreg_db/utils/defaults/set_default_center.py +7 -6
- endoreg_db/utils/env.py +6 -2
- endoreg_db/utils/extract_specific_frames.py +24 -9
- endoreg_db/utils/file_operations.py +30 -18
- endoreg_db/utils/fix_video_path_direct.py +57 -41
- endoreg_db/utils/frame_anonymization_utils.py +157 -157
- endoreg_db/utils/hashs.py +3 -18
- endoreg_db/utils/links/requirement_link.py +96 -52
- endoreg_db/utils/ocr.py +30 -25
- endoreg_db/utils/operation_log.py +61 -0
- endoreg_db/utils/parse_and_generate_yaml.py +12 -13
- endoreg_db/utils/paths.py +6 -6
- endoreg_db/utils/permissions.py +40 -24
- endoreg_db/utils/pipelines/process_video_dir.py +50 -26
- endoreg_db/utils/product/sum_emissions.py +5 -3
- endoreg_db/utils/product/sum_weights.py +4 -2
- endoreg_db/utils/pydantic_models/__init__.py +3 -4
- endoreg_db/utils/requirement_operator_logic/_old/lab_value_operators.py +207 -107
- endoreg_db/utils/requirement_operator_logic/_old/model_evaluators.py +252 -65
- endoreg_db/utils/requirement_operator_logic/new_operator_logic.py +27 -10
- endoreg_db/utils/setup_config.py +21 -5
- endoreg_db/utils/storage.py +3 -1
- endoreg_db/utils/translation.py +19 -15
- endoreg_db/utils/uuid.py +1 -0
- endoreg_db/utils/validate_endo_roi.py +12 -4
- endoreg_db/utils/validate_subcategory_dict.py +26 -24
- endoreg_db/utils/validate_video_detailed.py +207 -149
- endoreg_db/utils/video/__init__.py +7 -3
- endoreg_db/utils/video/extract_frames.py +30 -18
- endoreg_db/utils/video/names.py +11 -6
- endoreg_db/utils/video/streaming_processor.py +175 -101
- endoreg_db/utils/video/video_splitter.py +30 -19
- endoreg_db/views/Frames_NICE_and_PARIS_classifications_views.py +59 -50
- endoreg_db/views/__init__.py +0 -20
- endoreg_db/views/anonymization/__init__.py +6 -2
- endoreg_db/views/anonymization/media_management.py +2 -6
- endoreg_db/views/anonymization/overview.py +34 -1
- endoreg_db/views/anonymization/validate.py +79 -18
- endoreg_db/views/auth/__init__.py +1 -1
- endoreg_db/views/auth/keycloak.py +16 -14
- endoreg_db/views/examination/__init__.py +12 -15
- endoreg_db/views/examination/examination.py +5 -5
- endoreg_db/views/examination/examination_manifest_cache.py +5 -5
- endoreg_db/views/examination/get_finding_classification_choices.py +8 -5
- endoreg_db/views/examination/get_finding_classifications.py +9 -7
- endoreg_db/views/examination/get_findings.py +8 -10
- endoreg_db/views/examination/get_instruments.py +3 -2
- endoreg_db/views/examination/get_interventions.py +1 -1
- endoreg_db/views/finding/__init__.py +2 -2
- endoreg_db/views/finding/finding.py +58 -54
- endoreg_db/views/finding/get_classifications.py +1 -1
- endoreg_db/views/finding/get_interventions.py +1 -1
- endoreg_db/views/finding_classification/__init__.py +5 -5
- endoreg_db/views/finding_classification/finding_classification.py +5 -6
- endoreg_db/views/finding_classification/get_classification_choices.py +3 -4
- endoreg_db/views/media/__init__.py +13 -13
- endoreg_db/views/media/pdf_media.py +9 -9
- endoreg_db/views/media/sensitive_metadata.py +10 -7
- endoreg_db/views/media/video_media.py +4 -4
- endoreg_db/views/meta/__init__.py +1 -1
- endoreg_db/views/meta/sensitive_meta_list.py +20 -22
- endoreg_db/views/meta/sensitive_meta_verification.py +14 -11
- endoreg_db/views/misc/__init__.py +6 -34
- endoreg_db/views/misc/center.py +2 -1
- endoreg_db/views/misc/csrf.py +2 -1
- endoreg_db/views/misc/gender.py +2 -1
- endoreg_db/views/misc/stats.py +141 -106
- endoreg_db/views/patient/__init__.py +1 -3
- endoreg_db/views/patient/patient.py +141 -99
- endoreg_db/views/patient_examination/__init__.py +5 -5
- endoreg_db/views/patient_examination/patient_examination.py +43 -42
- endoreg_db/views/patient_examination/patient_examination_create.py +10 -15
- endoreg_db/views/patient_examination/patient_examination_detail.py +12 -15
- endoreg_db/views/patient_examination/patient_examination_list.py +21 -17
- endoreg_db/views/patient_examination/video.py +114 -80
- endoreg_db/views/patient_finding/__init__.py +1 -1
- endoreg_db/views/patient_finding/patient_finding.py +17 -10
- endoreg_db/views/patient_finding/patient_finding_optimized.py +127 -95
- endoreg_db/views/patient_finding_classification/__init__.py +1 -1
- endoreg_db/views/patient_finding_classification/pfc_create.py +35 -27
- endoreg_db/views/report/reimport.py +1 -1
- endoreg_db/views/report/report_stream.py +5 -8
- endoreg_db/views/requirement/__init__.py +2 -1
- endoreg_db/views/requirement/evaluate.py +7 -9
- endoreg_db/views/requirement/lookup.py +2 -3
- endoreg_db/views/requirement/lookup_store.py +0 -1
- endoreg_db/views/requirement/requirement_utils.py +2 -4
- endoreg_db/views/stats/__init__.py +4 -4
- endoreg_db/views/stats/stats_views.py +152 -115
- endoreg_db/views/video/__init__.py +18 -27
- endoreg_db/views/{ai → video/ai}/__init__.py +2 -2
- endoreg_db/views/{ai → video/ai}/label.py +20 -16
- endoreg_db/views/video/correction.py +5 -6
- endoreg_db/views/video/reimport.py +134 -99
- endoreg_db/views/video/segments_crud.py +134 -44
- endoreg_db/views/video/video_apply_mask.py +13 -12
- endoreg_db/views/video/video_correction.py +2 -1
- endoreg_db/views/video/video_download_processed.py +15 -15
- endoreg_db/views/video/video_meta_stats.py +7 -6
- endoreg_db/views/video/video_processing_history.py +3 -2
- endoreg_db/views/video/video_remove_frames.py +13 -12
- endoreg_db/views/video/video_stream.py +110 -82
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/METADATA +9 -3
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/RECORD +434 -431
- endoreg_db/management/commands/import_fallback_video.py +0 -203
- endoreg_db/management/commands/import_video.py +0 -422
- endoreg_db/management/commands/import_video_with_classification.py +0 -367
- endoreg_db/models/media/processing_history/processing_history.py +0 -96
- endoreg_db/serializers/label/__init__.py +0 -7
- endoreg_db/serializers/label_video_segment/_lvs_create.py +0 -149
- endoreg_db/serializers/label_video_segment/_lvs_update.py +0 -138
- endoreg_db/serializers/label_video_segment/_lvs_validate.py +0 -149
- endoreg_db/serializers/label_video_segment/label_video_segment_annotation.py +0 -99
- endoreg_db/serializers/label_video_segment/label_video_segment_update.py +0 -163
- endoreg_db/services/__old/pdf_import.py +0 -1487
- endoreg_db/services/__old/video_import.py +0 -1306
- endoreg_db/tasks/upload_tasks.py +0 -216
- endoreg_db/tasks/video_ingest.py +0 -161
- endoreg_db/tasks/video_processing_tasks.py +0 -327
- endoreg_db/views/misc/translation.py +0 -182
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.9.2.dist-info → endoreg_db-0.8.9.10.dist-info}/licenses/LICENSE +0 -0
|
@@ -2,7 +2,9 @@ import logging
|
|
|
2
2
|
from typing import TYPE_CHECKING
|
|
3
3
|
|
|
4
4
|
from endoreg_db.models.media.video.video_file_io import _get_frame_dir_path
|
|
5
|
-
from endoreg_db.utils.video.ffmpeg_wrapper import
|
|
5
|
+
from endoreg_db.utils.video.ffmpeg_wrapper import (
|
|
6
|
+
extract_frames as ffmpeg_extract_frames,
|
|
7
|
+
)
|
|
6
8
|
|
|
7
9
|
if TYPE_CHECKING:
|
|
8
10
|
from endoreg_db.models import VideoFile
|
|
@@ -48,19 +50,27 @@ def _extract_frames(
|
|
|
48
50
|
if from_processed:
|
|
49
51
|
raw_file_path = video.get_processed_file_path()
|
|
50
52
|
if not raw_file_path or not raw_file_path.exists():
|
|
51
|
-
raise FileNotFoundError(
|
|
53
|
+
raise FileNotFoundError(
|
|
54
|
+
f"Processed video file not found at {raw_file_path} for video {video.video_hash}. Cannot extract frames."
|
|
55
|
+
)
|
|
52
56
|
else:
|
|
53
57
|
# Pre-validation checks (outside any transaction)
|
|
54
58
|
if not video.has_raw:
|
|
55
|
-
raise FileNotFoundError(
|
|
59
|
+
raise FileNotFoundError(
|
|
60
|
+
f"Raw video file not available for {video.video_hash}. Cannot extract frames."
|
|
61
|
+
)
|
|
56
62
|
|
|
57
63
|
raw_file_path = video.get_raw_file_path()
|
|
58
64
|
if not raw_file_path or not raw_file_path.exists():
|
|
59
|
-
raise FileNotFoundError(
|
|
65
|
+
raise FileNotFoundError(
|
|
66
|
+
f"Raw video file not found at {raw_file_path} for video {video.video_hash}. Cannot extract frames."
|
|
67
|
+
)
|
|
60
68
|
|
|
61
69
|
frame_dir = _get_frame_dir_path(video)
|
|
62
70
|
if not frame_dir:
|
|
63
|
-
raise ValueError(
|
|
71
|
+
raise ValueError(
|
|
72
|
+
f"Cannot determine frame directory path for video {video.video_hash}."
|
|
73
|
+
)
|
|
64
74
|
|
|
65
75
|
state = video.get_or_create_state()
|
|
66
76
|
frames_exist_in_db = Frame.objects.filter(video=video).exists()
|
|
@@ -70,59 +80,83 @@ def _extract_frames(
|
|
|
70
80
|
if (state.frames_extracted or files_exist_on_disk) and not overwrite:
|
|
71
81
|
logger.info(
|
|
72
82
|
"Frames already extracted or files exist for video %s, and overwrite=False. Skipping extraction.",
|
|
73
|
-
video.
|
|
83
|
+
video.video_hash,
|
|
74
84
|
)
|
|
75
85
|
with transaction.atomic():
|
|
76
86
|
state.refresh_from_db()
|
|
77
87
|
if frames_exist_in_db:
|
|
78
|
-
updated_count = Frame.objects.filter(
|
|
88
|
+
updated_count = Frame.objects.filter(
|
|
89
|
+
video=video, is_extracted=False
|
|
90
|
+
).update(is_extracted=True)
|
|
79
91
|
if updated_count > 0:
|
|
80
92
|
logger.info(
|
|
81
93
|
"Marked %d existing Frame objects as extracted for video %s based on current records.",
|
|
82
94
|
updated_count,
|
|
83
|
-
video.
|
|
95
|
+
video.video_hash,
|
|
84
96
|
)
|
|
85
97
|
if files_exist_on_disk and not state.frames_extracted:
|
|
86
98
|
logger.warning(
|
|
87
99
|
"Files exist on disk for video %s but state.frames_extracted is False. Persisting corrected state.",
|
|
88
|
-
video.
|
|
100
|
+
video.video_hash,
|
|
89
101
|
)
|
|
90
102
|
state.mark_frames_extracted(save=True)
|
|
91
103
|
return True
|
|
92
104
|
|
|
93
105
|
# Overwrite: delete existing frames/files before re-extraction.
|
|
94
106
|
if overwrite:
|
|
95
|
-
logger.info(
|
|
107
|
+
logger.info(
|
|
108
|
+
"Overwrite=True. Preparing to delete existing frames/files for video %s before extraction.",
|
|
109
|
+
video.video_hash,
|
|
110
|
+
)
|
|
96
111
|
try:
|
|
97
112
|
_delete_frames(video)
|
|
98
113
|
with transaction.atomic():
|
|
99
|
-
updated_count = Frame.objects.filter(
|
|
114
|
+
updated_count = Frame.objects.filter(
|
|
115
|
+
video=video, is_extracted=True
|
|
116
|
+
).update(is_extracted=False)
|
|
100
117
|
if updated_count > 0:
|
|
101
118
|
logger.info(
|
|
102
119
|
"Reset %d Frame objects to is_extracted=False for video %s due to overwrite.",
|
|
103
120
|
updated_count,
|
|
104
|
-
video.
|
|
121
|
+
video.video_hash,
|
|
105
122
|
)
|
|
106
123
|
state.refresh_from_db()
|
|
107
124
|
except Exception as del_e:
|
|
108
|
-
logger.error(
|
|
109
|
-
|
|
125
|
+
logger.error(
|
|
126
|
+
"Failed to delete existing frames for video %s during overwrite: %s",
|
|
127
|
+
video.video_hash,
|
|
128
|
+
del_e,
|
|
129
|
+
exc_info=True,
|
|
130
|
+
)
|
|
131
|
+
raise RuntimeError(
|
|
132
|
+
f"Failed to delete existing frames for video {video.video_hash} during overwrite."
|
|
133
|
+
) from del_e
|
|
110
134
|
|
|
111
135
|
frame_dir.mkdir(parents=True, exist_ok=True)
|
|
112
136
|
|
|
113
137
|
try:
|
|
114
|
-
logger.info(
|
|
138
|
+
logger.info(
|
|
139
|
+
"Starting frame extraction for video %s to %s", video.video_hash, frame_dir
|
|
140
|
+
)
|
|
115
141
|
# Step 1: Perform the long-running frame extraction outside any transaction.
|
|
116
|
-
extracted_paths = ffmpeg_extract_frames(
|
|
142
|
+
extracted_paths = ffmpeg_extract_frames(
|
|
143
|
+
raw_file_path, frame_dir, quality=quality, ext=ext
|
|
144
|
+
)
|
|
117
145
|
if not extracted_paths:
|
|
118
146
|
logger.warning(
|
|
119
147
|
"ffmpeg_extract_frames returned no paths for video %s. Check video duration and ffmpeg logs.",
|
|
120
|
-
video.
|
|
148
|
+
video.video_hash,
|
|
121
149
|
)
|
|
122
150
|
if video.frame_count is not None and video.frame_count > 0:
|
|
123
|
-
raise RuntimeError(
|
|
151
|
+
raise RuntimeError(
|
|
152
|
+
f"ffmpeg_extract_frames returned no paths for video {video.video_hash}, but {video.frame_count} frames were expected."
|
|
153
|
+
)
|
|
124
154
|
|
|
125
|
-
logger.info(
|
|
155
|
+
logger.info(
|
|
156
|
+
"Successfully extracted %d frames using ffmpeg for video %s.",
|
|
157
|
+
len(extracted_paths),
|
|
158
|
+
video.video_hash,
|
|
159
|
+
)
|
|
126
160
|
|
|
127
161
|
extracted_frame_numbers = []
|
|
128
162
|
for frame_path in extracted_paths:
|
|
@@ -130,38 +164,70 @@ def _extract_frames(
|
|
|
130
164
|
frame_number = int(frame_path.stem.split("_")[-1])
|
|
131
165
|
extracted_frame_numbers.append(frame_number)
|
|
132
166
|
except (ValueError, IndexError) as e:
|
|
133
|
-
logger.warning(
|
|
167
|
+
logger.warning(
|
|
168
|
+
"Could not parse frame number from extracted file %s: %s",
|
|
169
|
+
frame_path.name,
|
|
170
|
+
e,
|
|
171
|
+
)
|
|
134
172
|
|
|
135
173
|
# Step 2: Perform all the quick DB updates inside a minimal atomic transaction.
|
|
136
174
|
with transaction.atomic():
|
|
137
175
|
if extracted_frame_numbers:
|
|
138
176
|
try:
|
|
139
|
-
update_count = Frame.objects.filter(
|
|
140
|
-
|
|
177
|
+
update_count = Frame.objects.filter(
|
|
178
|
+
video=video, frame_number__in=extracted_frame_numbers
|
|
179
|
+
).update(is_extracted=True)
|
|
180
|
+
logger.info(
|
|
181
|
+
"Marked %d Frame objects as is_extracted=True for video %s.",
|
|
182
|
+
update_count,
|
|
183
|
+
video.video_hash,
|
|
184
|
+
)
|
|
141
185
|
if update_count != len(extracted_frame_numbers):
|
|
142
186
|
logger.warning(
|
|
143
187
|
"Number of updated frames (%d) does not match number of parsed extracted files (%d) for video %s.",
|
|
144
188
|
update_count,
|
|
145
189
|
len(extracted_frame_numbers),
|
|
146
|
-
video.
|
|
190
|
+
video.video_hash,
|
|
147
191
|
)
|
|
148
192
|
except Exception as update_e:
|
|
149
|
-
logger.error(
|
|
193
|
+
logger.error(
|
|
194
|
+
"Failed to update is_extracted flag for frames of video %s: %s",
|
|
195
|
+
video.video_hash,
|
|
196
|
+
update_e,
|
|
197
|
+
exc_info=True,
|
|
198
|
+
)
|
|
150
199
|
state.refresh_from_db()
|
|
151
200
|
state.mark_frames_extracted()
|
|
152
201
|
return True
|
|
153
202
|
|
|
154
203
|
except Exception as e:
|
|
155
|
-
logger.error(
|
|
156
|
-
|
|
204
|
+
logger.error(
|
|
205
|
+
"Frame extraction or update failed for video %s: %s",
|
|
206
|
+
video.video_hash,
|
|
207
|
+
e,
|
|
208
|
+
exc_info=True,
|
|
209
|
+
)
|
|
210
|
+
logger.warning(
|
|
211
|
+
"Cleaning up frame directory %s for video %s due to extraction error.",
|
|
212
|
+
frame_dir,
|
|
213
|
+
video.video_hash,
|
|
214
|
+
)
|
|
157
215
|
shutil.rmtree(frame_dir, ignore_errors=True)
|
|
158
216
|
try:
|
|
159
217
|
with transaction.atomic():
|
|
160
|
-
Frame.objects.filter(video=video, is_extracted=True).update(
|
|
218
|
+
Frame.objects.filter(video=video, is_extracted=True).update(
|
|
219
|
+
is_extracted=False
|
|
220
|
+
)
|
|
161
221
|
state.refresh_from_db()
|
|
162
222
|
if state.frames_extracted:
|
|
163
223
|
state.frames_extracted = False
|
|
164
224
|
state.save(update_fields=["frames_extracted"])
|
|
165
225
|
except Exception as db_err:
|
|
166
|
-
logger.error(
|
|
167
|
-
|
|
226
|
+
logger.error(
|
|
227
|
+
"Failed to reset flags/state in DB during error handling for video %s: %s",
|
|
228
|
+
video.video_hash,
|
|
229
|
+
db_err,
|
|
230
|
+
)
|
|
231
|
+
raise RuntimeError(
|
|
232
|
+
f"Frame extraction or update failed for video {video.video_hash}."
|
|
233
|
+
) from e
|
|
@@ -15,12 +15,22 @@ def _get_frame(video: "VideoFile", frame_number: int) -> "Frame":
|
|
|
15
15
|
# Access related manager directly
|
|
16
16
|
return video.frames.get(frame_number=frame_number)
|
|
17
17
|
except AttributeError:
|
|
18
|
-
logger.error(
|
|
18
|
+
logger.error(
|
|
19
|
+
"Could not access frame %d for video %s via related manager.",
|
|
20
|
+
frame_number,
|
|
21
|
+
video.video_hash,
|
|
22
|
+
)
|
|
19
23
|
# Fallback query
|
|
20
24
|
return Frame.objects.get(video_file=video, frame_number=frame_number)
|
|
21
25
|
except Frame.DoesNotExist:
|
|
22
|
-
logger.error("Frame %d not found for video %s.", frame_number, video.
|
|
26
|
+
logger.error("Frame %d not found for video %s.", frame_number, video.video_hash)
|
|
23
27
|
raise # Re-raise DoesNotExist
|
|
24
28
|
except Exception as e:
|
|
25
|
-
logger.error(
|
|
29
|
+
logger.error(
|
|
30
|
+
"Error getting frame %d for video %s: %s",
|
|
31
|
+
frame_number,
|
|
32
|
+
video.video_hash,
|
|
33
|
+
e,
|
|
34
|
+
exc_info=True,
|
|
35
|
+
)
|
|
26
36
|
raise # Re-raise other exceptions
|
|
@@ -13,7 +13,10 @@ def _get_frame_path(video: "VideoFile", frame_number: int) -> Optional[Path]:
|
|
|
13
13
|
"""Constructs the expected path for a given frame number."""
|
|
14
14
|
target_dir = video.get_frame_dir_path() # Use IO helper
|
|
15
15
|
if not target_dir:
|
|
16
|
-
logger.warning(
|
|
16
|
+
logger.warning(
|
|
17
|
+
"Cannot get frame path for video %s: Frame directory not set.",
|
|
18
|
+
video.video_hash,
|
|
19
|
+
)
|
|
17
20
|
return None
|
|
18
21
|
|
|
19
22
|
frame_filename = f"frame_{frame_number:07d}.jpg"
|
|
@@ -14,7 +14,11 @@ def _get_frame_paths(video: "VideoFile") -> List[Path]:
|
|
|
14
14
|
"""Returns a sorted list of Path objects for extracted frame image files."""
|
|
15
15
|
frame_dir = _get_frame_dir_path(video)
|
|
16
16
|
if not frame_dir or not frame_dir.exists():
|
|
17
|
-
logger.warning(
|
|
17
|
+
logger.warning(
|
|
18
|
+
"Frame directory %s does not exist for video %s.",
|
|
19
|
+
frame_dir,
|
|
20
|
+
video.video_hash,
|
|
21
|
+
)
|
|
18
22
|
return []
|
|
19
23
|
|
|
20
24
|
frame_paths = list(frame_dir.glob("frame_*.jpg"))
|
|
@@ -22,7 +26,15 @@ def _get_frame_paths(video: "VideoFile") -> List[Path]:
|
|
|
22
26
|
try:
|
|
23
27
|
frame_paths.sort(key=lambda p: int(p.stem.split("_")[-1]))
|
|
24
28
|
except (ValueError, IndexError) as e:
|
|
25
|
-
logger.error(
|
|
26
|
-
|
|
29
|
+
logger.error(
|
|
30
|
+
"Error sorting frame paths in %s: %s. Found paths: %s",
|
|
31
|
+
frame_dir,
|
|
32
|
+
e,
|
|
33
|
+
[p.name for p in frame_paths],
|
|
34
|
+
exc_info=True,
|
|
35
|
+
)
|
|
36
|
+
logger.warning(
|
|
37
|
+
"Falling back to unsorted frame paths to preserve available data."
|
|
38
|
+
)
|
|
27
39
|
return frame_paths
|
|
28
40
|
return frame_paths
|
|
@@ -9,7 +9,9 @@ if TYPE_CHECKING:
|
|
|
9
9
|
logger = logging.getLogger(__name__)
|
|
10
10
|
|
|
11
11
|
|
|
12
|
-
def _get_frame_range(
|
|
12
|
+
def _get_frame_range(
|
|
13
|
+
video: "VideoFile", start_frame_number: int, end_frame_number: int
|
|
14
|
+
) -> "QuerySet[Frame]":
|
|
13
15
|
"""Gets a QuerySet of Frame objects within a specific range, ordered by frame number."""
|
|
14
16
|
from endoreg_db.models import Frame
|
|
15
17
|
|
|
@@ -20,7 +22,10 @@ def _get_frame_range(video: "VideoFile", start_frame_number: int, end_frame_numb
|
|
|
20
22
|
frame_number__lte=end_frame_number,
|
|
21
23
|
).order_by("frame_number")
|
|
22
24
|
except AttributeError:
|
|
23
|
-
logger.error(
|
|
25
|
+
logger.error(
|
|
26
|
+
"Could not access frame range for video %s via related manager.",
|
|
27
|
+
video.video_hash,
|
|
28
|
+
)
|
|
24
29
|
# Fallback query
|
|
25
30
|
return Frame.objects.filter(
|
|
26
31
|
video_file=video,
|
|
@@ -28,5 +33,12 @@ def _get_frame_range(video: "VideoFile", start_frame_number: int, end_frame_numb
|
|
|
28
33
|
frame_number__lte=end_frame_number,
|
|
29
34
|
).order_by("frame_number")
|
|
30
35
|
except Exception as e:
|
|
31
|
-
logger.error(
|
|
36
|
+
logger.error(
|
|
37
|
+
"Error getting frame range (%d-%d) for video %s: %s",
|
|
38
|
+
start_frame_number,
|
|
39
|
+
end_frame_number,
|
|
40
|
+
video.video_hash,
|
|
41
|
+
e,
|
|
42
|
+
exc_info=True,
|
|
43
|
+
)
|
|
32
44
|
return Frame.objects.none() # Return empty queryset on error
|
|
@@ -17,9 +17,14 @@ def _get_frames(video: "VideoFile") -> "QuerySet[Frame]":
|
|
|
17
17
|
# Access related manager directly
|
|
18
18
|
return video.frames.order_by("frame_number")
|
|
19
19
|
except AttributeError:
|
|
20
|
-
logger.error(
|
|
20
|
+
logger.error(
|
|
21
|
+
"Could not access frames for video %s. 'frames' related manager not found.",
|
|
22
|
+
video.video_hash,
|
|
23
|
+
)
|
|
21
24
|
# Fallback query
|
|
22
25
|
return Frame.objects.filter(video_file=video).order_by("frame_number")
|
|
23
26
|
except Exception as e:
|
|
24
|
-
logger.error(
|
|
27
|
+
logger.error(
|
|
28
|
+
"Error getting frames for video %s: %s", video.video_hash, e, exc_info=True
|
|
29
|
+
)
|
|
25
30
|
return Frame.objects.none() # Return empty queryset on error
|
|
@@ -32,29 +32,54 @@ def _initialize_frames(video: "VideoFile", frame_paths: Optional[List[Path]] = N
|
|
|
32
32
|
- On Failure: Does not change state (error is raised).
|
|
33
33
|
"""
|
|
34
34
|
from endoreg_db.models import Frame
|
|
35
|
-
from endoreg_db.models.media.video.video_file_frames._bulk_create_frames import
|
|
36
|
-
|
|
35
|
+
from endoreg_db.models.media.video.video_file_frames._bulk_create_frames import (
|
|
36
|
+
_bulk_create_frames,
|
|
37
|
+
)
|
|
38
|
+
from endoreg_db.models.media.video.video_file_frames._create_frame_object import (
|
|
39
|
+
_create_frame_object,
|
|
40
|
+
)
|
|
37
41
|
|
|
38
42
|
frames_to_create = []
|
|
39
43
|
num_expected_or_provided = 0
|
|
40
44
|
mark_as_extracted = False
|
|
41
45
|
|
|
42
46
|
if frame_paths:
|
|
43
|
-
logger.info(
|
|
47
|
+
logger.info(
|
|
48
|
+
"Initializing Frame objects based on %d provided paths for video %s.",
|
|
49
|
+
len(frame_paths),
|
|
50
|
+
video.video_hash,
|
|
51
|
+
)
|
|
44
52
|
mark_as_extracted = True
|
|
45
53
|
num_expected_or_provided = len(frame_paths)
|
|
46
|
-
for frame_path in tqdm(
|
|
54
|
+
for frame_path in tqdm(
|
|
55
|
+
frame_paths,
|
|
56
|
+
desc=f"Initializing Frames from Paths {video.video_hash}",
|
|
57
|
+
unit="frame",
|
|
58
|
+
):
|
|
47
59
|
try:
|
|
48
60
|
frame_number = int(frame_path.stem.split("_")[-1])
|
|
49
61
|
relative_path_str = frame_path.name
|
|
50
|
-
frames_to_create.append(
|
|
62
|
+
frames_to_create.append(
|
|
63
|
+
_create_frame_object(
|
|
64
|
+
video,
|
|
65
|
+
frame_number,
|
|
66
|
+
relative_path_str,
|
|
67
|
+
extracted=mark_as_extracted,
|
|
68
|
+
)
|
|
69
|
+
)
|
|
51
70
|
except (ValueError, IndexError) as e:
|
|
52
|
-
logger.warning(
|
|
71
|
+
logger.warning(
|
|
72
|
+
"Could not parse frame number from %s: %s", frame_path.name, e
|
|
73
|
+
)
|
|
53
74
|
continue
|
|
54
75
|
else:
|
|
55
76
|
expected_frame_count = video.frame_count
|
|
56
77
|
if expected_frame_count is None or expected_frame_count <= 0:
|
|
57
|
-
logger.warning(
|
|
78
|
+
logger.warning(
|
|
79
|
+
"Cannot initialize frames for video %s: Frame count is %s.",
|
|
80
|
+
video.video_hash,
|
|
81
|
+
expected_frame_count,
|
|
82
|
+
)
|
|
58
83
|
try:
|
|
59
84
|
state = video.get_or_create_state()
|
|
60
85
|
if state.frames_initialized or state.frame_count is not None:
|
|
@@ -62,53 +87,109 @@ def _initialize_frames(video: "VideoFile", frame_paths: Optional[List[Path]] = N
|
|
|
62
87
|
state.frame_count = None
|
|
63
88
|
state.save(update_fields=["frames_initialized", "frame_count"])
|
|
64
89
|
except Exception as state_e:
|
|
65
|
-
logger.error(
|
|
90
|
+
logger.error(
|
|
91
|
+
"Failed to reset state during empty initialization for video %s: %s",
|
|
92
|
+
video.video_hash,
|
|
93
|
+
state_e,
|
|
94
|
+
exc_info=True,
|
|
95
|
+
)
|
|
66
96
|
return
|
|
67
97
|
|
|
68
|
-
logger.info(
|
|
98
|
+
logger.info(
|
|
99
|
+
"Initializing %d expected Frame objects for video %s (is_extracted=False).",
|
|
100
|
+
expected_frame_count,
|
|
101
|
+
video.video_hash,
|
|
102
|
+
)
|
|
69
103
|
mark_as_extracted = False
|
|
70
104
|
num_expected_or_provided = expected_frame_count
|
|
71
|
-
for frame_number in tqdm(
|
|
105
|
+
for frame_number in tqdm(
|
|
106
|
+
range(expected_frame_count),
|
|
107
|
+
desc=f"Initializing Expected Frames {video.video_hash}",
|
|
108
|
+
unit="frame",
|
|
109
|
+
):
|
|
72
110
|
relative_path_str = f"frame_{frame_number:07d}.jpg"
|
|
73
|
-
frames_to_create.append(
|
|
111
|
+
frames_to_create.append(
|
|
112
|
+
_create_frame_object(
|
|
113
|
+
video, frame_number, relative_path_str, extracted=mark_as_extracted
|
|
114
|
+
)
|
|
115
|
+
)
|
|
74
116
|
|
|
75
117
|
if frames_to_create:
|
|
76
118
|
for attempt in range(5):
|
|
77
119
|
try:
|
|
78
120
|
_bulk_create_frames(video, frames_to_create)
|
|
79
121
|
num_created_or_ignored = len(frames_to_create)
|
|
80
|
-
logger.info(
|
|
122
|
+
logger.info(
|
|
123
|
+
"Bulk create attempted for %d Frame objects for video %s (ignore_conflicts=True).",
|
|
124
|
+
num_created_or_ignored,
|
|
125
|
+
video.video_hash,
|
|
126
|
+
)
|
|
81
127
|
|
|
82
128
|
if mark_as_extracted:
|
|
83
129
|
frame_numbers_to_update = [f.frame_number for f in frames_to_create]
|
|
84
130
|
if frame_numbers_to_update:
|
|
85
|
-
update_count = Frame.objects.filter(
|
|
131
|
+
update_count = Frame.objects.filter(
|
|
132
|
+
video=video,
|
|
133
|
+
frame_number__in=frame_numbers_to_update,
|
|
134
|
+
is_extracted=False,
|
|
135
|
+
).update(is_extracted=True)
|
|
86
136
|
if update_count > 0:
|
|
87
|
-
logger.info(
|
|
137
|
+
logger.info(
|
|
138
|
+
"Marked %d existing Frame objects as is_extracted=True for video %s.",
|
|
139
|
+
update_count,
|
|
140
|
+
video.video_hash,
|
|
141
|
+
)
|
|
88
142
|
|
|
89
143
|
try:
|
|
90
144
|
state = video.get_or_create_state()
|
|
91
145
|
state.frames_initialized = True
|
|
92
146
|
state.frame_count = num_expected_or_provided
|
|
93
147
|
state.save(update_fields=["frames_initialized", "frame_count"])
|
|
94
|
-
logger.info(
|
|
148
|
+
logger.info(
|
|
149
|
+
"Set frames_initialized=True and frame_count=%d for video %s.",
|
|
150
|
+
num_expected_or_provided,
|
|
151
|
+
video.video_hash,
|
|
152
|
+
)
|
|
95
153
|
except Exception as state_e:
|
|
96
|
-
logger.error(
|
|
97
|
-
|
|
154
|
+
logger.error(
|
|
155
|
+
"Failed to update state after frame initialization for video %s: %s",
|
|
156
|
+
video.video_hash,
|
|
157
|
+
state_e,
|
|
158
|
+
exc_info=True,
|
|
159
|
+
)
|
|
160
|
+
raise RuntimeError(
|
|
161
|
+
f"Failed to update state after frame initialization for video {video.video_hash}"
|
|
162
|
+
) from state_e
|
|
98
163
|
|
|
99
164
|
except OperationalError as e:
|
|
100
165
|
if "database is locked" in str(e):
|
|
101
|
-
logger.warning(
|
|
166
|
+
logger.warning(
|
|
167
|
+
"Database is locked, retrying frame initialization for video %s (attempt %d/5).",
|
|
168
|
+
video.video_hash,
|
|
169
|
+
attempt + 1,
|
|
170
|
+
)
|
|
102
171
|
time.sleep(2**attempt)
|
|
103
172
|
if attempt < 4:
|
|
104
173
|
continue
|
|
105
174
|
else:
|
|
106
|
-
raise RuntimeError(
|
|
107
|
-
|
|
108
|
-
|
|
175
|
+
raise RuntimeError(
|
|
176
|
+
f"Failed to initialize frames for video {video.video_hash}."
|
|
177
|
+
) from e
|
|
178
|
+
logger.error(
|
|
179
|
+
"Error initializing frames for video %s: %s",
|
|
180
|
+
video.video_hash,
|
|
181
|
+
e,
|
|
182
|
+
exc_info=True,
|
|
183
|
+
)
|
|
184
|
+
raise RuntimeError(
|
|
185
|
+
f"Failed to initialize frames for video {video.video_hash}."
|
|
186
|
+
) from e
|
|
109
187
|
|
|
110
188
|
else:
|
|
111
|
-
logger.warning(
|
|
189
|
+
logger.warning(
|
|
190
|
+
"No valid frames found/generated to initialize for video %s.",
|
|
191
|
+
video.video_hash,
|
|
192
|
+
)
|
|
112
193
|
try:
|
|
113
194
|
state = video.get_or_create_state()
|
|
114
195
|
if state.frames_initialized or state.frame_count is not None:
|
|
@@ -116,4 +197,9 @@ def _initialize_frames(video: "VideoFile", frame_paths: Optional[List[Path]] = N
|
|
|
116
197
|
state.frame_count = None
|
|
117
198
|
state.save(update_fields=["frames_initialized", "frame_count"])
|
|
118
199
|
except Exception as state_e:
|
|
119
|
-
logger.error(
|
|
200
|
+
logger.error(
|
|
201
|
+
"Failed to reset state during empty initialization for video %s: %s",
|
|
202
|
+
video.video_hash,
|
|
203
|
+
state_e,
|
|
204
|
+
exc_info=True,
|
|
205
|
+
)
|