endoreg-db 0.5.3__py3-none-any.whl → 0.6.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of endoreg-db might be problematic. Click here for more details.
- endoreg_db/admin.py +90 -1
- endoreg_db/case_generator/case_generator.py +159 -0
- endoreg_db/case_generator/lab_sample_factory.py +33 -0
- endoreg_db/case_generator/utils.py +30 -0
- endoreg_db/data/__init__.py +50 -4
- endoreg_db/data/ai_model/data.yaml +7 -0
- endoreg_db/data/{label → ai_model_label}/label/data.yaml +27 -1
- endoreg_db/data/ai_model_label/label-set/data.yaml +21 -0
- endoreg_db/data/ai_model_meta/default_multilabel_classification.yaml +5 -0
- endoreg_db/data/ai_model_video_segmentation_label/base_segmentation.yaml +176 -0
- endoreg_db/data/ai_model_video_segmentation_labelset/data.yaml +20 -0
- endoreg_db/data/center/data.yaml +35 -5
- endoreg_db/data/contraindication/bleeding.yaml +11 -0
- endoreg_db/data/distribution/numeric/data.yaml +14 -0
- endoreg_db/data/endoscope/data.yaml +93 -0
- endoreg_db/data/examination_indication/endoscopy.yaml +8 -0
- endoreg_db/data/examination_indication_classification/endoscopy.yaml +8 -0
- endoreg_db/data/examination_indication_classification_choice/endoscopy.yaml +101 -0
- endoreg_db/data/finding/data.yaml +141 -0
- endoreg_db/data/finding_intervention/endoscopy.yaml +138 -0
- endoreg_db/data/finding_intervention_type/endoscopy.yaml +15 -0
- endoreg_db/data/finding_location_classification/colonoscopy.yaml +46 -0
- endoreg_db/data/finding_location_classification_choice/colonoscopy.yaml +240 -0
- endoreg_db/data/finding_morphology_classification/colonoscopy.yaml +48 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_circularity_default.yaml +34 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_nice.yaml +20 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_paris.yaml +65 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_planarity_default.yaml +56 -0
- endoreg_db/data/finding_morphology_classification_choice/colon_lesion_surface_intact_default.yaml +39 -0
- endoreg_db/data/finding_morphology_classification_choice/colonoscopy_size.yaml +57 -0
- endoreg_db/data/finding_morphology_classification_type/colonoscopy.yaml +79 -0
- endoreg_db/data/finding_type/data.yaml +30 -0
- endoreg_db/data/gender/data.yaml +17 -0
- endoreg_db/data/lab_value/cardiac_enzymes.yaml +7 -1
- endoreg_db/data/lab_value/coagulation.yaml +6 -1
- endoreg_db/data/lab_value/electrolytes.yaml +39 -1
- endoreg_db/data/lab_value/gastrointestinal_function.yaml +12 -0
- endoreg_db/data/lab_value/hematology.yaml +17 -2
- endoreg_db/data/lab_value/hormones.yaml +6 -0
- endoreg_db/data/lab_value/lipids.yaml +12 -3
- endoreg_db/data/lab_value/misc.yaml +5 -2
- endoreg_db/data/lab_value/renal_function.yaml +2 -1
- endoreg_db/data/lx_client_tag/base.yaml +54 -0
- endoreg_db/data/lx_client_type/base.yaml +30 -0
- endoreg_db/data/lx_permission/base.yaml +24 -0
- endoreg_db/data/lx_permission/endoreg.yaml +52 -0
- endoreg_db/data/medication_indication/anticoagulation.yaml +44 -49
- endoreg_db/data/names_first/first_names.yaml +51 -0
- endoreg_db/data/names_last/last_names.yaml +51 -0
- endoreg_db/data/network_device/data.yaml +30 -0
- endoreg_db/data/organ/data.yaml +29 -0
- endoreg_db/data/pdf_type/data.yaml +2 -1
- endoreg_db/data/report_reader_flag/ukw-examination-generic.yaml +4 -0
- endoreg_db/forms/__init__.py +3 -1
- endoreg_db/forms/examination_form.py +11 -0
- endoreg_db/forms/patient_finding_intervention_form.py +19 -0
- endoreg_db/forms/patient_form.py +26 -0
- endoreg_db/management/commands/__init__.py +0 -0
- endoreg_db/management/commands/load_ai_model_data.py +57 -23
- endoreg_db/management/commands/load_ai_model_label_data.py +59 -0
- endoreg_db/management/commands/load_base_db_data.py +160 -118
- endoreg_db/management/commands/{load_endoscope_type_data.py → load_contraindication_data.py} +3 -7
- endoreg_db/management/commands/load_disease_data.py +29 -7
- endoreg_db/management/commands/load_endoscope_data.py +68 -0
- endoreg_db/management/commands/load_examination_indication_data.py +65 -0
- endoreg_db/management/commands/load_finding_data.py +171 -0
- endoreg_db/management/commands/load_lab_value_data.py +3 -3
- endoreg_db/management/commands/load_lx_data.py +64 -0
- endoreg_db/management/commands/load_medication_data.py +83 -21
- endoreg_db/management/commands/load_name_data.py +37 -0
- endoreg_db/management/commands/{load_endoscopy_processor_data.py → load_organ_data.py} +7 -9
- endoreg_db/migrations/0001_initial.py +1206 -728
- endoreg_db/migrations/0002_alter_frame_image_alter_rawframe_image.py +23 -0
- endoreg_db/migrations/0003_alter_frame_image_alter_rawframe_image.py +23 -0
- endoreg_db/migrations/0004_alter_rawvideofile_file_alter_video_file.py +25 -0
- endoreg_db/migrations/0005_rawvideofile_frame_count_and_more.py +33 -0
- endoreg_db/migrations/0006_frame_extracted_rawframe_extracted.py +23 -0
- endoreg_db/migrations/0007_rename_pseudo_patient_video_patient_and_more.py +24 -0
- endoreg_db/migrations/0008_remove_reportfile_patient_examination_and_more.py +48 -0
- endoreg_db/models/__init__.py +331 -28
- endoreg_db/models/ai_model/__init__.py +1 -0
- endoreg_db/models/ai_model/ai_model.py +103 -0
- endoreg_db/models/ai_model/lightning/__init__.py +3 -0
- endoreg_db/models/ai_model/lightning/inference_dataset.py +53 -0
- endoreg_db/models/ai_model/lightning/multilabel_classification_net.py +155 -0
- endoreg_db/models/ai_model/lightning/postprocess.py +53 -0
- endoreg_db/models/ai_model/lightning/predict.py +172 -0
- endoreg_db/models/ai_model/lightning/prediction_visualizer.py +55 -0
- endoreg_db/models/ai_model/lightning/preprocess.py +68 -0
- endoreg_db/models/ai_model/lightning/run_visualizer.py +21 -0
- endoreg_db/models/ai_model/model_meta.py +232 -6
- endoreg_db/models/ai_model/model_type.py +13 -3
- endoreg_db/models/annotation/__init__.py +31 -2
- endoreg_db/models/annotation/anonymized_image_annotation.py +73 -18
- endoreg_db/models/annotation/binary_classification_annotation_task.py +94 -57
- endoreg_db/models/annotation/image_classification.py +73 -14
- endoreg_db/models/annotation/video_segmentation_annotation.py +52 -0
- endoreg_db/models/annotation/video_segmentation_labelset.py +20 -0
- endoreg_db/models/case/__init__.py +1 -0
- endoreg_db/models/{persons/patient/case → case}/case.py +4 -0
- endoreg_db/models/case_template/__init__.py +10 -1
- endoreg_db/models/case_template/case_template.py +57 -13
- endoreg_db/models/case_template/case_template_rule.py +5 -5
- endoreg_db/models/case_template/case_template_rule_value.py +19 -4
- endoreg_db/models/center/__init__.py +7 -0
- endoreg_db/models/center/center.py +31 -5
- endoreg_db/models/center/center_product.py +0 -1
- endoreg_db/models/center/center_resource.py +16 -2
- endoreg_db/models/center/center_waste.py +6 -1
- endoreg_db/models/contraindication/__init__.py +21 -0
- endoreg_db/models/data_file/__init__.py +38 -5
- endoreg_db/models/data_file/base_classes/__init__.py +6 -1
- endoreg_db/models/data_file/base_classes/abstract_frame.py +64 -15
- endoreg_db/models/data_file/base_classes/abstract_pdf.py +136 -0
- endoreg_db/models/data_file/base_classes/abstract_video.py +744 -138
- endoreg_db/models/data_file/base_classes/frame_helpers.py +17 -0
- endoreg_db/models/data_file/base_classes/prepare_bulk_frames.py +19 -0
- endoreg_db/models/data_file/base_classes/utils.py +80 -0
- endoreg_db/models/data_file/frame.py +22 -38
- endoreg_db/models/data_file/import_classes/__init__.py +4 -18
- endoreg_db/models/data_file/import_classes/raw_pdf.py +162 -90
- endoreg_db/models/data_file/import_classes/raw_video.py +239 -294
- endoreg_db/models/data_file/metadata/__init__.py +10 -0
- endoreg_db/models/data_file/metadata/pdf_meta.py +4 -0
- endoreg_db/models/data_file/metadata/sensitive_meta.py +265 -6
- endoreg_db/models/data_file/metadata/video_meta.py +116 -50
- endoreg_db/models/data_file/report_file.py +30 -63
- endoreg_db/models/data_file/video/__init__.py +6 -2
- endoreg_db/models/data_file/video/video.py +187 -16
- endoreg_db/models/data_file/video_segment.py +162 -55
- endoreg_db/models/disease.py +25 -2
- endoreg_db/models/emission/__init__.py +5 -1
- endoreg_db/models/emission/emission_factor.py +71 -6
- endoreg_db/models/event.py +51 -0
- endoreg_db/models/examination/__init__.py +6 -1
- endoreg_db/models/examination/examination.py +53 -12
- endoreg_db/models/examination/examination_indication.py +170 -0
- endoreg_db/models/examination/examination_time.py +31 -5
- endoreg_db/models/examination/examination_time_type.py +28 -4
- endoreg_db/models/examination/examination_type.py +28 -6
- endoreg_db/models/finding/__init__.py +11 -0
- endoreg_db/models/finding/finding.py +75 -0
- endoreg_db/models/finding/finding_intervention.py +60 -0
- endoreg_db/models/finding/finding_location_classification.py +94 -0
- endoreg_db/models/finding/finding_morphology_classification.py +89 -0
- endoreg_db/models/finding/finding_type.py +22 -0
- endoreg_db/models/hardware/endoscope.py +16 -0
- endoreg_db/models/hardware/endoscopy_processor.py +31 -19
- endoreg_db/models/label/label.py +35 -7
- endoreg_db/models/laboratory/lab_value.py +12 -3
- endoreg_db/models/logging/__init__.py +8 -1
- endoreg_db/models/lx/__init__.py +4 -0
- endoreg_db/models/lx/client.py +57 -0
- endoreg_db/models/lx/identity.py +34 -0
- endoreg_db/models/lx/permission.py +18 -0
- endoreg_db/models/lx/user.py +16 -0
- endoreg_db/models/medication/__init__.py +19 -1
- endoreg_db/models/medication/medication.py +7 -122
- endoreg_db/models/medication/medication_indication.py +50 -0
- endoreg_db/models/medication/medication_indication_type.py +34 -0
- endoreg_db/models/medication/medication_intake_time.py +26 -0
- endoreg_db/models/medication/medication_schedule.py +37 -0
- endoreg_db/models/network/__init__.py +7 -1
- endoreg_db/models/network/network_device.py +13 -8
- endoreg_db/models/organ/__init__.py +38 -0
- endoreg_db/models/other/__init__.py +19 -1
- endoreg_db/models/other/distribution/__init__.py +44 -0
- endoreg_db/models/other/distribution/base_value_distribution.py +20 -0
- endoreg_db/models/other/distribution/date_value_distribution.py +91 -0
- endoreg_db/models/other/distribution/multiple_categorical_value_distribution.py +32 -0
- endoreg_db/models/other/distribution/numeric_value_distribution.py +97 -0
- endoreg_db/models/other/distribution/single_categorical_value_distribution.py +22 -0
- endoreg_db/models/other/distribution.py +1 -211
- endoreg_db/models/other/material.py +4 -0
- endoreg_db/models/other/transport_route.py +2 -1
- endoreg_db/models/patient/__init__.py +24 -0
- endoreg_db/models/patient/patient_examination.py +182 -0
- endoreg_db/models/patient/patient_finding.py +143 -0
- endoreg_db/models/patient/patient_finding_intervention.py +26 -0
- endoreg_db/models/patient/patient_finding_location.py +120 -0
- endoreg_db/models/patient/patient_finding_morphology.py +166 -0
- endoreg_db/models/persons/__init__.py +29 -2
- endoreg_db/models/persons/examiner/examiner.py +48 -4
- endoreg_db/models/persons/patient/__init__.py +1 -1
- endoreg_db/models/persons/patient/patient.py +227 -54
- endoreg_db/models/persons/patient/patient_disease.py +6 -0
- endoreg_db/models/persons/patient/patient_event.py +31 -1
- endoreg_db/models/persons/patient/patient_examination_indication.py +32 -0
- endoreg_db/models/persons/patient/patient_lab_sample.py +4 -2
- endoreg_db/models/persons/patient/patient_lab_value.py +37 -16
- endoreg_db/models/persons/patient/patient_medication.py +27 -12
- endoreg_db/models/persons/patient/patient_medication_schedule.py +62 -2
- endoreg_db/models/prediction/__init__.py +7 -1
- endoreg_db/models/prediction/image_classification.py +20 -6
- endoreg_db/models/prediction/video_prediction_meta.py +151 -89
- endoreg_db/models/product/__init__.py +10 -1
- endoreg_db/models/product/product.py +15 -2
- endoreg_db/models/product/product_group.py +8 -0
- endoreg_db/models/product/product_material.py +4 -0
- endoreg_db/models/product/product_weight.py +12 -0
- endoreg_db/models/product/reference_product.py +19 -3
- endoreg_db/models/quiz/__init__.py +8 -1
- endoreg_db/models/report_reader/__init__.py +6 -1
- endoreg_db/serializers/__init__.py +1 -1
- endoreg_db/serializers/annotation.py +2 -5
- endoreg_db/serializers/frame.py +1 -5
- endoreg_db/serializers/patient.py +26 -3
- endoreg_db/serializers/prediction.py +2 -7
- endoreg_db/serializers/raw_video_meta_validation.py +13 -0
- endoreg_db/serializers/video.py +6 -13
- endoreg_db/serializers/video_segmentation.py +492 -0
- endoreg_db/templates/admin/patient_finding_intervention.html +253 -0
- endoreg_db/templates/admin/start_examination.html +12 -0
- endoreg_db/templates/timeline.html +176 -0
- endoreg_db/urls.py +173 -0
- endoreg_db/utils/__init__.py +36 -1
- endoreg_db/utils/dataloader.py +45 -19
- endoreg_db/utils/dates.py +39 -0
- endoreg_db/utils/hashs.py +122 -4
- endoreg_db/utils/names.py +74 -0
- endoreg_db/utils/parse_and_generate_yaml.py +46 -0
- endoreg_db/utils/pydantic_models/__init__.py +6 -0
- endoreg_db/utils/pydantic_models/db_config.py +57 -0
- endoreg_db/utils/validate_endo_roi.py +19 -0
- endoreg_db/utils/validate_subcategory_dict.py +91 -0
- endoreg_db/utils/video/__init__.py +13 -0
- endoreg_db/utils/video/extract_frames.py +121 -0
- endoreg_db/utils/video/transcode_videofile.py +111 -0
- endoreg_db/views/__init__.py +2 -0
- endoreg_db/views/csrf.py +7 -0
- endoreg_db/views/patient_views.py +90 -0
- endoreg_db/views/raw_video_meta_validation_views.py +38 -0
- endoreg_db/views/report_views.py +96 -0
- endoreg_db/views/video_segmentation_views.py +149 -0
- endoreg_db/views/views_for_timeline.py +46 -0
- endoreg_db/views.py +0 -3
- endoreg_db-0.6.1.dist-info/METADATA +151 -0
- endoreg_db-0.6.1.dist-info/RECORD +420 -0
- {endoreg_db-0.5.3.dist-info → endoreg_db-0.6.1.dist-info}/WHEEL +1 -1
- endoreg_db/data/active_model/data.yaml +0 -3
- endoreg_db/data/label/label-set/data.yaml +0 -18
- endoreg_db/management/commands/delete_legacy_images.py +0 -19
- endoreg_db/management/commands/delete_legacy_videos.py +0 -17
- endoreg_db/management/commands/extract_legacy_video_frames.py +0 -18
- endoreg_db/management/commands/import_legacy_images.py +0 -94
- endoreg_db/management/commands/import_legacy_videos.py +0 -76
- endoreg_db/management/commands/load_label_data.py +0 -67
- endoreg_db/migrations/0002_anonymizedimagelabel_anonymousimageannotation_and_more.py +0 -55
- endoreg_db/migrations/0003_anonymousimageannotation_original_image_url_and_more.py +0 -39
- endoreg_db/migrations/0004_alter_rawpdffile_file.py +0 -20
- endoreg_db/migrations/0005_uploadedfile_alter_rawpdffile_file_anonymizedfile.py +0 -40
- endoreg_db/migrations/0006_alter_rawpdffile_file.py +0 -20
- endoreg_db/migrations/0007_networkdevicelogentry_datetime_and_more.py +0 -43
- endoreg_db/migrations/0008_networkdevicelogentry_aglnet_ip_and_more.py +0 -28
- endoreg_db/migrations/0009_alter_networkdevicelogentry_vpn_service_status.py +0 -18
- endoreg_db/migrations/0010_remove_networkdevicelogentry_hostname.py +0 -17
- endoreg_db/models/legacy_data/__init__.py +0 -3
- endoreg_db/models/legacy_data/image.py +0 -34
- endoreg_db/models/patient_examination/__init__.py +0 -35
- endoreg_db/utils/video_metadata.py +0 -87
- endoreg_db-0.5.3.dist-info/METADATA +0 -28
- endoreg_db-0.5.3.dist-info/RECORD +0 -319
- /endoreg_db/{models/persons/patient/case → case_generator}/__init__.py +0 -0
- /endoreg_db/data/{label → ai_model_label}/label-type/data.yaml +0 -0
- /endoreg_db/data/{model_type → ai_model_type}/data.yaml +0 -0
- /endoreg_db/{data/distribution/numeric/.init → management/__init__.py} +0 -0
- /endoreg_db/management/commands/{load_report_reader_flag.py → load_report_reader_flag_data.py} +0 -0
- {endoreg_db-0.5.3.dist-info → endoreg_db-0.6.1.dist-info}/licenses/LICENSE +0 -0
|
@@ -1,25 +1,196 @@
|
|
|
1
|
-
from ..base_classes import AbstractVideo
|
|
2
1
|
from django.db import models
|
|
3
|
-
|
|
4
2
|
from endoreg_db.models.data_file.frame import Frame
|
|
5
|
-
from
|
|
3
|
+
from django.core.validators import FileExtensionValidator
|
|
4
|
+
from django.core.files.storage import FileSystemStorage
|
|
5
|
+
from typing import TYPE_CHECKING
|
|
6
|
+
import cv2
|
|
7
|
+
from ..base_classes import AbstractVideoFile
|
|
8
|
+
from ..base_classes.utils import (
|
|
9
|
+
VIDEO_DIR_NAME,
|
|
10
|
+
STORAGE_LOCATION,
|
|
11
|
+
FRAME_PROCESSING_BATCH_SIZE,
|
|
12
|
+
)
|
|
13
|
+
|
|
14
|
+
if TYPE_CHECKING:
|
|
15
|
+
from django.db.models import QuerySet
|
|
16
|
+
from endoreg_db.models import (
|
|
17
|
+
LabelVideoSegment,
|
|
18
|
+
VideoImportMeta,
|
|
19
|
+
SensitiveMeta,
|
|
20
|
+
Patient,
|
|
21
|
+
PatientExamination,
|
|
22
|
+
RawVideoFile,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class Video(AbstractVideoFile):
|
|
27
|
+
file = models.FileField(
|
|
28
|
+
upload_to=VIDEO_DIR_NAME,
|
|
29
|
+
validators=[FileExtensionValidator(allowed_extensions=["mp4"])], # FIXME
|
|
30
|
+
storage=FileSystemStorage(location=STORAGE_LOCATION.resolve().as_posix()),
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
patient = models.ForeignKey(
|
|
34
|
+
"Patient",
|
|
35
|
+
on_delete=models.CASCADE,
|
|
36
|
+
blank=True,
|
|
37
|
+
null=True,
|
|
38
|
+
related_name="videos",
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
examination = models.ForeignKey(
|
|
42
|
+
"PatientExamination",
|
|
43
|
+
on_delete=models.SET_NULL,
|
|
44
|
+
blank=True,
|
|
45
|
+
null=True,
|
|
46
|
+
related_name="videos",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# Deprecate and move to video meta?
|
|
50
|
+
date = models.DateField(blank=True, null=True)
|
|
51
|
+
suffix = models.CharField(max_length=255, blank=True, null=True)
|
|
52
|
+
fps = models.FloatField(blank=True, null=True)
|
|
53
|
+
duration = models.FloatField(blank=True, null=True)
|
|
54
|
+
width = models.IntegerField(blank=True, null=True)
|
|
55
|
+
height = models.IntegerField(blank=True, null=True)
|
|
56
|
+
endoscope_image_x = models.IntegerField(blank=True, null=True)
|
|
57
|
+
endoscope_image_y = models.IntegerField(blank=True, null=True)
|
|
58
|
+
endoscope_image_width = models.IntegerField(blank=True, null=True)
|
|
59
|
+
endoscope_image_height = models.IntegerField(blank=True, null=True)
|
|
60
|
+
|
|
61
|
+
state_frames_extracted = models.BooleanField(default=False)
|
|
62
|
+
|
|
63
|
+
meta = models.JSONField(blank=True, null=True)
|
|
64
|
+
|
|
65
|
+
sensitive_meta = models.ForeignKey(
|
|
66
|
+
"SensitiveMeta",
|
|
67
|
+
on_delete=models.CASCADE,
|
|
68
|
+
related_name="videos",
|
|
69
|
+
null=True,
|
|
70
|
+
blank=True,
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
import_meta = models.OneToOneField(
|
|
74
|
+
"VideoImportMeta", on_delete=models.CASCADE, blank=True, null=True
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
if TYPE_CHECKING:
|
|
78
|
+
import_meta: "VideoImportMeta"
|
|
79
|
+
patient: "Patient"
|
|
80
|
+
examination: "PatientExamination"
|
|
81
|
+
frames: "QuerySet[Frame]"
|
|
82
|
+
label_video_segments: (
|
|
83
|
+
"QuerySet[LabelVideoSegment]" # Foreign key in LabelVideoSegment.video
|
|
84
|
+
)
|
|
85
|
+
sensitive_meta: "SensitiveMeta"
|
|
86
|
+
raw_videos: "QuerySet[RawVideoFile]"
|
|
87
|
+
|
|
88
|
+
# override the save method to set fps if not set
|
|
89
|
+
def save(self, *args, **kwargs):
|
|
90
|
+
if not self.fps:
|
|
91
|
+
self.fps = self.get_fps()
|
|
92
|
+
super().save(*args, **kwargs)
|
|
93
|
+
|
|
94
|
+
def label_segments_to_frame_annotations(self):
|
|
95
|
+
"""
|
|
96
|
+
Generate annotations for all label video segments.
|
|
97
|
+
"""
|
|
98
|
+
for lvs in self.label_video_segments.all():
|
|
99
|
+
lvs.generate_annotations()
|
|
100
|
+
|
|
101
|
+
def sync_from_raw_video(self):
|
|
102
|
+
"""
|
|
103
|
+
Sync metadata from the associated raw video file.
|
|
104
|
+
"""
|
|
105
|
+
from endoreg_db.models import RawVideoFile, LabelVideoSegment
|
|
106
|
+
|
|
107
|
+
raw_video: RawVideoFile = self.raw_videos.first()
|
|
108
|
+
|
|
109
|
+
assert isinstance(raw_video, RawVideoFile), "Raw video is not a file"
|
|
110
|
+
|
|
111
|
+
self.predictions = raw_video.predictions
|
|
112
|
+
self.readable_predictions = raw_video.readable_predictions
|
|
113
|
+
self.sequences = raw_video.sequences
|
|
114
|
+
|
|
115
|
+
label_video_segments = raw_video.label_video_segments.all()
|
|
116
|
+
|
|
117
|
+
label_video_segments = [
|
|
118
|
+
LabelVideoSegment.from_raw(
|
|
119
|
+
video=self, raw_label_video_segment=raw_label_video_segment
|
|
120
|
+
)
|
|
121
|
+
# LabelVideoSegment.from_raw(self, raw_label_video_segment)
|
|
122
|
+
for raw_label_video_segment in label_video_segments
|
|
123
|
+
]
|
|
124
|
+
|
|
125
|
+
for lvs in label_video_segments:
|
|
126
|
+
lvs.save()
|
|
127
|
+
|
|
128
|
+
self.state_histology_required = raw_video.state_histology_required
|
|
129
|
+
self.state_histology_available = raw_video.state_histology_available
|
|
130
|
+
self.state_follow_up_intervention_required = (
|
|
131
|
+
raw_video.state_follow_up_intervention_required
|
|
132
|
+
)
|
|
133
|
+
self.state_follow_up_intervention_available = (
|
|
134
|
+
raw_video.state_follow_up_intervention_available
|
|
135
|
+
)
|
|
136
|
+
self.state_dataset_complete = raw_video.state_dataset_complete
|
|
137
|
+
|
|
138
|
+
self.save()
|
|
139
|
+
|
|
140
|
+
## Deprecated
|
|
141
|
+
# def get_roi_endoscope_image(self):
|
|
142
|
+
# return {
|
|
143
|
+
# "x": self.endoscope_image_content_x,
|
|
144
|
+
# "y": self.endoscope_image_content_y,
|
|
145
|
+
# "width": self.endoscope_image_content_width,
|
|
146
|
+
# "height": self.endoscope_image_content_height,
|
|
147
|
+
# }
|
|
148
|
+
|
|
149
|
+
# def initialize_metadata_in_db(self, video_meta=None):
|
|
150
|
+
# if not video_meta:
|
|
151
|
+
# video_meta = self.meta
|
|
152
|
+
# self.set_examination_date_from_video_meta(video_meta)
|
|
153
|
+
# self.patient, created = self.get_or_create_patient(video_meta)
|
|
154
|
+
# self.save()
|
|
155
|
+
|
|
156
|
+
# def get_or_create_patient(self, video_meta=None):
|
|
157
|
+
# from ...persons import Patient
|
|
6
158
|
|
|
7
|
-
|
|
159
|
+
# if not video_meta:
|
|
160
|
+
# video_meta = self.meta
|
|
8
161
|
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
return Video
|
|
162
|
+
# patient_first_name = video_meta["patient_first_name"]
|
|
163
|
+
# patient_last_name = video_meta["patient_last_name"]
|
|
164
|
+
# patient_dob = video_meta["patient_dob"]
|
|
13
165
|
|
|
14
|
-
|
|
15
|
-
|
|
166
|
+
# # assert that we got all the necessary information
|
|
167
|
+
# assert patient_first_name and patient_last_name and patient_dob, (
|
|
168
|
+
# "Missing patient information"
|
|
169
|
+
# )
|
|
16
170
|
|
|
171
|
+
# patient, created = Patient.objects.get_or_create(
|
|
172
|
+
# first_name=patient_first_name, last_name=patient_last_name, dob=patient_dob
|
|
173
|
+
# )
|
|
17
174
|
|
|
18
|
-
|
|
19
|
-
file = models.FileField(upload_to="legacy_videos", blank=True, null=True)
|
|
175
|
+
# return patient, created
|
|
20
176
|
|
|
21
|
-
|
|
22
|
-
|
|
177
|
+
# DEPRECATED
|
|
178
|
+
# def set_examination_date_from_video_meta(self, video_meta=None):
|
|
179
|
+
# if not video_meta:
|
|
180
|
+
# video_meta = self.meta
|
|
181
|
+
# date_str = video_meta["examination_date"] # e.g. 2020-01-01
|
|
182
|
+
# if date_str:
|
|
183
|
+
# self.date = date.fromisoformat(date_str)
|
|
184
|
+
# self.save()
|
|
23
185
|
|
|
24
|
-
def
|
|
25
|
-
|
|
186
|
+
def initialize_video_specs(self):
|
|
187
|
+
"""
|
|
188
|
+
Initialize and save video metadata like framerate, dimensions, and duration.
|
|
189
|
+
"""
|
|
190
|
+
video = cv2.VideoCapture(self.file.path)
|
|
191
|
+
self.fps = video.get(cv2.CAP_PROP_FPS)
|
|
192
|
+
self.width = int(video.get(cv2.CAP_PROP_FRAME_WIDTH))
|
|
193
|
+
self.height = int(video.get(cv2.CAP_PROP_FRAME_HEIGHT))
|
|
194
|
+
self.duration = video.get(cv2.CAP_PROP_FRAME_COUNT) / self.fps
|
|
195
|
+
self.save()
|
|
196
|
+
video.release()
|
|
@@ -1,6 +1,19 @@
|
|
|
1
1
|
from django.db import models
|
|
2
2
|
import numpy as np
|
|
3
3
|
from ..annotation import ImageClassificationAnnotation
|
|
4
|
+
from typing import TYPE_CHECKING, Union
|
|
5
|
+
from tqdm import tqdm
|
|
6
|
+
|
|
7
|
+
if TYPE_CHECKING:
|
|
8
|
+
from endoreg_db.models import (
|
|
9
|
+
RawVideoFile,
|
|
10
|
+
Video,
|
|
11
|
+
Label,
|
|
12
|
+
InformationSource,
|
|
13
|
+
VideoPredictionMeta,
|
|
14
|
+
RawVideoPredictionMeta,
|
|
15
|
+
)
|
|
16
|
+
|
|
4
17
|
|
|
5
18
|
def find_segments_in_prediction_array(prediction_array: np.array, min_frame_len: int):
|
|
6
19
|
"""
|
|
@@ -8,100 +21,194 @@ def find_segments_in_prediction_array(prediction_array: np.array, min_frame_len:
|
|
|
8
21
|
Returns a list of tuples (start_frame_number, end_frame_number) that represent the segments.
|
|
9
22
|
"""
|
|
10
23
|
# Add False to the beginning and end to detect changes at the array boundaries
|
|
11
|
-
padded_prediction = np.pad(
|
|
12
|
-
|
|
24
|
+
padded_prediction = np.pad(
|
|
25
|
+
prediction_array, (1, 1), "constant", constant_values=False
|
|
26
|
+
)
|
|
27
|
+
|
|
13
28
|
# Find the start points and end points of the segments
|
|
14
29
|
diffs = np.diff(padded_prediction.astype(int))
|
|
15
30
|
segment_starts = np.where(diffs == 1)[0]
|
|
16
31
|
segment_ends = np.where(diffs == -1)[0]
|
|
17
|
-
|
|
32
|
+
|
|
18
33
|
# Filter segments based on min_frame_len
|
|
19
|
-
segments = [
|
|
20
|
-
|
|
34
|
+
segments = [
|
|
35
|
+
(start, end)
|
|
36
|
+
for start, end in zip(segment_starts, segment_ends)
|
|
37
|
+
if end - start >= min_frame_len
|
|
38
|
+
]
|
|
39
|
+
|
|
21
40
|
return segments
|
|
22
41
|
|
|
42
|
+
|
|
23
43
|
class AbstractLabelVideoSegment(models.Model):
|
|
24
|
-
video = None # Placeholder for the video field, to be defined in derived classes
|
|
25
|
-
prediction_meta = None # Placeholder for the prediction_meta field, to be defined in derived classes
|
|
26
44
|
start_frame_number = models.IntegerField()
|
|
27
45
|
end_frame_number = models.IntegerField()
|
|
28
|
-
source = models.ForeignKey(
|
|
29
|
-
|
|
46
|
+
source = models.ForeignKey(
|
|
47
|
+
"InformationSource", on_delete=models.SET_NULL, null=True
|
|
48
|
+
)
|
|
49
|
+
label = models.ForeignKey("Label", on_delete=models.SET_NULL, null=True, blank=True)
|
|
50
|
+
|
|
51
|
+
if TYPE_CHECKING:
|
|
52
|
+
label: "Label"
|
|
53
|
+
source: "InformationSource"
|
|
54
|
+
prediction_meta: Union["RawVideoPredictionMeta", "VideoPredictionMeta"]
|
|
55
|
+
video: Union[Video, RawVideoFile]
|
|
30
56
|
|
|
31
57
|
class Meta:
|
|
32
58
|
abstract = True
|
|
33
59
|
|
|
34
60
|
def __str__(self):
|
|
35
|
-
|
|
36
|
-
|
|
61
|
+
video = self.video
|
|
62
|
+
label = self.label
|
|
63
|
+
|
|
64
|
+
str_repr = (
|
|
65
|
+
video.file.path
|
|
66
|
+
+ " Label - "
|
|
67
|
+
+ label.name
|
|
68
|
+
+ " - "
|
|
69
|
+
+ str(self.start_frame_number)
|
|
70
|
+
+ " - "
|
|
71
|
+
+ str(self.end_frame_number)
|
|
72
|
+
)
|
|
73
|
+
assert isinstance(str_repr, str), "String representation is not a string"
|
|
74
|
+
return str_repr
|
|
75
|
+
|
|
76
|
+
def get_model_meta(self):
|
|
77
|
+
return self.prediction_meta.model_meta
|
|
78
|
+
|
|
37
79
|
def get_frames(self):
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
80
|
+
video = self.video
|
|
81
|
+
return video.get_frame_range(self.start_frame_number, self.end_frame_number)
|
|
82
|
+
|
|
83
|
+
def get_annotations(self):
|
|
41
84
|
frames = self.get_frames()
|
|
42
|
-
annotations = ImageClassificationAnnotation.objects.filter(
|
|
85
|
+
annotations = ImageClassificationAnnotation.objects.filter(
|
|
86
|
+
frame__in=frames, label=self.label
|
|
87
|
+
)
|
|
43
88
|
|
|
44
89
|
return annotations
|
|
45
|
-
|
|
46
|
-
def get_frames_without_annotation(self):
|
|
47
|
-
"""
|
|
48
|
-
Get a frame without an annotation.
|
|
49
|
-
"""
|
|
50
|
-
assert 1 == 2, "This method should be overridden in derived classes"
|
|
51
90
|
|
|
52
91
|
def get_segment_len_in_s(self):
|
|
53
|
-
return (self.end_frame_number - self.start_frame_number) / self.video.
|
|
92
|
+
return (self.end_frame_number - self.start_frame_number) / self.video.get_fps()
|
|
54
93
|
|
|
55
|
-
|
|
56
|
-
video = models.ForeignKey("LegacyVideo", on_delete=models.CASCADE)
|
|
57
|
-
prediction_meta = models.ForeignKey("LegacyVideoPredictionMeta", on_delete=models.CASCADE, related_name="video_segments")
|
|
58
|
-
|
|
59
|
-
def get_video_model(self):
|
|
60
|
-
from endoreg_db.models.data_file.video import LegacyVideo
|
|
61
|
-
return LegacyVideo
|
|
62
|
-
|
|
63
|
-
def get_annotations(self) -> ImageClassificationAnnotation.objects:
|
|
64
|
-
frames = self.get_frames()
|
|
65
|
-
annotations = ImageClassificationAnnotation.objects.filter(legacy_frame__in=frames, label=self.label)
|
|
66
|
-
|
|
67
|
-
return annotations
|
|
68
|
-
|
|
69
|
-
def get_frames_without_annotation(self, n_frames):
|
|
94
|
+
def get_frames_without_annotation(self, n_frames: int):
|
|
70
95
|
"""
|
|
71
96
|
Get a frame without an annotation.
|
|
72
97
|
"""
|
|
73
98
|
frames = self.get_frames()
|
|
74
|
-
annotations = ImageClassificationAnnotation.objects.filter(
|
|
99
|
+
annotations = ImageClassificationAnnotation.objects.filter(
|
|
100
|
+
frame__in=frames, label=self.label
|
|
101
|
+
)
|
|
75
102
|
|
|
76
|
-
annotated_frames = [annotation.
|
|
77
|
-
frames_without_annotation = [
|
|
103
|
+
annotated_frames = [annotation.frame for annotation in annotations]
|
|
104
|
+
frames_without_annotation = [
|
|
105
|
+
frame for frame in frames if frame not in annotated_frames
|
|
106
|
+
]
|
|
78
107
|
|
|
79
108
|
# draw n random frames
|
|
80
109
|
if len(frames_without_annotation) > n_frames:
|
|
81
|
-
frames_without_annotation = np.random.choice(
|
|
110
|
+
frames_without_annotation = np.random.choice(
|
|
111
|
+
frames_without_annotation, n_frames, replace=False
|
|
112
|
+
)
|
|
82
113
|
|
|
83
114
|
return frames_without_annotation
|
|
84
|
-
|
|
115
|
+
|
|
116
|
+
|
|
85
117
|
class LabelVideoSegment(AbstractLabelVideoSegment):
|
|
86
|
-
video = models.ForeignKey(
|
|
87
|
-
|
|
118
|
+
video = models.ForeignKey(
|
|
119
|
+
"Video", on_delete=models.CASCADE, related_name="label_video_segments"
|
|
120
|
+
)
|
|
121
|
+
prediction_meta = models.ForeignKey(
|
|
122
|
+
"VideoPredictionMeta",
|
|
123
|
+
on_delete=models.SET_NULL,
|
|
124
|
+
null=True,
|
|
125
|
+
blank=True,
|
|
126
|
+
related_name="label_video_segments",
|
|
127
|
+
)
|
|
128
|
+
|
|
129
|
+
if TYPE_CHECKING:
|
|
130
|
+
video: "Video"
|
|
131
|
+
label: "Label"
|
|
132
|
+
source: "InformationSource"
|
|
133
|
+
prediction_meta: "VideoPredictionMeta"
|
|
134
|
+
|
|
135
|
+
@classmethod
|
|
136
|
+
def from_raw(cls, video: "Video", raw_label_video_segment: "LabelRawVideoSegment"):
|
|
137
|
+
from endoreg_db.models import VideoPredictionMeta
|
|
138
|
+
|
|
139
|
+
raw_video_prediction_meta = raw_label_video_segment.prediction_meta
|
|
140
|
+
|
|
141
|
+
prediction_meta = VideoPredictionMeta.from_raw(
|
|
142
|
+
video=video, raw_video_prediction_meta=raw_video_prediction_meta
|
|
143
|
+
)
|
|
144
|
+
existing_segment = cls.objects.filter(
|
|
145
|
+
video=video,
|
|
146
|
+
start_frame_number=raw_label_video_segment.start_frame_number,
|
|
147
|
+
end_frame_number=raw_label_video_segment.end_frame_number,
|
|
148
|
+
source=raw_label_video_segment.source,
|
|
149
|
+
label=raw_label_video_segment.label,
|
|
150
|
+
prediction_meta=prediction_meta,
|
|
151
|
+
).first()
|
|
152
|
+
if existing_segment:
|
|
153
|
+
return existing_segment
|
|
154
|
+
segment = cls(
|
|
155
|
+
start_frame_number=raw_label_video_segment.start_frame_number,
|
|
156
|
+
end_frame_number=raw_label_video_segment.end_frame_number,
|
|
157
|
+
source=raw_label_video_segment.source,
|
|
158
|
+
label=raw_label_video_segment.label,
|
|
159
|
+
video=video,
|
|
160
|
+
prediction_meta=prediction_meta,
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
segment.save()
|
|
164
|
+
return segment
|
|
88
165
|
|
|
89
166
|
def get_video_model(self):
|
|
90
|
-
from endoreg_db.models
|
|
167
|
+
from endoreg_db.models import Video
|
|
168
|
+
|
|
91
169
|
return Video
|
|
92
170
|
|
|
93
|
-
def
|
|
171
|
+
def generate_annotations(self):
|
|
94
172
|
"""
|
|
95
|
-
|
|
173
|
+
Generate annotations for the segment.
|
|
96
174
|
"""
|
|
97
|
-
|
|
98
|
-
annotations = ImageClassificationAnnotation.objects.filter(frame__in=frames, label=self.label)
|
|
175
|
+
from endoreg_db.models import InformationSource
|
|
99
176
|
|
|
100
|
-
|
|
101
|
-
|
|
177
|
+
frames = self.get_frames()
|
|
178
|
+
model_meta = self.get_model_meta()
|
|
179
|
+
information_source, _created = InformationSource.objects.get_or_create(
|
|
180
|
+
name="prediction"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
for frame in tqdm(frames):
|
|
184
|
+
ImageClassificationAnnotation.objects.get_or_create(
|
|
185
|
+
frame=frame,
|
|
186
|
+
label=self.label,
|
|
187
|
+
model_meta=model_meta,
|
|
188
|
+
value=1,
|
|
189
|
+
information_source=information_source,
|
|
190
|
+
)
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
class LabelRawVideoSegment(AbstractLabelVideoSegment):
|
|
194
|
+
video = models.ForeignKey(
|
|
195
|
+
"RawVideoFile", on_delete=models.CASCADE, related_name="label_video_segments"
|
|
196
|
+
)
|
|
197
|
+
prediction_meta = models.ForeignKey(
|
|
198
|
+
"RawVideoPredictionMeta",
|
|
199
|
+
on_delete=models.SET_NULL,
|
|
200
|
+
null=True,
|
|
201
|
+
blank=True,
|
|
202
|
+
related_name="label_video_segments",
|
|
203
|
+
)
|
|
204
|
+
|
|
205
|
+
if TYPE_CHECKING:
|
|
206
|
+
video: "RawVideoFile"
|
|
207
|
+
label: "Label"
|
|
208
|
+
source: "InformationSource"
|
|
209
|
+
prediction_meta: "RawVideoPredictionMeta"
|
|
102
210
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
frames_without_annotation = np.random.choice(frames_without_annotation, n_frames, replace=False)
|
|
211
|
+
def get_video_model(self):
|
|
212
|
+
from endoreg_db.models import RawVideoFile
|
|
106
213
|
|
|
107
|
-
return
|
|
214
|
+
return RawVideoFile
|
endoreg_db/models/disease.py
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
+
from typing import List
|
|
2
3
|
|
|
3
4
|
class DiseaseManager(models.Manager):
|
|
4
5
|
def get_by_natural_key(self, name):
|
|
@@ -8,6 +9,8 @@ class Disease(models.Model):
|
|
|
8
9
|
name = models.CharField(max_length=255, unique=True)
|
|
9
10
|
name_de = models.CharField(max_length=255, blank=True, null=True)
|
|
10
11
|
name_en = models.CharField(max_length=255, blank=True, null=True)
|
|
12
|
+
subcategories = models.JSONField(default=dict)
|
|
13
|
+
numerical_descriptors = models.JSONField(default=dict)
|
|
11
14
|
|
|
12
15
|
objects = DiseaseManager()
|
|
13
16
|
|
|
@@ -17,6 +20,16 @@ class Disease(models.Model):
|
|
|
17
20
|
def __str__(self):
|
|
18
21
|
return self.name
|
|
19
22
|
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def get_classifications(self)->List['DiseaseClassification']:
|
|
30
|
+
classifications: List[DiseaseClassification] = [_ for _ in self.disease_classifications.all()]
|
|
31
|
+
return classifications
|
|
32
|
+
|
|
20
33
|
class DiseaseClassificationManager(models.Manager):
|
|
21
34
|
def get_by_natural_key(self, name):
|
|
22
35
|
return self.get(name=name)
|
|
@@ -26,7 +39,10 @@ class DiseaseClassification(models.Model):
|
|
|
26
39
|
name_de = models.CharField(max_length=255, blank=True, null=True)
|
|
27
40
|
name_en = models.CharField(max_length=255, blank=True, null=True)
|
|
28
41
|
|
|
29
|
-
disease = models.ForeignKey(
|
|
42
|
+
disease = models.ForeignKey(
|
|
43
|
+
Disease, on_delete=models.CASCADE,
|
|
44
|
+
related_name='disease_classifications'
|
|
45
|
+
)
|
|
30
46
|
|
|
31
47
|
objects = DiseaseClassificationManager()
|
|
32
48
|
|
|
@@ -36,6 +52,10 @@ class DiseaseClassification(models.Model):
|
|
|
36
52
|
def __str__(self):
|
|
37
53
|
return self.name
|
|
38
54
|
|
|
55
|
+
def get_choices(self)->List['DiseaseClassificationChoice']:
|
|
56
|
+
choices:List[DiseaseClassificationChoice] = [_ for _ in self.disease_classification_choices.all()]
|
|
57
|
+
return choices
|
|
58
|
+
|
|
39
59
|
class DiseaseClassificationChoiceManager(models.Manager):
|
|
40
60
|
def get_by_natural_key(self, name):
|
|
41
61
|
return self.get(name=name)
|
|
@@ -45,7 +65,10 @@ class DiseaseClassificationChoice(models.Model):
|
|
|
45
65
|
name_de = models.CharField(max_length=255, blank=True, null=True)
|
|
46
66
|
name_en = models.CharField(max_length=255, blank=True, null=True)
|
|
47
67
|
|
|
48
|
-
disease_classification = models.ForeignKey(
|
|
68
|
+
disease_classification = models.ForeignKey(
|
|
69
|
+
DiseaseClassification, on_delete=models.CASCADE,
|
|
70
|
+
related_name='disease_classification_choices'
|
|
71
|
+
)
|
|
49
72
|
|
|
50
73
|
objects = DiseaseClassificationChoiceManager()
|
|
51
74
|
|
|
@@ -1,10 +1,27 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
-
|
|
2
|
+
from typing import List
|
|
3
3
|
class EmissionFactorManager(models.Manager):
|
|
4
|
-
|
|
4
|
+
"""
|
|
5
|
+
Manager for EmissionFactor with custom query methods.
|
|
6
|
+
"""
|
|
7
|
+
def get_by_natural_key(self, name: str) -> "EmissionFactor":
|
|
5
8
|
return self.get(name=name)
|
|
6
|
-
|
|
9
|
+
|
|
10
|
+
# get debug from settings
|
|
11
|
+
# from django.conf import settings
|
|
12
|
+
# DEBUG = settings.DEBUG
|
|
13
|
+
|
|
7
14
|
class EmissionFactor(models.Model):
|
|
15
|
+
"""
|
|
16
|
+
Represents an emission factor with associated unit and value.
|
|
17
|
+
|
|
18
|
+
Attributes:
|
|
19
|
+
name (str): The name of the emission factor.
|
|
20
|
+
name_de (str): The German name of the emission factor.
|
|
21
|
+
name_en (str): The English name of the emission factor.
|
|
22
|
+
unit (ForeignKey): The unit associated with the emission factor.
|
|
23
|
+
value (float): The value of the emission factor.
|
|
24
|
+
"""
|
|
8
25
|
objects = EmissionFactorManager()
|
|
9
26
|
|
|
10
27
|
name = models.CharField(max_length=255)
|
|
@@ -13,8 +30,56 @@ class EmissionFactor(models.Model):
|
|
|
13
30
|
unit = models.ForeignKey("Unit", on_delete=models.SET_NULL, null=True)
|
|
14
31
|
value = models.FloatField()
|
|
15
32
|
|
|
16
|
-
def natural_key(self):
|
|
33
|
+
def natural_key(self) -> tuple:
|
|
34
|
+
"""
|
|
35
|
+
Returns the natural key for the emission factor.
|
|
36
|
+
|
|
37
|
+
Returns:
|
|
38
|
+
tuple: The natural key consisting of the name.
|
|
39
|
+
"""
|
|
17
40
|
return (self.name,)
|
|
41
|
+
|
|
42
|
+
def __str__(self, verbose: bool = False) -> str:
|
|
43
|
+
"""
|
|
44
|
+
String representation of the emission factor.
|
|
45
|
+
|
|
46
|
+
Args:
|
|
47
|
+
verbose (bool): If True, includes additional details.
|
|
48
|
+
|
|
49
|
+
Returns:
|
|
50
|
+
str: The string representation of the emission factor.
|
|
51
|
+
"""
|
|
52
|
+
result = f"{self.name}: {self.value} per {self.unit}"
|
|
53
|
+
if verbose:
|
|
54
|
+
result += "\n\tSources:"
|
|
55
|
+
for source in self.sources():
|
|
56
|
+
result += f"\n\t\t{source}"
|
|
57
|
+
|
|
58
|
+
return result
|
|
18
59
|
|
|
19
|
-
def
|
|
20
|
-
|
|
60
|
+
def get_reference_products(self) -> List["ReferenceProduct"]:
|
|
61
|
+
"""
|
|
62
|
+
Retrieves all reference products associated with the emission factor.
|
|
63
|
+
|
|
64
|
+
Returns:
|
|
65
|
+
list: A list of ReferenceProduct instances associated with this emission factor.
|
|
66
|
+
"""
|
|
67
|
+
from endoreg_db.models import ReferenceProduct
|
|
68
|
+
|
|
69
|
+
reference_products = []
|
|
70
|
+
reference_products += ReferenceProduct.objects.filter(emission_factor_total=self)
|
|
71
|
+
reference_products += ReferenceProduct.objects.filter(emission_factor_package=self)
|
|
72
|
+
reference_products += ReferenceProduct.objects.filter(emission_factor_product=self)
|
|
73
|
+
|
|
74
|
+
return reference_products
|
|
75
|
+
|
|
76
|
+
def sources(self) -> List:
|
|
77
|
+
"""
|
|
78
|
+
Retrieves all sources related to the emission factor.
|
|
79
|
+
|
|
80
|
+
Returns:
|
|
81
|
+
list: A list of sources related to the emission factor.
|
|
82
|
+
"""
|
|
83
|
+
sources = []
|
|
84
|
+
sources.extend(self.get_reference_products())
|
|
85
|
+
return sources
|