endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/authz/auth.py +74 -0
- endoreg_db/authz/backends.py +168 -0
- endoreg_db/authz/management/commands/list_routes.py +18 -0
- endoreg_db/authz/middleware.py +83 -0
- endoreg_db/authz/permissions.py +127 -0
- endoreg_db/authz/policy.py +218 -0
- endoreg_db/authz/views_auth.py +66 -0
- endoreg_db/config/env.py +13 -8
- endoreg_db/data/__init__.py +8 -31
- endoreg_db/data/_examples/disease.yaml +55 -0
- endoreg_db/data/_examples/disease_classification.yaml +13 -0
- endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
- endoreg_db/data/_examples/event.yaml +64 -0
- endoreg_db/data/_examples/examination.yaml +72 -0
- endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
- endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
- endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
- endoreg_db/data/_examples/finding/complication.yaml +16 -0
- endoreg_db/data/_examples/finding/data.yaml +105 -0
- endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
- endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
- endoreg_db/data/_examples/finding/outcome.yaml +12 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
- endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
- endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
- endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
- endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
- endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
- endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
- endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
- endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
- endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/_examples/finding_type/data.yaml +43 -0
- endoreg_db/data/_examples/requirement/age.yaml +26 -0
- endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
- endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
- endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
- endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
- endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
- endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
- endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
- endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
- endoreg_db/data/_examples/requirement/gender.yaml +25 -0
- endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
- endoreg_db/data/_examples/requirement/medication.yaml +93 -0
- endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
- endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
- endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
- endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
- endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
- endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
- endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
- endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
- endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
- endoreg_db/data/requirement_set/_old_ +109 -0
- endoreg_db/data/requirement_set_type/data.yaml +21 -0
- endoreg_db/data/setup_config.yaml +4 -4
- endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
- endoreg_db/exceptions.py +5 -2
- endoreg_db/helpers/data_loader.py +1 -1
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
- endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
- endoreg_db/management/commands/import_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +1 -1
- endoreg_db/management/commands/init_default_ai_model.py +1 -1
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_requirement_data.py +60 -31
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/setup_endoreg_db.py +3 -3
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/migrations/0001_initial.py +1799 -1300
- endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
- endoreg_db/migrations/_old/0001_initial.py +1857 -0
- endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
- endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
- endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
- endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
- endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
- endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
- endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
- endoreg_db/models/__init__.py +78 -123
- endoreg_db/models/administration/__init__.py +21 -42
- endoreg_db/models/administration/ai/active_model.py +2 -2
- endoreg_db/models/administration/ai/ai_model.py +7 -6
- endoreg_db/models/administration/case/__init__.py +1 -15
- endoreg_db/models/administration/case/case.py +3 -3
- endoreg_db/models/administration/case/case_template/__init__.py +2 -14
- endoreg_db/models/administration/case/case_template/case_template.py +2 -124
- endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
- endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
- endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
- endoreg_db/models/administration/center/center.py +33 -19
- endoreg_db/models/administration/center/center_product.py +12 -9
- endoreg_db/models/administration/center/center_resource.py +25 -19
- endoreg_db/models/administration/center/center_shift.py +21 -17
- endoreg_db/models/administration/center/center_waste.py +16 -8
- endoreg_db/models/administration/person/__init__.py +2 -0
- endoreg_db/models/administration/person/employee/employee.py +10 -5
- endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
- endoreg_db/models/administration/person/employee/employee_type.py +12 -6
- endoreg_db/models/administration/person/examiner/examiner.py +13 -11
- endoreg_db/models/administration/person/patient/__init__.py +2 -0
- endoreg_db/models/administration/person/patient/patient.py +103 -100
- endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
- endoreg_db/models/administration/person/person.py +4 -0
- endoreg_db/models/administration/person/profession/__init__.py +8 -4
- endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
- endoreg_db/models/administration/product/product.py +20 -15
- endoreg_db/models/administration/product/product_material.py +17 -18
- endoreg_db/models/administration/product/product_weight.py +12 -8
- endoreg_db/models/administration/product/reference_product.py +23 -55
- endoreg_db/models/administration/qualification/qualification.py +7 -3
- endoreg_db/models/administration/qualification/qualification_type.py +7 -3
- endoreg_db/models/administration/shift/scheduled_days.py +8 -5
- endoreg_db/models/administration/shift/shift.py +16 -12
- endoreg_db/models/administration/shift/shift_type.py +23 -31
- endoreg_db/models/label/__init__.py +7 -8
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
- endoreg_db/models/label/label.py +15 -15
- endoreg_db/models/label/label_set.py +19 -6
- endoreg_db/models/label/label_type.py +1 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
- endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
- endoreg_db/models/label/video_segmentation_label.py +4 -0
- endoreg_db/models/label/video_segmentation_labelset.py +4 -3
- endoreg_db/models/media/frame/frame.py +22 -22
- endoreg_db/models/media/pdf/raw_pdf.py +110 -182
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +48 -56
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +150 -108
- endoreg_db/models/media/video/video_file_ai.py +288 -74
- endoreg_db/models/media/video/video_file_anonymize.py +38 -38
- endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
- endoreg_db/models/media/video/video_file_io.py +109 -62
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
- endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
- endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
- endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
- endoreg_db/models/media/video/video_file_segments.py +24 -17
- endoreg_db/models/media/video/video_metadata.py +19 -35
- endoreg_db/models/media/video/video_processing.py +96 -95
- endoreg_db/models/medical/contraindication/__init__.py +13 -3
- endoreg_db/models/medical/disease.py +22 -16
- endoreg_db/models/medical/event.py +31 -18
- endoreg_db/models/medical/examination/__init__.py +13 -6
- endoreg_db/models/medical/examination/examination.py +17 -18
- endoreg_db/models/medical/examination/examination_indication.py +26 -25
- endoreg_db/models/medical/examination/examination_time.py +16 -6
- endoreg_db/models/medical/examination/examination_time_type.py +9 -6
- endoreg_db/models/medical/examination/examination_type.py +3 -4
- endoreg_db/models/medical/finding/finding.py +38 -39
- endoreg_db/models/medical/finding/finding_classification.py +37 -48
- endoreg_db/models/medical/finding/finding_intervention.py +27 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +20 -26
- endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
- endoreg_db/models/medical/laboratory/lab_value.py +62 -91
- endoreg_db/models/medical/medication/medication.py +22 -10
- endoreg_db/models/medical/medication/medication_indication.py +29 -3
- endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
- endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
- endoreg_db/models/medical/medication/medication_schedule.py +27 -16
- endoreg_db/models/medical/organ/__init__.py +15 -12
- endoreg_db/models/medical/patient/medication_examples.py +1 -5
- endoreg_db/models/medical/patient/patient_disease.py +20 -23
- endoreg_db/models/medical/patient/patient_event.py +19 -22
- endoreg_db/models/medical/patient/patient_examination.py +48 -54
- endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
- endoreg_db/models/medical/patient/patient_finding.py +122 -139
- endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
- endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
- endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
- endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
- endoreg_db/models/medical/patient/patient_medication.py +27 -38
- endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
- endoreg_db/models/medical/risk/risk.py +7 -6
- endoreg_db/models/medical/risk/risk_type.py +8 -5
- endoreg_db/models/metadata/model_meta.py +60 -29
- endoreg_db/models/metadata/model_meta_logic.py +125 -18
- endoreg_db/models/metadata/pdf_meta.py +19 -24
- endoreg_db/models/metadata/sensitive_meta.py +102 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +192 -173
- endoreg_db/models/metadata/video_meta.py +51 -31
- endoreg_db/models/metadata/video_prediction_logic.py +16 -23
- endoreg_db/models/metadata/video_prediction_meta.py +29 -33
- endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
- endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
- endoreg_db/models/other/emission/emission_factor.py +18 -8
- endoreg_db/models/other/gender.py +10 -5
- endoreg_db/models/other/information_source.py +25 -25
- endoreg_db/models/other/material.py +9 -5
- endoreg_db/models/other/resource.py +6 -4
- endoreg_db/models/other/tag.py +10 -5
- endoreg_db/models/other/transport_route.py +13 -8
- endoreg_db/models/other/unit.py +10 -6
- endoreg_db/models/other/waste.py +6 -5
- endoreg_db/models/requirement/requirement.py +580 -272
- endoreg_db/models/requirement/requirement_error.py +85 -0
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
- endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
- endoreg_db/models/requirement/requirement_operator.py +36 -33
- endoreg_db/models/requirement/requirement_set.py +74 -57
- endoreg_db/models/state/__init__.py +4 -4
- endoreg_db/models/state/abstract.py +2 -2
- endoreg_db/models/state/anonymization.py +12 -0
- endoreg_db/models/state/audit_ledger.py +46 -47
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +40 -46
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +58 -53
- endoreg_db/models/upload_job.py +32 -55
- endoreg_db/models/utils.py +1 -2
- endoreg_db/root_urls.py +21 -2
- endoreg_db/serializers/__init__.py +0 -2
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/meta/report_meta.py +1 -1
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/file_overview.py +11 -99
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/services/anonymization.py +75 -73
- endoreg_db/services/lookup_service.py +37 -24
- endoreg_db/services/pdf_import.py +166 -68
- endoreg_db/services/storage_aware_video_processor.py +140 -114
- endoreg_db/services/video_import.py +193 -283
- endoreg_db/urls/__init__.py +7 -20
- endoreg_db/urls/media.py +108 -67
- endoreg_db/urls/root_urls.py +29 -0
- endoreg_db/utils/__init__.py +15 -5
- endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
- endoreg_db/utils/case_generator/__init__.py +3 -0
- endoreg_db/utils/dataloader.py +88 -16
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
- endoreg_db/utils/setup_config.py +8 -5
- endoreg_db/utils/storage.py +115 -0
- endoreg_db/utils/validate_endo_roi.py +8 -2
- endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
- endoreg_db/views/__init__.py +0 -10
- endoreg_db/views/anonymization/media_management.py +198 -163
- endoreg_db/views/anonymization/overview.py +4 -1
- endoreg_db/views/anonymization/validate.py +174 -40
- endoreg_db/views/media/__init__.py +2 -0
- endoreg_db/views/media/pdf_media.py +131 -152
- endoreg_db/views/media/sensitive_metadata.py +46 -6
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/media/video_segments.py +2 -3
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/pdf/pdf_stream.py +20 -21
- endoreg_db/views/pdf/reimport.py +11 -32
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +17 -3
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +0 -2
- endoreg_db/views/video/correction.py +2 -2
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +341 -245
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/media/video/video_file_frames.py +0 -0
- endoreg_db/models/metadata/frame_ocr_result.py +0 -0
- endoreg_db/models/rule/__init__.py +0 -13
- endoreg_db/models/rule/rule.py +0 -27
- endoreg_db/models/rule/rule_applicator.py +0 -224
- endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
- endoreg_db/models/rule/rule_type.py +0 -20
- endoreg_db/models/rule/ruleset.py +0 -17
- endoreg_db/serializers/video/video_metadata.py +0 -105
- endoreg_db/urls/report.py +0 -48
- endoreg_db/urls/video.py +0 -61
- endoreg_db/utils/case_generator/case_generator.py +0 -159
- endoreg_db/utils/case_generator/utils.py +0 -30
- endoreg_db/views/report/__init__.py +0 -9
- endoreg_db/views/report/report_list.py +0 -112
- endoreg_db/views/report/report_with_secure_url.py +0 -28
- endoreg_db/views/report/start_examination.py +0 -7
- endoreg_db/views.py +0 -0
- /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
- /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
- /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
- /endoreg_db/{models/media/video/refactor_plan.md → views/pdf/pdf_stream_views.py} +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from rest_framework import serializers
|
|
2
2
|
import logging
|
|
3
|
-
|
|
4
3
|
from ...models import SensitiveMeta
|
|
5
4
|
|
|
6
5
|
logger = logging.getLogger(__name__)
|
|
@@ -10,153 +9,99 @@ class SensitiveMetaDetailSerializer(serializers.ModelSerializer):
|
|
|
10
9
|
Serializer for displaying SensitiveMeta details with verification state.
|
|
11
10
|
Includes all relevant fields for annotation and verification.
|
|
12
11
|
"""
|
|
13
|
-
|
|
12
|
+
|
|
14
13
|
# State verification fields
|
|
15
14
|
is_verified = serializers.SerializerMethodField()
|
|
16
15
|
dob_verified = serializers.SerializerMethodField()
|
|
17
16
|
names_verified = serializers.SerializerMethodField()
|
|
18
|
-
|
|
19
|
-
# Related fields
|
|
17
|
+
|
|
18
|
+
# Related fields
|
|
20
19
|
center_name = serializers.CharField(source="center.name", read_only=True)
|
|
21
20
|
patient_gender_name = serializers.CharField(source="patient_gender.name", read_only=True)
|
|
22
|
-
|
|
23
|
-
# Examiner information
|
|
24
21
|
examiners_display = serializers.SerializerMethodField()
|
|
25
|
-
|
|
26
|
-
# Formatted
|
|
22
|
+
|
|
23
|
+
# Formatted display fields
|
|
27
24
|
patient_dob_display = serializers.SerializerMethodField()
|
|
28
25
|
examination_date_display = serializers.SerializerMethodField()
|
|
29
|
-
|
|
30
|
-
# Hash displays (last 8
|
|
26
|
+
|
|
27
|
+
# Hash displays (last 8 chars)
|
|
31
28
|
patient_hash_display = serializers.SerializerMethodField()
|
|
32
29
|
examination_hash_display = serializers.SerializerMethodField()
|
|
33
30
|
|
|
31
|
+
# Text fields
|
|
32
|
+
text = serializers.SerializerMethodField()
|
|
33
|
+
anonymized_text = serializers.SerializerMethodField()
|
|
34
|
+
|
|
34
35
|
class Meta:
|
|
35
36
|
model = SensitiveMeta
|
|
36
37
|
fields = [
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
38
|
+
"id",
|
|
39
|
+
"casenumber",
|
|
40
|
+
"patient_first_name",
|
|
41
|
+
"patient_last_name",
|
|
42
|
+
"patient_dob",
|
|
43
|
+
"patient_dob_display",
|
|
44
|
+
"examination_date",
|
|
45
|
+
"examination_date_display",
|
|
46
|
+
"examination_time",
|
|
47
|
+
"center_name",
|
|
48
|
+
"patient_gender_name",
|
|
49
|
+
"endoscope_type",
|
|
50
|
+
"endoscope_sn",
|
|
51
|
+
"patient_hash_display",
|
|
52
|
+
"examination_hash_display",
|
|
53
|
+
"examiners_display",
|
|
54
|
+
"is_verified",
|
|
55
|
+
"dob_verified",
|
|
56
|
+
"names_verified",
|
|
57
|
+
"text",
|
|
58
|
+
"anonymized_text",
|
|
59
|
+
"external_id",
|
|
60
|
+
"external_id_origin"
|
|
54
61
|
]
|
|
55
62
|
read_only_fields = [
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
63
|
+
"id",
|
|
64
|
+
"patient_hash_display",
|
|
65
|
+
"examination_hash_display",
|
|
59
66
|
]
|
|
60
67
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
Returns:
|
|
66
|
-
bool: True if the instance is verified; False if the verification attribute is missing.
|
|
67
|
-
|
|
68
|
-
Raises:
|
|
69
|
-
Exception: Propagates unexpected exceptions after logging.
|
|
70
|
-
"""
|
|
71
|
-
try:
|
|
72
|
-
return obj.is_verified
|
|
73
|
-
except AttributeError:
|
|
74
|
-
return False
|
|
75
|
-
except Exception as e:
|
|
76
|
-
logger.exception(f"Unexpected error in get_is_verified for SensitiveMeta {getattr(obj, 'pk', None)}: {e}")
|
|
77
|
-
raise
|
|
78
|
-
|
|
79
|
-
def get_dob_verified(self, obj):
|
|
80
|
-
"""
|
|
81
|
-
Return the date of birth verification status for the given object.
|
|
82
|
-
|
|
83
|
-
Returns:
|
|
84
|
-
bool: True if the date of birth is verified; otherwise, False if unavailable or on error.
|
|
85
|
-
"""
|
|
86
|
-
try:
|
|
87
|
-
return obj.state.dob_verified
|
|
88
|
-
except Exception:
|
|
89
|
-
return False
|
|
90
|
-
|
|
91
|
-
def get_names_verified(self, obj):
|
|
92
|
-
"""
|
|
93
|
-
Return whether the patient's names have been verified.
|
|
94
|
-
|
|
95
|
-
Returns:
|
|
96
|
-
bool: True if the names are verified; False if not verified or if an error occurs.
|
|
97
|
-
"""
|
|
98
|
-
try:
|
|
99
|
-
return obj.state.names_verified
|
|
100
|
-
except Exception:
|
|
101
|
-
return False
|
|
68
|
+
# --- Verification getters ---
|
|
69
|
+
def get_is_verified(self, obj): return getattr(obj, "is_verified", False)
|
|
70
|
+
def get_dob_verified(self, obj): return getattr(getattr(obj, "state", None), "dob_verified", False)
|
|
71
|
+
def get_names_verified(self, obj): return getattr(getattr(obj, "state", None), "names_verified", False)
|
|
102
72
|
|
|
73
|
+
# --- Examiner display ---
|
|
103
74
|
def get_examiners_display(self, obj):
|
|
104
|
-
"""
|
|
105
|
-
Return a list of examiner full names for the given SensitiveMeta instance.
|
|
106
|
-
|
|
107
|
-
Returns:
|
|
108
|
-
list[str]: List of examiner names in "First Last" format, or an empty list if unavailable.
|
|
109
|
-
"""
|
|
110
75
|
try:
|
|
111
|
-
if obj.pk
|
|
112
|
-
examiners = obj.examiners.all()
|
|
113
|
-
return [f"{e.first_name} {e.last_name}" for e in examiners]
|
|
114
|
-
return []
|
|
76
|
+
return [f"{e.first_name} {e.last_name}" for e in obj.examiners.all()] if obj.pk else []
|
|
115
77
|
except Exception as e:
|
|
116
|
-
logger.warning(f"Error
|
|
78
|
+
logger.warning(f"Error fetching examiners for SensitiveMeta {obj.pk}: {e}")
|
|
117
79
|
return []
|
|
118
80
|
|
|
81
|
+
# --- Date formatters ---
|
|
119
82
|
def get_patient_dob_display(self, obj):
|
|
120
|
-
""
|
|
121
|
-
Return the patient's date of birth formatted as "YYYY-MM-DD" for display.
|
|
122
|
-
|
|
123
|
-
Returns:
|
|
124
|
-
str or None: Formatted date string if available, otherwise None.
|
|
125
|
-
"""
|
|
126
|
-
if obj.patient_dob:
|
|
127
|
-
return obj.patient_dob.strftime("%Y-%m-%d")
|
|
128
|
-
return None
|
|
83
|
+
return obj.patient_dob.strftime("%Y-%m-%d") if obj.patient_dob else None
|
|
129
84
|
|
|
130
85
|
def get_examination_date_display(self, obj):
|
|
131
|
-
""
|
|
132
|
-
Return the examination date formatted as "YYYY-MM-DD" for display.
|
|
133
|
-
|
|
134
|
-
Returns:
|
|
135
|
-
str or None: Formatted examination date string, or None if not set.
|
|
136
|
-
"""
|
|
137
|
-
if obj.examination_date:
|
|
138
|
-
return obj.examination_date.strftime("%Y-%m-%d")
|
|
139
|
-
return None
|
|
86
|
+
return obj.examination_date.strftime("%Y-%m-%d") if obj.examination_date else None
|
|
140
87
|
|
|
88
|
+
# --- Hash short forms ---
|
|
141
89
|
def get_patient_hash_display(self, obj):
|
|
142
|
-
""
|
|
143
|
-
Return the last eight characters of the patient's hash, prefixed with ellipsis, or None if not available.
|
|
144
|
-
|
|
145
|
-
Returns:
|
|
146
|
-
str or None: Truncated patient hash for display, or None if the hash is not set.
|
|
147
|
-
"""
|
|
148
|
-
if obj.patient_hash:
|
|
149
|
-
return f"...{obj.patient_hash[-8:]}"
|
|
150
|
-
return None
|
|
90
|
+
return f"...{obj.patient_hash[-8:]}" if obj.patient_hash else None
|
|
151
91
|
|
|
152
92
|
def get_examination_hash_display(self, obj):
|
|
153
|
-
""
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
93
|
+
return f"...{obj.examination_hash[-8:]}" if obj.examination_hash else None
|
|
94
|
+
|
|
95
|
+
# --- Text fields ---
|
|
96
|
+
def get_text(self, obj):
|
|
97
|
+
return obj.text if isinstance(obj.text, str) else None
|
|
98
|
+
|
|
99
|
+
def get_anonymized_text(self, obj):
|
|
100
|
+
return obj.anonymized_text if isinstance(obj.anonymized_text, str) else None
|
|
101
|
+
|
|
102
|
+
def get_external_id(self, obj) -> str | None:
|
|
103
|
+
return obj.external_id if isinstance(obj.external_id, str) else None
|
|
104
|
+
|
|
105
|
+
def get_external_id_origin(self, obj) -> str | None:
|
|
106
|
+
return obj.external_id_origin if isinstance(obj.external_id_origin, str) else None
|
|
162
107
|
|
|
@@ -3,12 +3,8 @@ from typing import TYPE_CHECKING
|
|
|
3
3
|
from rest_framework import serializers
|
|
4
4
|
|
|
5
5
|
from endoreg_db.models.media import RawPdfFile, VideoFile
|
|
6
|
-
from endoreg_db.models.state.
|
|
7
|
-
|
|
8
|
-
)
|
|
9
|
-
from endoreg_db.models.state.video import (
|
|
10
|
-
AnonymizationStatus as VideoAnonymizationStatus,
|
|
11
|
-
)
|
|
6
|
+
from endoreg_db.models.state.anonymization import AnonymizationState as PdfAnonymizationState
|
|
7
|
+
from endoreg_db.models.state.anonymization import AnonymizationState as VideoAnonymizationState
|
|
12
8
|
|
|
13
9
|
if TYPE_CHECKING:
|
|
14
10
|
pass
|
|
@@ -28,10 +24,6 @@ class FileOverviewSerializer(serializers.Serializer):
|
|
|
28
24
|
anonymizationStatus = serializers.CharField(read_only=True)
|
|
29
25
|
annotationStatus = serializers.CharField(read_only=True)
|
|
30
26
|
createdAt = serializers.DateTimeField(read_only=True)
|
|
31
|
-
text = serializers.CharField(required=False, allow_blank=True, read_only=True)
|
|
32
|
-
anonymizedText = serializers.CharField(
|
|
33
|
-
required=False, allow_blank=True, read_only=True
|
|
34
|
-
)
|
|
35
27
|
|
|
36
28
|
# --- converting DB objects to that shape -----------------------
|
|
37
29
|
def to_representation(self, instance):
|
|
@@ -62,110 +54,32 @@ class FileOverviewSerializer(serializers.Serializer):
|
|
|
62
54
|
)
|
|
63
55
|
|
|
64
56
|
# ------- anonymization status using VideoState model
|
|
65
|
-
vs = instance.
|
|
57
|
+
vs = instance.get_or_create_state()
|
|
66
58
|
anonym_status = (
|
|
67
|
-
vs.anonymization_status if vs else
|
|
59
|
+
vs.anonymization_status if vs else VideoAnonymizationState.NOT_STARTED
|
|
68
60
|
)
|
|
69
61
|
|
|
70
62
|
# ------- annotation status (validated label segments)
|
|
71
63
|
if instance.label_video_segments.filter(state__is_validated=True).exists():
|
|
72
|
-
annot_status = "
|
|
64
|
+
annot_status = "validated"
|
|
73
65
|
else:
|
|
74
66
|
annot_status = "not_started"
|
|
75
67
|
|
|
76
|
-
# ------- Extract text from sensitive_meta for videos
|
|
77
|
-
if instance.sensitive_meta:
|
|
78
|
-
sm = instance.sensitive_meta
|
|
79
|
-
# Create a structured text representation from sensitive meta
|
|
80
|
-
text_parts = []
|
|
81
|
-
|
|
82
|
-
# Patient information
|
|
83
|
-
if sm.patient_first_name or sm.patient_last_name:
|
|
84
|
-
patient_name = f"{sm.patient_first_name or ''} {sm.patient_last_name or ''}".strip()
|
|
85
|
-
text_parts.append(f"Patient: {patient_name}")
|
|
86
|
-
|
|
87
|
-
if sm.patient_dob:
|
|
88
|
-
text_parts.append(f"Date of Birth: {sm.patient_dob.date()}")
|
|
89
|
-
|
|
90
|
-
if sm.patient_gender:
|
|
91
|
-
text_parts.append(f"Gender: {sm.patient_gender}")
|
|
92
|
-
|
|
93
|
-
# Examination information
|
|
94
|
-
if sm.examination_date:
|
|
95
|
-
text_parts.append(f"Examination Date: {sm.examination_date}")
|
|
96
|
-
|
|
97
|
-
if sm.center:
|
|
98
|
-
text_parts.append(f"Center: {sm.center.name}")
|
|
99
|
-
|
|
100
|
-
# Equipment information
|
|
101
|
-
if sm.endoscope_type:
|
|
102
|
-
text_parts.append(f"Endoscope Type: {sm.endoscope_type}")
|
|
103
|
-
|
|
104
|
-
if sm.endoscope_sn:
|
|
105
|
-
text_parts.append(f"Endoscope SN: {sm.endoscope_sn}")
|
|
106
|
-
|
|
107
|
-
# Examiner information
|
|
108
|
-
if sm.pk: # Only if saved
|
|
109
|
-
try:
|
|
110
|
-
examiners = list(sm.examiners.all())
|
|
111
|
-
if examiners:
|
|
112
|
-
examiner_names = ", ".join(str(e) for e in examiners)
|
|
113
|
-
text_parts.append(f"Examiners: {examiner_names}")
|
|
114
|
-
except Exception:
|
|
115
|
-
pass # Ignore examiner lookup errors
|
|
116
|
-
|
|
117
|
-
text = "\n".join(text_parts)
|
|
118
|
-
|
|
119
|
-
# Create anonymized version by replacing sensitive data
|
|
120
|
-
anonym_text = text
|
|
121
|
-
if sm.patient_first_name:
|
|
122
|
-
anonym_text = anonym_text.replace(
|
|
123
|
-
sm.patient_first_name, "[FIRST_NAME]"
|
|
124
|
-
)
|
|
125
|
-
if sm.patient_last_name:
|
|
126
|
-
anonym_text = anonym_text.replace(
|
|
127
|
-
sm.patient_last_name, "[LAST_NAME]"
|
|
128
|
-
)
|
|
129
|
-
if sm.patient_dob:
|
|
130
|
-
anonym_text = anonym_text.replace(
|
|
131
|
-
str(sm.patient_dob.date()), "[DOB]"
|
|
132
|
-
)
|
|
133
|
-
if sm.endoscope_sn:
|
|
134
|
-
anonym_text = anonym_text.replace(sm.endoscope_sn, "[ENDOSCOPE_SN]")
|
|
135
|
-
|
|
136
|
-
# Replace examiner names if available
|
|
137
|
-
if sm.pk:
|
|
138
|
-
try:
|
|
139
|
-
examiners = list(sm.examiners.all())
|
|
140
|
-
for examiner in examiners:
|
|
141
|
-
anonym_text = anonym_text.replace(
|
|
142
|
-
str(examiner), "[EXAMINER]"
|
|
143
|
-
)
|
|
144
|
-
except Exception:
|
|
145
|
-
pass
|
|
146
|
-
|
|
147
68
|
elif isinstance(instance, RawPdfFile):
|
|
148
|
-
instance: RawPdfFile
|
|
149
69
|
media_type = "pdf"
|
|
150
70
|
created_at = instance.date_created
|
|
151
71
|
filename = instance.file.name.split("/")[-1] if instance.file else "unknown"
|
|
152
72
|
|
|
153
|
-
# ------- anonymization status using RawPdfState model
|
|
154
|
-
|
|
73
|
+
# ------- anonymization status using RawPdfState model
|
|
74
|
+
rps = instance.get_or_create_state()
|
|
155
75
|
anonym_status = (
|
|
156
|
-
|
|
76
|
+
rps.anonymization_status if rps else PdfAnonymizationState.NOT_STARTED
|
|
157
77
|
)
|
|
158
78
|
|
|
159
|
-
#
|
|
79
|
+
# ------- annotation status (not applicable for PDFs)
|
|
160
80
|
annot_status = (
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
else "not_started"
|
|
164
|
-
)
|
|
165
|
-
|
|
166
|
-
# Extract text content from PDF
|
|
167
|
-
text = instance.text or ""
|
|
168
|
-
anonym_text = instance.anonymized_text or ""
|
|
81
|
+
PdfAnonymizationState.VALIDATED if rps.anonymization_validated else PdfAnonymizationState.NOT_STARTED
|
|
82
|
+
)
|
|
169
83
|
|
|
170
84
|
else: # shouldn't happen
|
|
171
85
|
raise TypeError(f"Unsupported instance for overview: {type(instance)}")
|
|
@@ -177,6 +91,4 @@ class FileOverviewSerializer(serializers.Serializer):
|
|
|
177
91
|
"anonymizationStatus": anonym_status,
|
|
178
92
|
"annotationStatus": annot_status,
|
|
179
93
|
"createdAt": created_at,
|
|
180
|
-
"text": text,
|
|
181
|
-
"anonymizedText": anonym_text,
|
|
182
94
|
}
|
|
@@ -1,29 +1,99 @@
|
|
|
1
|
-
from
|
|
2
|
-
|
|
1
|
+
from rest_framework import serializers
|
|
2
|
+
|
|
3
|
+
from endoreg_db.models.requirement.requirement_set import RequirementSet
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class RequirementSetSerializer(serializers.ModelSerializer):
|
|
7
|
+
"""
|
|
8
|
+
Serializer for RequirementSet model with optional tag field.
|
|
9
|
+
|
|
10
|
+
This serializer provides a flexible representation of requirement sets,
|
|
11
|
+
including role-based tags for filtering (e.g., "Gastroenterologist", "Student").
|
|
12
|
+
|
|
13
|
+
Fields:
|
|
14
|
+
id: Primary key
|
|
15
|
+
name: Display name of the requirement set
|
|
16
|
+
description: Optional description text
|
|
17
|
+
requirement_set_type: Type classification
|
|
18
|
+
tags: List of tag names (optional, read-only)
|
|
19
|
+
"""
|
|
20
|
+
|
|
21
|
+
tags = serializers.SlugRelatedField(
|
|
22
|
+
many=True, read_only=True, slug_field="name", required=False
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
requirement_set_type = serializers.CharField(
|
|
26
|
+
source="requirement_set_type.name", read_only=True, allow_null=True
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
class Meta:
|
|
30
|
+
model = RequirementSet
|
|
31
|
+
fields = ["id", "name", "description", "requirement_set_type", "tags"]
|
|
32
|
+
|
|
33
|
+
def to_representation(self, instance):
|
|
34
|
+
"""
|
|
35
|
+
Customize representation to exclude tags field if empty or not prefetched.
|
|
36
|
+
|
|
37
|
+
This prevents N+1 query issues and keeps the response clean when tags
|
|
38
|
+
aren't needed or weren't prefetched in the queryset.
|
|
39
|
+
"""
|
|
40
|
+
representation = super().to_representation(instance)
|
|
41
|
+
|
|
42
|
+
# Only include tags if they exist and were prefetched
|
|
43
|
+
if not representation.get("tags"):
|
|
44
|
+
representation.pop("tags", None)
|
|
45
|
+
|
|
46
|
+
return representation
|
|
47
|
+
|
|
3
48
|
|
|
4
49
|
def requirement_set_to_dict(requirement_set: RequirementSet) -> dict:
|
|
5
50
|
"""
|
|
6
51
|
Convert a RequirementSet instance to a dictionary representation.
|
|
7
|
-
|
|
52
|
+
|
|
53
|
+
This function performs additional queries to fetch related data including
|
|
54
|
+
requirements and linked sets. Use RequirementSetSerializer for simpler
|
|
55
|
+
representations without the overhead of additional queries.
|
|
56
|
+
|
|
8
57
|
Args:
|
|
9
|
-
requirement_set
|
|
10
|
-
|
|
58
|
+
requirement_set: The RequirementSet instance to convert.
|
|
59
|
+
|
|
11
60
|
Returns:
|
|
12
|
-
|
|
61
|
+
Dictionary representation of the RequirementSet with full nested details:
|
|
62
|
+
- id, name, description: Basic metadata
|
|
63
|
+
- requirements: List of requirement dictionaries
|
|
64
|
+
- links: List of linked requirement set data
|
|
65
|
+
- tags: List of tag names (if any exist)
|
|
13
66
|
"""
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
"
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
)
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
"
|
|
26
|
-
"
|
|
27
|
-
"
|
|
28
|
-
"
|
|
29
|
-
|
|
67
|
+
# Fetch the requirement set with all related data to avoid N+1 queries
|
|
68
|
+
requirement_set_full = (
|
|
69
|
+
RequirementSet.objects.select_related("requirement_set_type")
|
|
70
|
+
.prefetch_related("requirements", "links_to_sets", "tags")
|
|
71
|
+
.get(pk=requirement_set.pk)
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
# Get linked requirement sets
|
|
75
|
+
linked_sets = requirement_set_full.links_to_sets.all()
|
|
76
|
+
|
|
77
|
+
result = {
|
|
78
|
+
"id": requirement_set_full.pk,
|
|
79
|
+
"name": requirement_set_full.name,
|
|
80
|
+
"description": requirement_set_full.description or "",
|
|
81
|
+
"requirement_set_type": requirement_set_full.requirement_set_type.name
|
|
82
|
+
if requirement_set_full.requirement_set_type
|
|
83
|
+
else None,
|
|
84
|
+
"requirements": [
|
|
85
|
+
{"id": req.pk, "name": req.name, "description": req.description or ""}
|
|
86
|
+
for req in requirement_set_full.requirements.all()
|
|
87
|
+
],
|
|
88
|
+
"linked_sets": [
|
|
89
|
+
{"id": link.pk, "name": link.name, "description": link.description or ""}
|
|
90
|
+
for link in linked_sets
|
|
91
|
+
],
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
# Add tags if they exist
|
|
95
|
+
tags = list(requirement_set_full.tags.values_list("name", flat=True))
|
|
96
|
+
if tags:
|
|
97
|
+
result["tags"] = tags
|
|
98
|
+
|
|
99
|
+
return result
|
|
@@ -10,7 +10,8 @@ from django.conf import settings
|
|
|
10
10
|
from typing import TYPE_CHECKING
|
|
11
11
|
from rest_framework.exceptions import ValidationError
|
|
12
12
|
if TYPE_CHECKING:
|
|
13
|
-
from endoreg_db.models import
|
|
13
|
+
from endoreg_db.models import VideoFile
|
|
14
|
+
|
|
14
15
|
class VideoFileSerializer(serializers.ModelSerializer):
|
|
15
16
|
"""
|
|
16
17
|
Serializer that dynamically handles video retrieval and streaming.
|
|
@@ -4,7 +4,10 @@ Video Processing History Serializer
|
|
|
4
4
|
Serializes VideoProcessingHistory model for API responses.
|
|
5
5
|
Created as part of Phase 1.1: Video Correction API Endpoints.
|
|
6
6
|
"""
|
|
7
|
+
from collections.abc import Mapping
|
|
8
|
+
|
|
7
9
|
from rest_framework import serializers
|
|
10
|
+
|
|
8
11
|
from endoreg_db.models import VideoProcessingHistory
|
|
9
12
|
|
|
10
13
|
|
|
@@ -16,8 +19,8 @@ class VideoProcessingHistorySerializer(serializers.ModelSerializer):
|
|
|
16
19
|
with download URLs for processed files.
|
|
17
20
|
"""
|
|
18
21
|
download_url = serializers.SerializerMethodField()
|
|
19
|
-
operation_display = serializers.
|
|
20
|
-
status_display = serializers.
|
|
22
|
+
operation_display = serializers.SerializerMethodField()
|
|
23
|
+
status_display = serializers.SerializerMethodField()
|
|
21
24
|
duration = serializers.ReadOnlyField()
|
|
22
25
|
is_complete = serializers.ReadOnlyField()
|
|
23
26
|
|
|
@@ -57,13 +60,24 @@ class VideoProcessingHistorySerializer(serializers.ModelSerializer):
|
|
|
57
60
|
|
|
58
61
|
# Build URL to download endpoint (to be implemented)
|
|
59
62
|
# Format: /api/media/processed-videos/{video_id}/{history_id}/
|
|
60
|
-
|
|
63
|
+
context = self.context if isinstance(self.context, Mapping) else None
|
|
64
|
+
request = context.get('request') if context else None
|
|
61
65
|
if request:
|
|
62
66
|
return request.build_absolute_uri(
|
|
63
67
|
f'/api/media/processed-videos/{obj.video.id}/{obj.id}/'
|
|
64
68
|
)
|
|
65
69
|
|
|
66
70
|
return f'/api/media/processed-videos/{obj.video.id}/{obj.id}/'
|
|
71
|
+
|
|
72
|
+
def get_operation_display(self, obj) -> str:
|
|
73
|
+
display = getattr(obj, "get_operation_display", None)
|
|
74
|
+
result = display() if callable(display) else obj.operation
|
|
75
|
+
return str(result)
|
|
76
|
+
|
|
77
|
+
def get_status_display(self, obj) -> str:
|
|
78
|
+
display = getattr(obj, "get_status_display", None)
|
|
79
|
+
result = display() if callable(display) else obj.status
|
|
80
|
+
return str(result)
|
|
67
81
|
|
|
68
82
|
def validate_operation(self, value):
|
|
69
83
|
"""
|
|
@@ -120,8 +134,9 @@ class VideoProcessingHistorySerializer(serializers.ModelSerializer):
|
|
|
120
134
|
"""
|
|
121
135
|
if not isinstance(value, dict):
|
|
122
136
|
raise serializers.ValidationError("config must be a dictionary")
|
|
123
|
-
|
|
124
|
-
|
|
137
|
+
|
|
138
|
+
initial = self.initial_data if isinstance(self.initial_data, Mapping) else {}
|
|
139
|
+
operation = initial.get('operation')
|
|
125
140
|
|
|
126
141
|
# Validate masking config
|
|
127
142
|
if operation == VideoProcessingHistory.OPERATION_MASKING:
|