endoreg-db 0.8.6.1__py3-none-any.whl → 0.8.8.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/authz/auth.py +74 -0
- endoreg_db/authz/backends.py +168 -0
- endoreg_db/authz/management/commands/list_routes.py +18 -0
- endoreg_db/authz/middleware.py +83 -0
- endoreg_db/authz/permissions.py +127 -0
- endoreg_db/authz/policy.py +218 -0
- endoreg_db/authz/views_auth.py +66 -0
- endoreg_db/config/env.py +13 -8
- endoreg_db/data/__init__.py +8 -31
- endoreg_db/data/_examples/disease.yaml +55 -0
- endoreg_db/data/_examples/disease_classification.yaml +13 -0
- endoreg_db/data/_examples/disease_classification_choice.yaml +62 -0
- endoreg_db/data/_examples/event.yaml +64 -0
- endoreg_db/data/_examples/examination.yaml +72 -0
- endoreg_db/data/_examples/finding/anatomy_colon.yaml +128 -0
- endoreg_db/data/_examples/finding/colonoscopy.yaml +40 -0
- endoreg_db/data/_examples/finding/colonoscopy_bowel_prep.yaml +56 -0
- endoreg_db/data/_examples/finding/complication.yaml +16 -0
- endoreg_db/data/_examples/finding/data.yaml +105 -0
- endoreg_db/data/_examples/finding/examination_setting.yaml +16 -0
- endoreg_db/data/_examples/finding/medication_related.yaml +18 -0
- endoreg_db/data/_examples/finding/outcome.yaml +12 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_bowel_preparation.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_jnet.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_kudo.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_circularity.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_planarity.yaml +24 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_size.yaml +68 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lesion_surface.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_location.yaml +80 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_lst.yaml +21 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_nice.yaml +20 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_paris.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_sano.yaml +22 -0
- endoreg_db/data/_examples/finding_classification/colonoscopy_summary.yaml +53 -0
- endoreg_db/data/_examples/finding_classification/complication_generic.yaml +25 -0
- endoreg_db/data/_examples/finding_classification/examination_setting_generic.yaml +40 -0
- endoreg_db/data/_examples/finding_classification/histology_colo.yaml +51 -0
- endoreg_db/data/_examples/finding_classification/intervention_required.yaml +26 -0
- endoreg_db/data/_examples/finding_classification/medication_related.yaml +23 -0
- endoreg_db/data/_examples/finding_classification/visualized.yaml +33 -0
- endoreg_db/data/_examples/finding_classification_choice/bowel_preparation.yaml +78 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_circularity_default.yaml +32 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_jnet.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_kudo.yaml +23 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_lst.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_nice.yaml +17 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_paris.yaml +57 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_planarity_default.yaml +49 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_sano.yaml +14 -0
- endoreg_db/data/_examples/finding_classification_choice/colon_lesion_surface_intact_default.yaml +36 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_location.yaml +229 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_not_complete_reason.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_size.yaml +82 -0
- endoreg_db/data/_examples/finding_classification_choice/colonoscopy_summary_worst_finding.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/complication_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/examination_setting_generic_types.yaml +15 -0
- endoreg_db/data/_examples/finding_classification_choice/histology.yaml +24 -0
- endoreg_db/data/_examples/finding_classification_choice/histology_polyp.yaml +20 -0
- endoreg_db/data/_examples/finding_classification_choice/outcome.yaml +19 -0
- endoreg_db/data/_examples/finding_classification_choice/yes_no_na.yaml +11 -0
- endoreg_db/data/_examples/finding_classification_type/colonoscopy_basic.yaml +48 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy.yaml +43 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_colonoscopy.yaml +168 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_egd.yaml +128 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_ercp.yaml +32 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_lower.yaml +9 -0
- endoreg_db/data/_examples/finding_intervention/endoscopy_eus_upper.yaml +36 -0
- endoreg_db/data/_examples/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/_examples/finding_type/data.yaml +43 -0
- endoreg_db/data/_examples/requirement/age.yaml +26 -0
- endoreg_db/data/_examples/requirement/colonoscopy_baseline_austria.yaml +45 -0
- endoreg_db/data/_examples/requirement/disease_cardiovascular.yaml +79 -0
- endoreg_db/data/_examples/requirement/disease_classification_choice_cardiovascular.yaml +41 -0
- endoreg_db/data/_examples/requirement/disease_hepatology.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_misc.yaml +12 -0
- endoreg_db/data/_examples/requirement/disease_renal.yaml +96 -0
- endoreg_db/data/_examples/requirement/endoscopy_bleeding_risk.yaml +59 -0
- endoreg_db/data/_examples/requirement/event_cardiology.yaml +251 -0
- endoreg_db/data/_examples/requirement/event_requirements.yaml +145 -0
- endoreg_db/data/_examples/requirement/finding_colon_polyp.yaml +50 -0
- endoreg_db/data/_examples/requirement/gender.yaml +25 -0
- endoreg_db/data/_examples/requirement/lab_value.yaml +441 -0
- endoreg_db/data/_examples/requirement/medication.yaml +93 -0
- endoreg_db/data/_examples/requirement_operator/age.yaml +13 -0
- endoreg_db/data/_examples/requirement_operator/lab_operators.yaml +129 -0
- endoreg_db/data/_examples/requirement_operator/model_operators.yaml +96 -0
- endoreg_db/data/_examples/requirement_set/01_endoscopy_generic.yaml +48 -0
- endoreg_db/data/_examples/requirement_set/colonoscopy_austria_screening.yaml +57 -0
- endoreg_db/data/_examples/yaml_examples.xlsx +0 -0
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +4 -3
- endoreg_db/data/event_classification/data.yaml +4 -0
- endoreg_db/data/event_classification_choice/data.yaml +9 -0
- endoreg_db/data/finding_classification/colonoscopy_bowel_preparation.yaml +43 -70
- endoreg_db/data/finding_classification/colonoscopy_lesion_size.yaml +22 -52
- endoreg_db/data/finding_classification/colonoscopy_location.yaml +31 -62
- endoreg_db/data/finding_classification/histology_colo.yaml +28 -36
- endoreg_db/data/requirement/colon_polyp_intervention.yaml +49 -0
- endoreg_db/data/requirement/coloreg_colon_polyp.yaml +49 -0
- endoreg_db/data/requirement_set/01_endoscopy_generic.yaml +31 -12
- endoreg_db/data/requirement_set/01_laboratory.yaml +13 -0
- endoreg_db/data/requirement_set/02_endoscopy_bleeding_risk.yaml +46 -0
- endoreg_db/data/requirement_set/90_coloreg.yaml +178 -0
- endoreg_db/data/requirement_set/_old_ +109 -0
- endoreg_db/data/requirement_set_type/data.yaml +21 -0
- endoreg_db/data/setup_config.yaml +4 -4
- endoreg_db/data/tag/requirement_set_tags.yaml +21 -0
- endoreg_db/exceptions.py +5 -2
- endoreg_db/helpers/data_loader.py +1 -1
- endoreg_db/management/commands/create_model_meta_from_huggingface.py +21 -10
- endoreg_db/management/commands/create_multilabel_model_meta.py +299 -129
- endoreg_db/management/commands/import_video.py +9 -10
- endoreg_db/management/commands/import_video_with_classification.py +1 -1
- endoreg_db/management/commands/init_default_ai_model.py +1 -1
- endoreg_db/management/commands/list_routes.py +18 -0
- endoreg_db/management/commands/load_center_data.py +12 -12
- endoreg_db/management/commands/load_requirement_data.py +60 -31
- endoreg_db/management/commands/load_requirement_set_tags.py +95 -0
- endoreg_db/management/commands/setup_endoreg_db.py +3 -3
- endoreg_db/management/commands/storage_management.py +271 -203
- endoreg_db/migrations/0001_initial.py +1799 -1300
- endoreg_db/migrations/0002_requirementset_depends_on.py +18 -0
- endoreg_db/migrations/_old/0001_initial.py +1857 -0
- endoreg_db/migrations/_old/0004_employee_city_employee_post_code_employee_street_and_more.py +68 -0
- endoreg_db/migrations/_old/0004_remove_casetemplate_rules_and_more.py +77 -0
- endoreg_db/migrations/_old/0005_merge_20251111_1003.py +14 -0
- endoreg_db/migrations/_old/0006_sensitivemeta_anonymized_text_and_more.py +68 -0
- endoreg_db/migrations/_old/0007_remove_rule_attribute_dtype_remove_rule_rule_type_and_more.py +89 -0
- endoreg_db/migrations/_old/0008_remove_event_event_classification_and_more.py +27 -0
- endoreg_db/migrations/_old/0009_alter_modelmeta_options_and_more.py +21 -0
- endoreg_db/models/__init__.py +78 -123
- endoreg_db/models/administration/__init__.py +21 -42
- endoreg_db/models/administration/ai/active_model.py +2 -2
- endoreg_db/models/administration/ai/ai_model.py +7 -6
- endoreg_db/models/administration/case/__init__.py +1 -15
- endoreg_db/models/administration/case/case.py +3 -3
- endoreg_db/models/administration/case/case_template/__init__.py +2 -14
- endoreg_db/models/administration/case/case_template/case_template.py +2 -124
- endoreg_db/models/administration/case/case_template/case_template_rule.py +2 -268
- endoreg_db/models/administration/case/case_template/case_template_rule_value.py +2 -85
- endoreg_db/models/administration/case/case_template/case_template_type.py +2 -25
- endoreg_db/models/administration/center/center.py +33 -19
- endoreg_db/models/administration/center/center_product.py +12 -9
- endoreg_db/models/administration/center/center_resource.py +25 -19
- endoreg_db/models/administration/center/center_shift.py +21 -17
- endoreg_db/models/administration/center/center_waste.py +16 -8
- endoreg_db/models/administration/person/__init__.py +2 -0
- endoreg_db/models/administration/person/employee/employee.py +10 -5
- endoreg_db/models/administration/person/employee/employee_qualification.py +9 -4
- endoreg_db/models/administration/person/employee/employee_type.py +12 -6
- endoreg_db/models/administration/person/examiner/examiner.py +13 -11
- endoreg_db/models/administration/person/patient/__init__.py +2 -0
- endoreg_db/models/administration/person/patient/patient.py +103 -100
- endoreg_db/models/administration/person/patient/patient_external_id.py +37 -0
- endoreg_db/models/administration/person/person.py +4 -0
- endoreg_db/models/administration/person/profession/__init__.py +8 -4
- endoreg_db/models/administration/person/user/portal_user_information.py +11 -7
- endoreg_db/models/administration/product/product.py +20 -15
- endoreg_db/models/administration/product/product_material.py +17 -18
- endoreg_db/models/administration/product/product_weight.py +12 -8
- endoreg_db/models/administration/product/reference_product.py +23 -55
- endoreg_db/models/administration/qualification/qualification.py +7 -3
- endoreg_db/models/administration/qualification/qualification_type.py +7 -3
- endoreg_db/models/administration/shift/scheduled_days.py +8 -5
- endoreg_db/models/administration/shift/shift.py +16 -12
- endoreg_db/models/administration/shift/shift_type.py +23 -31
- endoreg_db/models/label/__init__.py +7 -8
- endoreg_db/models/label/annotation/image_classification.py +10 -9
- endoreg_db/models/label/annotation/video_segmentation_annotation.py +8 -5
- endoreg_db/models/label/label.py +15 -15
- endoreg_db/models/label/label_set.py +19 -6
- endoreg_db/models/label/label_type.py +1 -1
- endoreg_db/models/label/label_video_segment/_create_from_video.py +5 -8
- endoreg_db/models/label/label_video_segment/label_video_segment.py +76 -102
- endoreg_db/models/label/video_segmentation_label.py +4 -0
- endoreg_db/models/label/video_segmentation_labelset.py +4 -3
- endoreg_db/models/media/frame/frame.py +22 -22
- endoreg_db/models/media/pdf/raw_pdf.py +110 -182
- endoreg_db/models/media/pdf/report_file.py +25 -29
- endoreg_db/models/media/pdf/report_reader/report_reader_config.py +30 -46
- endoreg_db/models/media/pdf/report_reader/report_reader_flag.py +23 -7
- endoreg_db/models/media/video/__init__.py +1 -0
- endoreg_db/models/media/video/create_from_file.py +48 -56
- endoreg_db/models/media/video/pipe_2.py +8 -9
- endoreg_db/models/media/video/video_file.py +150 -108
- endoreg_db/models/media/video/video_file_ai.py +288 -74
- endoreg_db/models/media/video/video_file_anonymize.py +38 -38
- endoreg_db/models/media/video/video_file_frames/__init__.py +3 -1
- endoreg_db/models/media/video/video_file_frames/_bulk_create_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_create_frame_object.py +7 -9
- endoreg_db/models/media/video/video_file_frames/_delete_frames.py +9 -8
- endoreg_db/models/media/video/video_file_frames/_extract_frames.py +38 -45
- endoreg_db/models/media/video/video_file_frames/_get_frame.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frame_number.py +4 -18
- endoreg_db/models/media/video/video_file_frames/_get_frame_path.py +4 -3
- endoreg_db/models/media/video/video_file_frames/_get_frame_paths.py +7 -6
- endoreg_db/models/media/video/video_file_frames/_get_frame_range.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_get_frames.py +6 -8
- endoreg_db/models/media/video/video_file_frames/_initialize_frames.py +15 -25
- endoreg_db/models/media/video/video_file_frames/_manage_frame_range.py +26 -23
- endoreg_db/models/media/video/video_file_frames/_mark_frames_extracted_status.py +23 -14
- endoreg_db/models/media/video/video_file_io.py +109 -62
- endoreg_db/models/media/video/video_file_meta/get_crop_template.py +3 -3
- endoreg_db/models/media/video/video_file_meta/get_endo_roi.py +5 -3
- endoreg_db/models/media/video/video_file_meta/get_fps.py +37 -34
- endoreg_db/models/media/video/video_file_meta/initialize_video_specs.py +19 -25
- endoreg_db/models/media/video/video_file_meta/text_meta.py +41 -38
- endoreg_db/models/media/video/video_file_meta/video_meta.py +14 -7
- endoreg_db/models/media/video/video_file_segments.py +24 -17
- endoreg_db/models/media/video/video_metadata.py +19 -35
- endoreg_db/models/media/video/video_processing.py +96 -95
- endoreg_db/models/medical/contraindication/__init__.py +13 -3
- endoreg_db/models/medical/disease.py +22 -16
- endoreg_db/models/medical/event.py +31 -18
- endoreg_db/models/medical/examination/__init__.py +13 -6
- endoreg_db/models/medical/examination/examination.py +17 -18
- endoreg_db/models/medical/examination/examination_indication.py +26 -25
- endoreg_db/models/medical/examination/examination_time.py +16 -6
- endoreg_db/models/medical/examination/examination_time_type.py +9 -6
- endoreg_db/models/medical/examination/examination_type.py +3 -4
- endoreg_db/models/medical/finding/finding.py +38 -39
- endoreg_db/models/medical/finding/finding_classification.py +37 -48
- endoreg_db/models/medical/finding/finding_intervention.py +27 -22
- endoreg_db/models/medical/finding/finding_type.py +13 -12
- endoreg_db/models/medical/hardware/endoscope.py +20 -26
- endoreg_db/models/medical/hardware/endoscopy_processor.py +2 -2
- endoreg_db/models/medical/laboratory/lab_value.py +62 -91
- endoreg_db/models/medical/medication/medication.py +22 -10
- endoreg_db/models/medical/medication/medication_indication.py +29 -3
- endoreg_db/models/medical/medication/medication_indication_type.py +25 -14
- endoreg_db/models/medical/medication/medication_intake_time.py +31 -19
- endoreg_db/models/medical/medication/medication_schedule.py +27 -16
- endoreg_db/models/medical/organ/__init__.py +15 -12
- endoreg_db/models/medical/patient/medication_examples.py +1 -5
- endoreg_db/models/medical/patient/patient_disease.py +20 -23
- endoreg_db/models/medical/patient/patient_event.py +19 -22
- endoreg_db/models/medical/patient/patient_examination.py +48 -54
- endoreg_db/models/medical/patient/patient_examination_indication.py +16 -14
- endoreg_db/models/medical/patient/patient_finding.py +122 -139
- endoreg_db/models/medical/patient/patient_finding_classification.py +44 -49
- endoreg_db/models/medical/patient/patient_finding_intervention.py +8 -19
- endoreg_db/models/medical/patient/patient_lab_sample.py +28 -23
- endoreg_db/models/medical/patient/patient_lab_value.py +82 -89
- endoreg_db/models/medical/patient/patient_medication.py +27 -38
- endoreg_db/models/medical/patient/patient_medication_schedule.py +28 -36
- endoreg_db/models/medical/risk/risk.py +7 -6
- endoreg_db/models/medical/risk/risk_type.py +8 -5
- endoreg_db/models/metadata/model_meta.py +60 -29
- endoreg_db/models/metadata/model_meta_logic.py +125 -18
- endoreg_db/models/metadata/pdf_meta.py +19 -24
- endoreg_db/models/metadata/sensitive_meta.py +102 -85
- endoreg_db/models/metadata/sensitive_meta_logic.py +192 -173
- endoreg_db/models/metadata/video_meta.py +51 -31
- endoreg_db/models/metadata/video_prediction_logic.py +16 -23
- endoreg_db/models/metadata/video_prediction_meta.py +29 -33
- endoreg_db/models/other/distribution/date_value_distribution.py +89 -29
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +21 -5
- endoreg_db/models/other/distribution/numeric_value_distribution.py +114 -53
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +4 -3
- endoreg_db/models/other/emission/emission_factor.py +18 -8
- endoreg_db/models/other/gender.py +10 -5
- endoreg_db/models/other/information_source.py +25 -25
- endoreg_db/models/other/material.py +9 -5
- endoreg_db/models/other/resource.py +6 -4
- endoreg_db/models/other/tag.py +10 -5
- endoreg_db/models/other/transport_route.py +13 -8
- endoreg_db/models/other/unit.py +10 -6
- endoreg_db/models/other/waste.py +6 -5
- endoreg_db/models/requirement/requirement.py +580 -272
- endoreg_db/models/requirement/requirement_error.py +85 -0
- endoreg_db/models/requirement/requirement_evaluation/evaluate_with_dependencies.py +268 -0
- endoreg_db/models/requirement/requirement_evaluation/operator_evaluation_models.py +3 -6
- endoreg_db/models/requirement/requirement_evaluation/requirement_type_parser.py +90 -64
- endoreg_db/models/requirement/requirement_operator.py +36 -33
- endoreg_db/models/requirement/requirement_set.py +74 -57
- endoreg_db/models/state/__init__.py +4 -4
- endoreg_db/models/state/abstract.py +2 -2
- endoreg_db/models/state/anonymization.py +12 -0
- endoreg_db/models/state/audit_ledger.py +46 -47
- endoreg_db/models/state/label_video_segment.py +9 -0
- endoreg_db/models/state/raw_pdf.py +40 -46
- endoreg_db/models/state/sensitive_meta.py +6 -2
- endoreg_db/models/state/video.py +58 -53
- endoreg_db/models/upload_job.py +32 -55
- endoreg_db/models/utils.py +1 -2
- endoreg_db/root_urls.py +21 -2
- endoreg_db/serializers/__init__.py +0 -2
- endoreg_db/serializers/anonymization.py +18 -10
- endoreg_db/serializers/meta/report_meta.py +1 -1
- endoreg_db/serializers/meta/sensitive_meta_detail.py +63 -118
- endoreg_db/serializers/misc/file_overview.py +11 -99
- endoreg_db/serializers/requirements/requirement_sets.py +92 -22
- endoreg_db/serializers/video/segmentation.py +2 -1
- endoreg_db/serializers/video/video_processing_history.py +20 -5
- endoreg_db/services/anonymization.py +75 -73
- endoreg_db/services/lookup_service.py +37 -24
- endoreg_db/services/pdf_import.py +166 -68
- endoreg_db/services/storage_aware_video_processor.py +140 -114
- endoreg_db/services/video_import.py +193 -283
- endoreg_db/urls/__init__.py +7 -20
- endoreg_db/urls/media.py +108 -67
- endoreg_db/urls/root_urls.py +29 -0
- endoreg_db/utils/__init__.py +15 -5
- endoreg_db/utils/ai/multilabel_classification_net.py +116 -20
- endoreg_db/utils/case_generator/__init__.py +3 -0
- endoreg_db/utils/dataloader.py +88 -16
- endoreg_db/utils/defaults/set_default_center.py +32 -0
- endoreg_db/utils/names.py +22 -16
- endoreg_db/utils/permissions.py +2 -1
- endoreg_db/utils/pipelines/process_video_dir.py +1 -1
- endoreg_db/utils/requirement_operator_logic/model_evaluators.py +414 -127
- endoreg_db/utils/setup_config.py +8 -5
- endoreg_db/utils/storage.py +115 -0
- endoreg_db/utils/validate_endo_roi.py +8 -2
- endoreg_db/utils/video/ffmpeg_wrapper.py +184 -188
- endoreg_db/views/__init__.py +0 -10
- endoreg_db/views/anonymization/media_management.py +198 -163
- endoreg_db/views/anonymization/overview.py +4 -1
- endoreg_db/views/anonymization/validate.py +174 -40
- endoreg_db/views/media/__init__.py +2 -0
- endoreg_db/views/media/pdf_media.py +131 -152
- endoreg_db/views/media/sensitive_metadata.py +46 -6
- endoreg_db/views/media/video_media.py +89 -82
- endoreg_db/views/media/video_segments.py +2 -3
- endoreg_db/views/meta/sensitive_meta_detail.py +0 -63
- endoreg_db/views/patient/patient.py +5 -4
- endoreg_db/views/pdf/pdf_stream.py +20 -21
- endoreg_db/views/pdf/reimport.py +11 -32
- endoreg_db/views/requirement/evaluate.py +188 -187
- endoreg_db/views/requirement/lookup.py +17 -3
- endoreg_db/views/requirement/requirement_utils.py +89 -0
- endoreg_db/views/video/__init__.py +0 -2
- endoreg_db/views/video/correction.py +2 -2
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/METADATA +7 -3
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/RECORD +341 -245
- endoreg_db/models/administration/permissions/__init__.py +0 -44
- endoreg_db/models/media/video/video_file_frames.py +0 -0
- endoreg_db/models/metadata/frame_ocr_result.py +0 -0
- endoreg_db/models/rule/__init__.py +0 -13
- endoreg_db/models/rule/rule.py +0 -27
- endoreg_db/models/rule/rule_applicator.py +0 -224
- endoreg_db/models/rule/rule_attribute_dtype.py +0 -17
- endoreg_db/models/rule/rule_type.py +0 -20
- endoreg_db/models/rule/ruleset.py +0 -17
- endoreg_db/serializers/video/video_metadata.py +0 -105
- endoreg_db/urls/report.py +0 -48
- endoreg_db/urls/video.py +0 -61
- endoreg_db/utils/case_generator/case_generator.py +0 -159
- endoreg_db/utils/case_generator/utils.py +0 -30
- endoreg_db/views/report/__init__.py +0 -9
- endoreg_db/views/report/report_list.py +0 -112
- endoreg_db/views/report/report_with_secure_url.py +0 -28
- endoreg_db/views/report/start_examination.py +0 -7
- endoreg_db/views.py +0 -0
- /endoreg_db/data/{requirement_set → _examples/requirement_set}/endoscopy_bleeding_risk.yaml +0 -0
- /endoreg_db/migrations/{0002_add_video_correction_models.py → _old/0002_add_video_correction_models.py} +0 -0
- /endoreg_db/migrations/{0003_add_center_display_name.py → _old/0003_add_center_display_name.py} +0 -0
- /endoreg_db/{models/media/video/refactor_plan.md → views/pdf/pdf_stream_views.py} +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/WHEEL +0 -0
- {endoreg_db-0.8.6.1.dist-info → endoreg_db-0.8.8.0.dist-info}/licenses/LICENSE +0 -0
endoreg_db/urls/__init__.py
CHANGED
|
@@ -3,14 +3,14 @@ from django.conf.urls.static import static
|
|
|
3
3
|
from django.urls import include, path
|
|
4
4
|
from rest_framework.routers import DefaultRouter
|
|
5
5
|
|
|
6
|
+
from endoreg_db.authz.views_auth import auth_bootstrap
|
|
7
|
+
|
|
6
8
|
from endoreg_db.views import (
|
|
7
9
|
ExaminationViewSet,
|
|
8
10
|
FindingClassificationViewSet,
|
|
9
11
|
FindingViewSet,
|
|
10
12
|
PatientExaminationViewSet,
|
|
11
13
|
PatientFindingViewSet,
|
|
12
|
-
VideoExaminationViewSet,
|
|
13
|
-
VideoViewSet,
|
|
14
14
|
)
|
|
15
15
|
|
|
16
16
|
from .anonymization import url_patterns as anonymization_url_patterns
|
|
@@ -29,11 +29,9 @@ from .patient import urlpatterns as patient_url_patterns
|
|
|
29
29
|
|
|
30
30
|
# TODO Phase 1.2: Implement VideoMediaView and PDFMediaView before enabling
|
|
31
31
|
# from .media import urlpatterns as media_url_patterns
|
|
32
|
-
from .report import url_patterns as report_url_patterns
|
|
33
32
|
from .requirements import urlpatterns as requirements_url_patterns
|
|
34
33
|
from .stats import url_patterns as stats_url_patterns
|
|
35
34
|
from .upload import urlpatterns as upload_url_patterns
|
|
36
|
-
from .video import url_patterns as video_url_patterns
|
|
37
35
|
|
|
38
36
|
api_urls = []
|
|
39
37
|
api_urls += classification_url_patterns
|
|
@@ -42,22 +40,16 @@ api_urls += auth_url_patterns
|
|
|
42
40
|
api_urls += examination_url_patterns
|
|
43
41
|
api_urls += files_url_patterns
|
|
44
42
|
api_urls += label_video_segments_url_patterns
|
|
45
|
-
api_urls += label_video_segment_validate_url_patterns
|
|
46
|
-
# Phase 1.2: Enable media_url_patterns ✅ IMPLEMENTED
|
|
43
|
+
api_urls += label_video_segment_validate_url_patterns
|
|
47
44
|
api_urls += media_url_patterns
|
|
48
|
-
api_urls += report_url_patterns
|
|
49
45
|
api_urls += upload_url_patterns
|
|
50
|
-
api_urls += video_url_patterns
|
|
51
46
|
api_urls += requirements_url_patterns
|
|
52
47
|
api_urls += patient_url_patterns
|
|
53
48
|
api_urls += stats_url_patterns
|
|
54
49
|
|
|
55
50
|
router = DefaultRouter()
|
|
56
|
-
router.register(r"videos", VideoViewSet, basename="videos")
|
|
57
51
|
router.register(r"examinations", ExaminationViewSet)
|
|
58
|
-
|
|
59
|
-
r"video-examinations", VideoExaminationViewSet, basename="video-examinations"
|
|
60
|
-
)
|
|
52
|
+
|
|
61
53
|
router.register(r"findings", FindingViewSet)
|
|
62
54
|
router.register(r"classifications", FindingClassificationViewSet)
|
|
63
55
|
router.register(r"patient-findings", PatientFindingViewSet)
|
|
@@ -65,17 +57,12 @@ router.register(r"patient-examinations", PatientExaminationViewSet)
|
|
|
65
57
|
|
|
66
58
|
# Additional custom video examination routes
|
|
67
59
|
# Frontend expects: GET /api/video/{id}/examinations/
|
|
68
|
-
video_examinations_list = VideoExaminationViewSet.as_view({"get": "by_video"})
|
|
69
60
|
|
|
70
61
|
# Export raw API urlpatterns (no prefix). The project-level endoreg_db/urls.py mounts these under /api/.
|
|
71
62
|
urlpatterns = [
|
|
72
|
-
path(
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
name="video-examinations-by-video",
|
|
76
|
-
),
|
|
77
|
-
path("", include(api_urls)), # Specific routes first
|
|
78
|
-
path("", include(router.urls)), # Generic router routes second
|
|
63
|
+
path("auth/bootstrap", auth_bootstrap, name="auth-bootstrap"),
|
|
64
|
+
path('', include(router.urls)),
|
|
65
|
+
path('', include(api_urls)),
|
|
79
66
|
]
|
|
80
67
|
|
|
81
68
|
if settings.DEBUG:
|
endoreg_db/urls/media.py
CHANGED
|
@@ -1,37 +1,36 @@
|
|
|
1
|
-
from PIL.PdfParser import PdfStream
|
|
2
1
|
from django.urls import path
|
|
3
2
|
|
|
3
|
+
from endoreg_db.views import VideoStreamView
|
|
4
4
|
from endoreg_db.views.media import (
|
|
5
|
-
VideoMediaView,
|
|
6
5
|
PdfMediaView, # Alias to avoid conflict with legacy pdf.PDFMediaView
|
|
6
|
+
VideoMediaView,
|
|
7
|
+
pdf_sensitive_metadata,
|
|
8
|
+
pdf_sensitive_metadata_list,
|
|
9
|
+
pdf_sensitive_metadata_verify,
|
|
10
|
+
sensitive_metadata_list,
|
|
11
|
+
video_segment_detail,
|
|
12
|
+
video_segment_validate,
|
|
7
13
|
video_segments_by_pk,
|
|
8
|
-
video_segments_collection,
|
|
9
14
|
video_segments_by_video,
|
|
10
|
-
|
|
15
|
+
video_segments_collection,
|
|
11
16
|
video_segments_stats,
|
|
12
|
-
video_segment_validate,
|
|
13
17
|
video_segments_validate_bulk,
|
|
14
18
|
video_segments_validation_status,
|
|
15
19
|
video_sensitive_metadata,
|
|
16
20
|
video_sensitive_metadata_verify,
|
|
17
|
-
|
|
18
|
-
pdf_sensitive_metadata_verify,
|
|
19
|
-
sensitive_metadata_list,
|
|
20
|
-
pdf_sensitive_metadata_list,
|
|
21
|
-
)
|
|
22
|
-
from endoreg_db.views import (
|
|
23
|
-
VideoStreamView,
|
|
21
|
+
get_sensitive_metadata_pk
|
|
24
22
|
)
|
|
25
|
-
from endoreg_db.views.pdf.reimport import PdfReimportView
|
|
26
23
|
from endoreg_db.views.pdf.pdf_stream import PdfStreamView
|
|
27
|
-
from endoreg_db.views.
|
|
24
|
+
from endoreg_db.views.pdf.reimport import PdfReimportView
|
|
28
25
|
from endoreg_db.views.video.correction import (
|
|
26
|
+
VideoApplyMaskView,
|
|
27
|
+
VideoCorrectionView,
|
|
29
28
|
VideoMetadataView,
|
|
30
29
|
VideoProcessingHistoryView,
|
|
31
|
-
VideoApplyMaskView,
|
|
32
30
|
VideoRemoveFramesView,
|
|
33
|
-
VideoCorrectionView,
|
|
34
31
|
)
|
|
32
|
+
from endoreg_db.views.video.reimport import VideoReimportView
|
|
33
|
+
|
|
35
34
|
# ---------------------------------------------------------------------------------------
|
|
36
35
|
# ANNOTATION API ENDPOINTS
|
|
37
36
|
#
|
|
@@ -41,19 +40,32 @@ from endoreg_db.views.video.correction import (
|
|
|
41
40
|
# ---------------------------------------------------------------------------------------
|
|
42
41
|
|
|
43
42
|
# Simplified Meta and Validation Endpoints
|
|
44
|
-
|
|
43
|
+
|
|
45
44
|
urlpatterns = [
|
|
45
|
+
path(
|
|
46
|
+
"media/sensitive-media-id/<int:pk>/<str:mediaType>/",
|
|
47
|
+
get_sensitive_metadata_pk,
|
|
48
|
+
name="sm-pk"
|
|
49
|
+
),
|
|
46
50
|
# Video media endpoints
|
|
47
51
|
path("media/videos/", VideoMediaView.as_view(), name="video-list"),
|
|
48
|
-
path(
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
+
path(
|
|
53
|
+
"media/videos/<int:pk>/", VideoStreamView.as_view(), name="video-detail-stream"
|
|
54
|
+
), # Support ?type= params
|
|
55
|
+
path(
|
|
56
|
+
"media/videos/<int:pk>/details/", VideoMediaView.as_view(), name="video-detail"
|
|
57
|
+
), # JSON metadata
|
|
58
|
+
path(
|
|
59
|
+
"media/videos/<int:pk>/stream/", VideoStreamView.as_view(), name="video-stream"
|
|
60
|
+
), # Legacy support
|
|
52
61
|
# Video Re-import API endpoint (modern media framework)
|
|
53
62
|
# POST /api/media/videos/<int:pk>/reimport/
|
|
54
63
|
# Re-imports a video file to regenerate metadata when OCR failed or data is incomplete
|
|
55
|
-
path(
|
|
56
|
-
|
|
64
|
+
path(
|
|
65
|
+
"media/videos/<int:pk>/reimport/",
|
|
66
|
+
VideoReimportView.as_view(),
|
|
67
|
+
name="video-reimport",
|
|
68
|
+
),
|
|
57
69
|
# ---------------------------------------------------------------------------------------
|
|
58
70
|
# VIDEO CORRECTION API ENDPOINTS (Modern Media Framework - October 14, 2025)
|
|
59
71
|
#
|
|
@@ -66,38 +78,51 @@ urlpatterns = [
|
|
|
66
78
|
# - Metadata: View analysis results
|
|
67
79
|
# - History: Track all correction operations
|
|
68
80
|
# ---------------------------------------------------------------------------------------
|
|
69
|
-
|
|
70
81
|
# Video Correction API
|
|
71
82
|
# GET /api/media/videos/video-correction/{id}/ - Get video details for correction
|
|
72
|
-
path(
|
|
73
|
-
|
|
83
|
+
path(
|
|
84
|
+
"media/videos/video-correction/<int:pk>",
|
|
85
|
+
VideoCorrectionView.as_view(),
|
|
86
|
+
name="video-correction",
|
|
87
|
+
),
|
|
74
88
|
# Video Metadata API
|
|
75
89
|
# GET /api/media/videos/<int:pk>/metadata/
|
|
76
90
|
# Returns analysis results (sensitive frame count, ratio, frame IDs)
|
|
77
|
-
path(
|
|
78
|
-
|
|
91
|
+
path(
|
|
92
|
+
"media/videos/<int:pk>/metadata/",
|
|
93
|
+
VideoMetadataView.as_view(),
|
|
94
|
+
name="video-metadata",
|
|
95
|
+
),
|
|
79
96
|
# Video Processing History API
|
|
80
97
|
# GET /api/media/videos/<int:pk>/processing-history/
|
|
81
98
|
# Returns history of all processing operations (masking, frame removal, analysis)
|
|
82
|
-
path(
|
|
83
|
-
|
|
99
|
+
path(
|
|
100
|
+
"media/videos/<int:pk>/processing-history/",
|
|
101
|
+
VideoProcessingHistoryView.as_view(),
|
|
102
|
+
name="video-processing-history",
|
|
103
|
+
),
|
|
84
104
|
# Video Analysis API
|
|
85
105
|
# POST /api/media/videos/<int:pk>/analyze/
|
|
86
106
|
# Analyzes video for sensitive frames using MiniCPM-o 2.6 or OCR+LLM
|
|
87
107
|
# Body: { detection_method: 'minicpm'|'ocr_llm'|'hybrid', sample_interval: 30 }
|
|
88
|
-
|
|
89
108
|
# Video Masking API
|
|
90
109
|
# POST /api/media/videos/<int:pk>/apply-mask/
|
|
91
110
|
# Applies device mask or custom ROI mask to video
|
|
92
111
|
# Body: { mask_type: 'device'|'custom', device_name: 'olympus', roi: {...} }
|
|
93
|
-
path(
|
|
94
|
-
|
|
112
|
+
path(
|
|
113
|
+
"media/videos/<int:pk>/apply-mask/",
|
|
114
|
+
VideoApplyMaskView.as_view(),
|
|
115
|
+
name="video-apply-mask",
|
|
116
|
+
),
|
|
95
117
|
# Video Frame Removal API
|
|
96
118
|
# POST /api/media/videos/<int:pk>/remove-frames/
|
|
97
119
|
# Removes specified frames from video
|
|
98
120
|
# Body: { frame_list: [10,20,30] OR frame_ranges: '10-20,30' OR detection_method: 'automatic' }
|
|
99
|
-
path(
|
|
100
|
-
|
|
121
|
+
path(
|
|
122
|
+
"media/videos/<int:pk>/remove-frames/",
|
|
123
|
+
VideoRemoveFramesView.as_view(),
|
|
124
|
+
name="video-remove-frames",
|
|
125
|
+
),
|
|
101
126
|
# ---------------------------------------------------------------------------------------
|
|
102
127
|
# VIDEO SEGMENT API ENDPOINTS (Modern Media Framework - October 14, 2025)
|
|
103
128
|
#
|
|
@@ -106,29 +131,40 @@ urlpatterns = [
|
|
|
106
131
|
# Video-scoped: GET/POST segments for specific video
|
|
107
132
|
# Detail: GET/PATCH/DELETE individual segment
|
|
108
133
|
# ---------------------------------------------------------------------------------------
|
|
109
|
-
|
|
110
134
|
# Video Segments Collection API
|
|
111
135
|
# GET/POST /api/media/videos/segments/
|
|
112
136
|
# List all video segments across videos or create new segment
|
|
113
|
-
path(
|
|
114
|
-
|
|
137
|
+
path(
|
|
138
|
+
"media/videos/segments/",
|
|
139
|
+
video_segments_collection,
|
|
140
|
+
name="video-segments-collection",
|
|
141
|
+
),
|
|
115
142
|
# Video Segments Stats API
|
|
116
143
|
# GET /api/media/videos/segments/stats/
|
|
117
144
|
# Get statistics about video segments
|
|
118
|
-
path(
|
|
119
|
-
|
|
145
|
+
path(
|
|
146
|
+
"media/videos/segments/stats/",
|
|
147
|
+
video_segments_stats,
|
|
148
|
+
name="video-segments-stats",
|
|
149
|
+
),
|
|
120
150
|
# Video-Specific Segments API
|
|
121
151
|
# GET/POST /api/media/videos/<int:pk>/segments/
|
|
122
152
|
# List segments for specific video or create segment for video
|
|
123
|
-
path(
|
|
124
|
-
|
|
153
|
+
path(
|
|
154
|
+
"media/videos/<int:pk>/segments/",
|
|
155
|
+
video_segments_by_video,
|
|
156
|
+
name="video-segments-by-video",
|
|
157
|
+
),
|
|
125
158
|
# Segment Detail API
|
|
126
159
|
# GET /api/media/videos/<int:pk>/segments/<int:segment_id>/
|
|
127
160
|
# PATCH /api/media/videos/<int:pk>/segments/<int:segment_id>/
|
|
128
161
|
# DELETE /api/media/videos/<int:pk>/segments/<int:segment_id>/
|
|
129
162
|
# Manages individual segment operations
|
|
130
|
-
path(
|
|
131
|
-
|
|
163
|
+
path(
|
|
164
|
+
"media/videos/<int:pk>/segments/<int:segment_id>/",
|
|
165
|
+
video_segment_detail,
|
|
166
|
+
name="video-segment-detail",
|
|
167
|
+
),
|
|
132
168
|
# ---------------------------------------------------------------------------------------
|
|
133
169
|
# VIDEO SEGMENT VALIDATION API ENDPOINTS (Modern Media Framework - October 14, 2025)
|
|
134
170
|
#
|
|
@@ -137,65 +173,68 @@ urlpatterns = [
|
|
|
137
173
|
# Bulk: POST validate multiple segments
|
|
138
174
|
# Status: GET/POST validation status for all segments
|
|
139
175
|
# ---------------------------------------------------------------------------------------
|
|
140
|
-
|
|
141
176
|
# Single Segment Validation API
|
|
142
177
|
# POST /api/media/videos/<int:pk>/segments/<int:segment_id>/validate/
|
|
143
178
|
# Validates a single video segment
|
|
144
179
|
# Body: { "is_validated": true, "notes": "..." }
|
|
145
|
-
path(
|
|
146
|
-
|
|
180
|
+
path(
|
|
181
|
+
"media/videos/<int:pk>/segments/<int:segment_id>/validate/",
|
|
182
|
+
video_segment_validate,
|
|
183
|
+
name="video-segment-validate",
|
|
184
|
+
),
|
|
147
185
|
# Bulk Segment Validation API
|
|
148
186
|
# POST /api/media/videos/<int:pk>/segments/validate-bulk/
|
|
149
187
|
# Validates multiple segments at once
|
|
150
188
|
# Body: { "segment_ids": [1,2,3], "is_validated": true, "notes": "..." }
|
|
151
|
-
path(
|
|
152
|
-
|
|
189
|
+
path(
|
|
190
|
+
"media/videos/<int:pk>/segments/validate-bulk/",
|
|
191
|
+
video_segments_validate_bulk,
|
|
192
|
+
name="video-segments-validate-bulk",
|
|
193
|
+
),
|
|
153
194
|
# Segment Validation Status API
|
|
154
195
|
# GET /api/media/videos/<int:pk>/segments/validation-status/
|
|
155
196
|
# Returns validation statistics for all segments
|
|
156
197
|
# POST /api/media/videos/<int:pk>/segments/validation-status/
|
|
157
198
|
# Marks all segments (or filtered by label) as validated
|
|
158
199
|
# Body: { "label_name": "polyp", "notes": "..." }
|
|
159
|
-
path(
|
|
160
|
-
|
|
200
|
+
path(
|
|
201
|
+
"media/videos/<int:pk>/segments/validation-status/",
|
|
202
|
+
video_segments_validation_status,
|
|
203
|
+
name="video-segments-validation-status",
|
|
204
|
+
),
|
|
161
205
|
# ---------------------------------------------------------------------------------------
|
|
162
206
|
# SENSITIVE METADATA ENDPOINTS (Modern Media Framework)
|
|
163
207
|
# ---------------------------------------------------------------------------------------
|
|
164
|
-
|
|
165
208
|
# Video Sensitive Metadata (Resource-Scoped)
|
|
166
209
|
# GET/PATCH /api/media/videos/<pk>/sensitive-metadata/
|
|
167
210
|
# Get or update sensitive patient data for a video
|
|
168
211
|
path(
|
|
169
212
|
"media/videos/<int:pk>/sensitive-metadata/",
|
|
170
213
|
video_sensitive_metadata,
|
|
171
|
-
name="video-sensitive-metadata"
|
|
214
|
+
name="video-sensitive-metadata",
|
|
172
215
|
),
|
|
173
|
-
|
|
174
216
|
# POST /api/media/videos/<pk>/sensitive-metadata/verify/
|
|
175
217
|
# Update verification state (dob_verified, names_verified)
|
|
176
218
|
path(
|
|
177
219
|
"media/videos/<int:pk>/sensitive-metadata/verify/",
|
|
178
220
|
video_sensitive_metadata_verify,
|
|
179
|
-
name="video-sensitive-metadata-verify"
|
|
221
|
+
name="video-sensitive-metadata-verify",
|
|
180
222
|
),
|
|
181
|
-
|
|
182
223
|
# PDF Sensitive Metadata (Resource-Scoped)
|
|
183
224
|
# GET/PATCH /api/media/pdfs/<pk>/sensitive-metadata/
|
|
184
225
|
# Get or update sensitive patient data for a PDF
|
|
185
226
|
path(
|
|
186
227
|
"media/pdfs/<int:pk>/sensitive-metadata/",
|
|
187
228
|
pdf_sensitive_metadata,
|
|
188
|
-
name="pdf-sensitive-metadata"
|
|
229
|
+
name="pdf-sensitive-metadata",
|
|
189
230
|
),
|
|
190
|
-
|
|
191
231
|
# POST /api/media/pdfs/<pk>/sensitive-metadata/verify/
|
|
192
232
|
# Update verification state (dob_verified, names_verified)
|
|
193
233
|
path(
|
|
194
234
|
"media/pdfs/<int:pk>/sensitive-metadata/verify/",
|
|
195
235
|
pdf_sensitive_metadata_verify,
|
|
196
|
-
name="pdf-sensitive-metadata-verify"
|
|
236
|
+
name="pdf-sensitive-metadata-verify",
|
|
197
237
|
),
|
|
198
|
-
|
|
199
238
|
# List Endpoints (Collection-Level)
|
|
200
239
|
# GET /api/media/sensitive-metadata/
|
|
201
240
|
# List all sensitive metadata (combined PDFs and Videos)
|
|
@@ -203,25 +242,27 @@ urlpatterns = [
|
|
|
203
242
|
path(
|
|
204
243
|
"media/sensitive-metadata/",
|
|
205
244
|
sensitive_metadata_list,
|
|
206
|
-
name="sensitive-metadata-list"
|
|
245
|
+
name="sensitive-metadata-list",
|
|
207
246
|
),
|
|
208
|
-
|
|
209
247
|
# GET /api/media/pdfs/sensitive-metadata/
|
|
210
248
|
# List sensitive metadata for PDFs only
|
|
211
249
|
# Replaces legacy /api/pdf/sensitivemeta/list/
|
|
212
250
|
path(
|
|
213
251
|
"media/pdfs/sensitive-metadata/",
|
|
214
252
|
pdf_sensitive_metadata_list,
|
|
215
|
-
name="pdf-sensitive-metadata-list"
|
|
253
|
+
name="pdf-sensitive-metadata-list",
|
|
216
254
|
),
|
|
217
|
-
|
|
218
255
|
# PDF media endpoints
|
|
219
256
|
path("media/pdfs/", PdfMediaView.as_view(), name="pdf-list"),
|
|
220
257
|
path("media/pdfs/<int:pk>/", PdfMediaView.as_view(), name="pdf-detail"),
|
|
221
|
-
path(
|
|
258
|
+
path(
|
|
259
|
+
"media/pdfs/<int:pk>/stream/", PdfStreamView.as_view(), name="pdf-stream"
|
|
260
|
+
), # Support ?type=raw|anonymized params
|
|
222
261
|
# PDF Re-import API endpoint (modern media framework)
|
|
223
262
|
# POST /api/media/pdfs/<int:pk>/reimport/
|
|
224
263
|
# Re-imports a PDF file to regenerate metadata when OCR failed or data is incomplete
|
|
225
|
-
path(
|
|
264
|
+
path(
|
|
265
|
+
"media/pdfs/<int:pk>/reimport/", PdfReimportView.as_view(), name="pdf-reimport"
|
|
266
|
+
),
|
|
226
267
|
]
|
|
227
|
-
|
|
268
|
+
# ---------------------------------------------------------------------------------------
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# endoreg_db/urls/root_urls.py
|
|
2
|
+
from django.contrib import admin
|
|
3
|
+
from django.urls import path, include
|
|
4
|
+
from django.http import HttpResponse
|
|
5
|
+
from django.conf import settings
|
|
6
|
+
from django.conf.urls.static import static
|
|
7
|
+
|
|
8
|
+
def public_home(_request):
|
|
9
|
+
return HttpResponse("Public home – no login required.")
|
|
10
|
+
|
|
11
|
+
urlpatterns = [
|
|
12
|
+
# Public landing page
|
|
13
|
+
path("", public_home, name="public_home"),
|
|
14
|
+
|
|
15
|
+
# Django admin (optional)
|
|
16
|
+
path("admin/", admin.site.urls),
|
|
17
|
+
|
|
18
|
+
# Mount ALL API routes under /api/
|
|
19
|
+
# This pulls the urlpatterns exported by endoreg_db/urls/__init__.py
|
|
20
|
+
path("api/", include("endoreg_db.urls")),
|
|
21
|
+
|
|
22
|
+
# Keycloak OIDC (mozilla-django-oidc provides /oidc/authenticate/ and /oidc/callback/)
|
|
23
|
+
path("oidc/", include("mozilla_django_oidc.urls")),
|
|
24
|
+
]
|
|
25
|
+
|
|
26
|
+
# Serve static/media in DEBUG at the root (NOT under /api/)
|
|
27
|
+
if settings.DEBUG:
|
|
28
|
+
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
|
29
|
+
urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
|
endoreg_db/utils/__init__.py
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
# dataloader
|
|
6
6
|
from endoreg_db.utils.video.ffmpeg_wrapper import assemble_video_from_frames, get_stream_info, transcode_video, transcode_videofile_if_required
|
|
7
|
+
|
|
7
8
|
from .dataloader import load_model_data_from_yaml
|
|
8
9
|
|
|
9
10
|
# dates
|
|
@@ -40,18 +41,23 @@ from .paths import data_paths
|
|
|
40
41
|
|
|
41
42
|
# pydantic_models
|
|
42
43
|
from .pydantic_models import DbConfig
|
|
44
|
+
from .storage import (
|
|
45
|
+
delete_field_file,
|
|
46
|
+
ensure_local_file,
|
|
47
|
+
save_local_file,
|
|
48
|
+
)
|
|
49
|
+
from .storage import (
|
|
50
|
+
file_exists as storage_file_exists,
|
|
51
|
+
)
|
|
43
52
|
|
|
44
53
|
# validate_endo_roi
|
|
45
54
|
from .validate_endo_roi import validate_endo_roi
|
|
55
|
+
from .video import split_video
|
|
46
56
|
|
|
47
57
|
# ffmpeg_wrapper
|
|
48
58
|
from .video.ffmpeg_wrapper import (
|
|
49
59
|
extract_frames,
|
|
50
60
|
)
|
|
51
|
-
from .video import (
|
|
52
|
-
split_video
|
|
53
|
-
)
|
|
54
|
-
|
|
55
61
|
|
|
56
62
|
# --- Exports ---
|
|
57
63
|
|
|
@@ -84,5 +90,9 @@ __all__ = [
|
|
|
84
90
|
"transcode_video",
|
|
85
91
|
"transcode_videofile_if_required", # Added
|
|
86
92
|
"extract_frames", # Added
|
|
93
|
+
"split_video",
|
|
94
|
+
"delete_field_file",
|
|
95
|
+
"ensure_local_file",
|
|
96
|
+
"save_local_file",
|
|
97
|
+
"storage_file_exists",
|
|
87
98
|
]
|
|
88
|
-
|
|
@@ -1,10 +1,22 @@
|
|
|
1
|
+
import logging
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
|
|
1
4
|
import torch
|
|
2
5
|
from torchvision import models
|
|
3
6
|
import torch.nn as nn
|
|
4
7
|
from pytorch_lightning import LightningModule
|
|
5
8
|
import numpy as np
|
|
9
|
+
from safetensors.torch import load_file
|
|
6
10
|
from sklearn.metrics import precision_score, recall_score, f1_score
|
|
7
11
|
|
|
12
|
+
try: # Torchvision >= 0.13 exposes explicit weight enums
|
|
13
|
+
from torchvision.models import EfficientNet_B4_Weights, RegNet_X_800MF_Weights
|
|
14
|
+
except ImportError: # pragma: no cover - compatibility with older torchvision
|
|
15
|
+
EfficientNet_B4_Weights = None
|
|
16
|
+
RegNet_X_800MF_Weights = None
|
|
17
|
+
|
|
18
|
+
logger = logging.getLogger(__name__)
|
|
19
|
+
|
|
8
20
|
METRICS_ON_STEP = False
|
|
9
21
|
|
|
10
22
|
|
|
@@ -41,6 +53,29 @@ def calculate_metrics(pred, target, threshold=0.5):
|
|
|
41
53
|
}
|
|
42
54
|
|
|
43
55
|
|
|
56
|
+
def _load_torchvision_backbone(factory, *, weights_enum=None, load_pretrained=False):
|
|
57
|
+
"""Instantiate a torchvision model without triggering unwanted downloads."""
|
|
58
|
+
if weights_enum is not None:
|
|
59
|
+
try:
|
|
60
|
+
weights = weights_enum.DEFAULT if load_pretrained else None
|
|
61
|
+
return factory(weights=weights)
|
|
62
|
+
except (TypeError, AttributeError):
|
|
63
|
+
# Fall back to legacy keyword on older torchvision versions
|
|
64
|
+
pass
|
|
65
|
+
|
|
66
|
+
try:
|
|
67
|
+
return factory(pretrained=load_pretrained)
|
|
68
|
+
except TypeError:
|
|
69
|
+
# Newer torchvision versions removed the pretrained kwarg; call without hints
|
|
70
|
+
try:
|
|
71
|
+
return factory()
|
|
72
|
+
except Exception as exc: # pragma: no cover - surfaced to caller for visibility
|
|
73
|
+
raise RuntimeError(
|
|
74
|
+
"Failed to instantiate torchvision backbone with load_pretrained="
|
|
75
|
+
f"{load_pretrained}."
|
|
76
|
+
) from exc
|
|
77
|
+
|
|
78
|
+
|
|
44
79
|
class MultiLabelClassificationNet(LightningModule):
|
|
45
80
|
def __init__(
|
|
46
81
|
self,
|
|
@@ -49,26 +84,40 @@ class MultiLabelClassificationNet(LightningModule):
|
|
|
49
84
|
weight_decay=0.001,
|
|
50
85
|
pos_weight=2,
|
|
51
86
|
model_type="EfficientNetB4",
|
|
87
|
+
load_imagenet_weights: bool = False,
|
|
88
|
+
track_hparams: bool = True,
|
|
52
89
|
):
|
|
53
90
|
super().__init__()
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
self.
|
|
91
|
+
if track_hparams:
|
|
92
|
+
self.save_hyperparameters()
|
|
93
|
+
if labels is None:
|
|
94
|
+
raise ValueError("labels must be provided to initialize MultiLabelClassificationNet")
|
|
95
|
+
|
|
96
|
+
self.model_type = model_type
|
|
97
|
+
self.labels = list(labels)
|
|
98
|
+
self.n_classes = len(self.labels)
|
|
99
|
+
self.val_preds: list[np.ndarray] = []
|
|
100
|
+
self.val_targets: list[np.ndarray] = []
|
|
60
101
|
self.pos_weight = pos_weight
|
|
61
102
|
self.weight_decay = weight_decay
|
|
62
103
|
self.lr = lr
|
|
63
104
|
self.sigm = nn.Sigmoid()
|
|
64
105
|
|
|
65
106
|
if model_type == "EfficientNetB4":
|
|
66
|
-
self.model =
|
|
107
|
+
self.model = _load_torchvision_backbone(
|
|
108
|
+
models.efficientnet_b4,
|
|
109
|
+
weights_enum=EfficientNet_B4_Weights,
|
|
110
|
+
load_pretrained=load_imagenet_weights,
|
|
111
|
+
)
|
|
67
112
|
num_ftrs = self.model.classifier[1].in_features
|
|
68
113
|
self.model.classifier[1] = nn.Linear(num_ftrs, len(labels))
|
|
69
114
|
|
|
70
115
|
elif model_type == "RegNetX800MF":
|
|
71
|
-
self.model =
|
|
116
|
+
self.model = _load_torchvision_backbone(
|
|
117
|
+
models.regnet_x_800mf,
|
|
118
|
+
weights_enum=RegNet_X_800MF_Weights,
|
|
119
|
+
load_pretrained=load_imagenet_weights,
|
|
120
|
+
)
|
|
72
121
|
num_ftrs = self.model.fc.in_features
|
|
73
122
|
self.model.fc = nn.Linear(num_ftrs, len(labels))
|
|
74
123
|
|
|
@@ -78,10 +127,47 @@ class MultiLabelClassificationNet(LightningModule):
|
|
|
78
127
|
|
|
79
128
|
@classmethod
|
|
80
129
|
def load_from_checkpoint(cls, checkpoint_path, *args, **kwargs):
|
|
81
|
-
|
|
130
|
+
path = Path(checkpoint_path)
|
|
131
|
+
suffix = path.suffix.lower()
|
|
132
|
+
|
|
133
|
+
if suffix == ".safetensors":
|
|
134
|
+
map_location = kwargs.pop("map_location", "cpu")
|
|
135
|
+
strict = kwargs.pop("strict", True)
|
|
136
|
+
labels = kwargs.pop("labels", None)
|
|
137
|
+
if not labels:
|
|
138
|
+
raise ValueError("labels must be provided when loading .safetensors checkpoints")
|
|
139
|
+
model_type = kwargs.pop("model_type", None) or "EfficientNetB4"
|
|
140
|
+
load_imagenet = kwargs.pop("load_imagenet_weights", False)
|
|
141
|
+
|
|
142
|
+
device = torch.device(map_location) if map_location is not None else torch.device("cpu")
|
|
143
|
+
if isinstance(device, torch.device):
|
|
144
|
+
device_hint = f"{device.type}:{device.index}" if device.index is not None else device.type
|
|
145
|
+
else:
|
|
146
|
+
device_hint = device
|
|
147
|
+
|
|
148
|
+
state_dict = load_file(path, device=device_hint)
|
|
149
|
+
|
|
150
|
+
instance = cls(
|
|
151
|
+
labels=labels,
|
|
152
|
+
model_type=model_type,
|
|
153
|
+
load_imagenet_weights=load_imagenet,
|
|
154
|
+
track_hparams=False,
|
|
155
|
+
*args,
|
|
156
|
+
**kwargs,
|
|
157
|
+
)
|
|
158
|
+
missing, unexpected = instance.load_state_dict(state_dict, strict=strict)
|
|
159
|
+
|
|
160
|
+
if missing:
|
|
161
|
+
logger.warning("Missing parameters when loading %s: %s", path, missing)
|
|
162
|
+
if unexpected:
|
|
163
|
+
logger.warning("Unexpected parameters when loading %s: %s", path, unexpected)
|
|
164
|
+
|
|
165
|
+
instance.to(device)
|
|
166
|
+
return instance
|
|
167
|
+
|
|
168
|
+
return super(MultiLabelClassificationNet, cls).load_from_checkpoint(
|
|
82
169
|
checkpoint_path, *args, **kwargs
|
|
83
170
|
)
|
|
84
|
-
return instance
|
|
85
171
|
|
|
86
172
|
def forward(self, x): # pylint: disable=arguments-differ
|
|
87
173
|
x = self.model(x)
|
|
@@ -113,18 +199,24 @@ class MultiLabelClassificationNet(LightningModule):
|
|
|
113
199
|
|
|
114
200
|
def validation_epoch_end(self, _outputs):
|
|
115
201
|
"""Called at the end of validation to aggregate outputs"""
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
metrics = calculate_metrics(
|
|
120
|
-
for key,
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
202
|
+
val_preds_np = np.concatenate(self.val_preds)
|
|
203
|
+
val_targets_np = np.concatenate(self.val_targets)
|
|
204
|
+
|
|
205
|
+
metrics = calculate_metrics(val_preds_np, val_targets_np, threshold=0.5)
|
|
206
|
+
for key, metric_value in metrics.items():
|
|
207
|
+
if isinstance(metric_value, np.ndarray):
|
|
208
|
+
processed_value = metric_value.tolist()
|
|
209
|
+
elif isinstance(metric_value, (list, tuple)):
|
|
210
|
+
processed_value = list(metric_value)
|
|
211
|
+
else:
|
|
212
|
+
processed_value = float(metric_value)
|
|
213
|
+
|
|
214
|
+
if isinstance(processed_value, list):
|
|
215
|
+
for i, single_value in enumerate(processed_value):
|
|
124
216
|
name = "val/" + f"{key}/{self.labels[i]}"
|
|
125
217
|
self.log(
|
|
126
218
|
name,
|
|
127
|
-
|
|
219
|
+
float(single_value),
|
|
128
220
|
on_epoch=True,
|
|
129
221
|
on_step=METRICS_ON_STEP,
|
|
130
222
|
prog_bar=False,
|
|
@@ -132,7 +224,11 @@ class MultiLabelClassificationNet(LightningModule):
|
|
|
132
224
|
else:
|
|
133
225
|
name = "val/" + f"{key}"
|
|
134
226
|
self.log(
|
|
135
|
-
name,
|
|
227
|
+
name,
|
|
228
|
+
float(processed_value),
|
|
229
|
+
on_epoch=True,
|
|
230
|
+
on_step=METRICS_ON_STEP,
|
|
231
|
+
prog_bar=True,
|
|
136
232
|
)
|
|
137
233
|
|
|
138
234
|
self.val_preds = []
|