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,21 +1,190 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
+
from endoreg_db.utils.hashs import (
|
|
3
|
+
get_patient_hash,
|
|
4
|
+
get_patient_examination_hash,
|
|
5
|
+
# get_hash_string,
|
|
6
|
+
)
|
|
7
|
+
from hashlib import sha256
|
|
8
|
+
|
|
9
|
+
# from datetime import date
|
|
10
|
+
from icecream import ic
|
|
11
|
+
import os
|
|
12
|
+
|
|
13
|
+
# get DJANGO_SALT from settings
|
|
14
|
+
SECRET_SALT = os.getenv("DJANGO_SALT", "default_salt")
|
|
15
|
+
|
|
2
16
|
|
|
3
17
|
class SensitiveMeta(models.Model):
|
|
4
18
|
examination_date = models.DateField(blank=True, null=True)
|
|
19
|
+
pseudo_patient = models.ForeignKey(
|
|
20
|
+
"Patient",
|
|
21
|
+
on_delete=models.CASCADE,
|
|
22
|
+
blank=True,
|
|
23
|
+
null=True,
|
|
24
|
+
)
|
|
5
25
|
patient_first_name = models.CharField(max_length=255, blank=True, null=True)
|
|
6
26
|
patient_last_name = models.CharField(max_length=255, blank=True, null=True)
|
|
7
27
|
patient_dob = models.DateField(blank=True, null=True)
|
|
28
|
+
pseudo_examination = models.ForeignKey(
|
|
29
|
+
"PatientExamination",
|
|
30
|
+
on_delete=models.CASCADE,
|
|
31
|
+
blank=True,
|
|
32
|
+
null=True,
|
|
33
|
+
)
|
|
34
|
+
patient_gender = models.ForeignKey(
|
|
35
|
+
"Gender",
|
|
36
|
+
on_delete=models.CASCADE,
|
|
37
|
+
blank=True,
|
|
38
|
+
null=True,
|
|
39
|
+
)
|
|
40
|
+
examiners = models.ManyToManyField(
|
|
41
|
+
"Examiner",
|
|
42
|
+
blank=True,
|
|
43
|
+
)
|
|
44
|
+
center = models.ForeignKey(
|
|
45
|
+
"Center",
|
|
46
|
+
on_delete=models.CASCADE,
|
|
47
|
+
blank=True,
|
|
48
|
+
null=True,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
examiner_first_name = models.CharField(max_length=255, blank=True, null=True)
|
|
52
|
+
examiner_last_name = models.CharField(max_length=255, blank=True, null=True)
|
|
53
|
+
|
|
54
|
+
examination_hash = models.CharField(max_length=255, blank=True, null=True)
|
|
55
|
+
patient_hash = models.CharField(max_length=255, blank=True, null=True)
|
|
56
|
+
|
|
8
57
|
endoscope_type = models.CharField(max_length=255, blank=True, null=True)
|
|
9
58
|
endoscope_sn = models.CharField(max_length=255, blank=True, null=True)
|
|
59
|
+
state_verified = models.BooleanField(default=False)
|
|
60
|
+
state_hash_generated = models.BooleanField(default=False)
|
|
61
|
+
state_names_substituted = models.BooleanField(default=False)
|
|
62
|
+
state_dob_substituted = models.BooleanField(default=False)
|
|
63
|
+
state_examination_date_substituted = models.BooleanField(default=False)
|
|
64
|
+
state_endoscope_sn_substituted = models.BooleanField(default=False)
|
|
65
|
+
state_examiners_substituted = models.BooleanField(default=False)
|
|
10
66
|
|
|
11
67
|
@classmethod
|
|
12
68
|
def create_from_dict(cls, data: dict):
|
|
69
|
+
from endoreg_db.models import Center, Examiner
|
|
70
|
+
from endoreg_db.utils import guess_name_gender
|
|
71
|
+
|
|
13
72
|
# data can contain more fields than the model has
|
|
14
73
|
field_names = [_.name for _ in cls._meta.fields]
|
|
15
74
|
selected_data = {k: v for k, v in data.items() if k in field_names}
|
|
16
75
|
|
|
17
|
-
|
|
18
|
-
|
|
76
|
+
first_name = selected_data.get("patient_first_name")
|
|
77
|
+
last_name = selected_data.get("patient_last_name")
|
|
78
|
+
center_name = data.get("center_name")
|
|
79
|
+
|
|
80
|
+
try:
|
|
81
|
+
center = Center.objects.get_by_natural_key(center_name)
|
|
82
|
+
except Exception as exc:
|
|
83
|
+
raise ValueError(f"Center with name {center_name} does not exist") from exc
|
|
84
|
+
|
|
85
|
+
selected_data["center"] = center
|
|
86
|
+
|
|
87
|
+
try:
|
|
88
|
+
# TODO Add to documentation and replace with better method
|
|
89
|
+
gender = guess_name_gender(first_name)
|
|
90
|
+
except:
|
|
91
|
+
raise ValueError(f"Gender for name {first_name} could not be guessed")
|
|
92
|
+
|
|
93
|
+
selected_data["patient_gender"] = gender
|
|
94
|
+
|
|
95
|
+
if first_name and last_name:
|
|
96
|
+
# TODO Add to documentation
|
|
97
|
+
cls._update_name_db(first_name, last_name)
|
|
98
|
+
|
|
99
|
+
sensitive_meta = cls.objects.create(**selected_data)
|
|
100
|
+
sensitive_meta.get_or_create_pseudo_examiner()
|
|
101
|
+
sensitive_meta.get_or_create_pseudo_patient()
|
|
102
|
+
sensitive_meta.get_or_create_pseudo_patient_examination()
|
|
103
|
+
|
|
104
|
+
ic("EXAMINER_FIRST_NAME", sensitive_meta.examiner_first_name)
|
|
105
|
+
ic("EXAMINER_LAST_NAME", sensitive_meta.examiner_last_name)
|
|
106
|
+
|
|
107
|
+
return sensitive_meta
|
|
108
|
+
|
|
109
|
+
def get_or_create_pseudo_examiner(self):
|
|
110
|
+
ic("GETTING OR CREATING EXAMINER")
|
|
111
|
+
|
|
112
|
+
if self.examiners.exists():
|
|
113
|
+
examiner = self.examiners.first()
|
|
114
|
+
ic(f"Exisiting examiner: {examiner}")
|
|
115
|
+
|
|
116
|
+
else:
|
|
117
|
+
examiner = self.create_pseudo_examiner()
|
|
118
|
+
ic(f"Created examiner: {examiner}")
|
|
119
|
+
|
|
120
|
+
return examiner
|
|
121
|
+
|
|
122
|
+
def create_pseudo_examiner(self):
|
|
123
|
+
from endoreg_db.models import Examiner, Center
|
|
124
|
+
|
|
125
|
+
first_name = self.examiner_first_name
|
|
126
|
+
last_name = self.examiner_last_name
|
|
127
|
+
center = self.center
|
|
128
|
+
ic("CREATING EXAMINER", first_name, last_name, center)
|
|
129
|
+
if not first_name or not last_name or not center:
|
|
130
|
+
default_center = Center.objects.get_by_natural_key("endoreg_db_demo")
|
|
131
|
+
examiner, _created = Examiner.custom_get_or_create(
|
|
132
|
+
first_name="Unknown", last_name="Unknown", center=default_center
|
|
133
|
+
)
|
|
134
|
+
else:
|
|
135
|
+
examiner, _created = Examiner.custom_get_or_create(
|
|
136
|
+
first_name=first_name, last_name=last_name, center=center
|
|
137
|
+
)
|
|
138
|
+
self.examiners.add(examiner)
|
|
139
|
+
self.save()
|
|
140
|
+
|
|
141
|
+
return examiner
|
|
142
|
+
|
|
143
|
+
def get_or_create_pseudo_patient(self):
|
|
144
|
+
if not self.pseudo_patient:
|
|
145
|
+
self.pseudo_patient = self.create_pseudo_patient()
|
|
146
|
+
self.save()
|
|
147
|
+
return self.pseudo_patient
|
|
148
|
+
|
|
149
|
+
def create_pseudo_patient(self):
|
|
150
|
+
from endoreg_db.models import Patient
|
|
151
|
+
from datetime import date
|
|
152
|
+
|
|
153
|
+
dob = self.patient_dob
|
|
154
|
+
|
|
155
|
+
if isinstance(dob, str):
|
|
156
|
+
dob = date.fromisoformat(dob)
|
|
157
|
+
|
|
158
|
+
month = dob.month
|
|
159
|
+
year = dob.year
|
|
160
|
+
|
|
161
|
+
patient_hash = self.get_patient_hash()
|
|
162
|
+
patient, _created = Patient.get_or_create_pseudo_patient_by_hash(
|
|
163
|
+
patient_hash=patient_hash,
|
|
164
|
+
center=self.center,
|
|
165
|
+
gender=self.patient_gender,
|
|
166
|
+
birth_year=year,
|
|
167
|
+
birth_month=month,
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return patient
|
|
171
|
+
|
|
172
|
+
def get_or_create_pseudo_patient_examination(self):
|
|
173
|
+
from endoreg_db.models import PatientExamination
|
|
174
|
+
|
|
175
|
+
patient_hash = self.get_patient_hash()
|
|
176
|
+
examination_hash = self.get_patient_examination_hash()
|
|
177
|
+
|
|
178
|
+
patient_examination, _created = (
|
|
179
|
+
PatientExamination.get_or_create_pseudo_patient_examination_by_hash(
|
|
180
|
+
patient_hash, examination_hash
|
|
181
|
+
)
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
self.pseudo_examination = patient_examination
|
|
185
|
+
|
|
186
|
+
return patient_examination
|
|
187
|
+
|
|
19
188
|
def update_from_dict(self, data: dict):
|
|
20
189
|
# data can contain more fields than the model has
|
|
21
190
|
field_names = [_.name for _ in self._meta.fields]
|
|
@@ -23,9 +192,99 @@ class SensitiveMeta(models.Model):
|
|
|
23
192
|
|
|
24
193
|
for k, v in selected_data.items():
|
|
25
194
|
setattr(self, k, v)
|
|
26
|
-
|
|
195
|
+
|
|
27
196
|
self.save()
|
|
28
|
-
|
|
197
|
+
first_name = self.patient_first_name
|
|
198
|
+
last_name = self.patient_last_name
|
|
199
|
+
|
|
200
|
+
if first_name and last_name:
|
|
201
|
+
SensitiveMeta._update_name_db(first_name=first_name, last_name=last_name)
|
|
202
|
+
|
|
203
|
+
if not self.examination_hash:
|
|
204
|
+
self.examination_hash = self.get_patient_examination_hash()
|
|
205
|
+
if not self.patient_hash:
|
|
206
|
+
self.patient_hash = self.get_patient_hash()
|
|
207
|
+
|
|
208
|
+
examiner_first_name = data.get("examiner_first_name", "")
|
|
209
|
+
examiner_last_name = data.get("examiner_last_name", "")
|
|
210
|
+
|
|
211
|
+
if examiner_first_name and examiner_last_name:
|
|
212
|
+
self.examiner_first_name = examiner_first_name
|
|
213
|
+
self.examiner_last_name = examiner_last_name
|
|
214
|
+
_examiner = self.get_or_create_pseudo_examiner()
|
|
215
|
+
|
|
216
|
+
return self
|
|
217
|
+
|
|
29
218
|
def __str__(self):
|
|
30
|
-
|
|
31
|
-
|
|
219
|
+
result_str = "SensitiveMeta:"
|
|
220
|
+
result_str += f"\tExamination Date: {self.examination_date}"
|
|
221
|
+
result_str += f"\tFirst Name: {self.patient_first_name}"
|
|
222
|
+
result_str += f"\tLast Name: {self.patient_last_name}"
|
|
223
|
+
result_str += f"\tDate of Birth: (*{self.patient_dob})"
|
|
224
|
+
result_str += f"\tGender: {self.patient_gender}"
|
|
225
|
+
result_str += f"\tCenter: {self.center}"
|
|
226
|
+
result_str += f"\tExaminers: {self.examiners.all()}"
|
|
227
|
+
result_str += f"\tEndoscope Type: {self.endoscope_type}"
|
|
228
|
+
result_str += f"\tEndoscope SN: {self.endoscope_sn}"
|
|
229
|
+
result_str += f"\tState Verified: {self.state_verified}"
|
|
230
|
+
result_str += f"\tPatient Hash: {self.patient_hash}"
|
|
231
|
+
result_str += f"\tExamination Hash: {self.examination_hash}"
|
|
232
|
+
|
|
233
|
+
return result_str
|
|
234
|
+
|
|
235
|
+
def __repr__(self):
|
|
236
|
+
return self.__str__()
|
|
237
|
+
|
|
238
|
+
def get_patient_hash(self, salt=SECRET_SALT):
|
|
239
|
+
dob = self.patient_dob
|
|
240
|
+
first_name = self.patient_first_name
|
|
241
|
+
last_name = self.patient_last_name
|
|
242
|
+
center = self.center
|
|
243
|
+
|
|
244
|
+
assert dob, "Patient DOB is required"
|
|
245
|
+
assert center, "Center is required"
|
|
246
|
+
|
|
247
|
+
hash_str = get_patient_hash(
|
|
248
|
+
first_name=first_name,
|
|
249
|
+
last_name=last_name,
|
|
250
|
+
dob=dob,
|
|
251
|
+
center=self.center.name,
|
|
252
|
+
salt=salt,
|
|
253
|
+
)
|
|
254
|
+
return sha256(hash_str.encode()).hexdigest()
|
|
255
|
+
|
|
256
|
+
def get_patient_examination_hash(self, salt=SECRET_SALT):
|
|
257
|
+
dob = self.patient_dob
|
|
258
|
+
first_name = self.patient_first_name
|
|
259
|
+
last_name = self.patient_last_name
|
|
260
|
+
examination_date = self.examination_date
|
|
261
|
+
center = self.center
|
|
262
|
+
|
|
263
|
+
assert dob, "Patient DOB is required"
|
|
264
|
+
assert examination_date, "Examination date is required"
|
|
265
|
+
|
|
266
|
+
hash_str = get_patient_examination_hash(
|
|
267
|
+
first_name=first_name,
|
|
268
|
+
last_name=last_name,
|
|
269
|
+
dob=dob,
|
|
270
|
+
examination_date=examination_date,
|
|
271
|
+
center=center.name,
|
|
272
|
+
salt=salt,
|
|
273
|
+
)
|
|
274
|
+
|
|
275
|
+
return sha256(hash_str.encode()).hexdigest()
|
|
276
|
+
|
|
277
|
+
# override save method to update hashes
|
|
278
|
+
def save(self, *args, **kwargs):
|
|
279
|
+
self.examination_hash = self.get_patient_examination_hash()
|
|
280
|
+
self.patient_hash = self.get_patient_hash()
|
|
281
|
+
self.pseudo_patient = self.create_pseudo_patient()
|
|
282
|
+
self.pseudo_examination = self.get_or_create_pseudo_patient_examination()
|
|
283
|
+
super().save(*args, **kwargs)
|
|
284
|
+
|
|
285
|
+
@classmethod
|
|
286
|
+
def _update_name_db(cls, first_name, last_name):
|
|
287
|
+
from endoreg_db.models import FirstName, LastName
|
|
288
|
+
|
|
289
|
+
FirstName.objects.get_or_create(name=first_name)
|
|
290
|
+
LastName.objects.get_or_create(name=last_name)
|
|
@@ -1,38 +1,77 @@
|
|
|
1
1
|
from django.db import models
|
|
2
|
-
import
|
|
2
|
+
import subprocess
|
|
3
|
+
import json
|
|
3
4
|
from pathlib import Path
|
|
5
|
+
from typing import Optional, TYPE_CHECKING
|
|
4
6
|
|
|
5
7
|
# import endoreg_center_id from django settings
|
|
6
8
|
from django.conf import settings
|
|
7
9
|
|
|
8
10
|
# check if endoreg_center_id is set
|
|
9
|
-
if not hasattr(settings,
|
|
11
|
+
if not hasattr(settings, "ENDOREG_CENTER_ID"):
|
|
10
12
|
ENDOREG_CENTER_ID = 9999
|
|
11
13
|
else:
|
|
12
14
|
ENDOREG_CENTER_ID = settings.ENDOREG_CENTER_ID
|
|
13
15
|
|
|
16
|
+
if TYPE_CHECKING:
|
|
17
|
+
from endoreg_db.models import EndoscopyProcessor, Endoscope, Center
|
|
18
|
+
|
|
19
|
+
|
|
14
20
|
# VideoMeta
|
|
15
21
|
class VideoMeta(models.Model):
|
|
16
|
-
processor = models.ForeignKey(
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
22
|
+
processor = models.ForeignKey(
|
|
23
|
+
"EndoscopyProcessor", on_delete=models.CASCADE, blank=True, null=True
|
|
24
|
+
)
|
|
25
|
+
endoscope = models.ForeignKey(
|
|
26
|
+
"Endoscope", on_delete=models.CASCADE, blank=True, null=True
|
|
27
|
+
)
|
|
28
|
+
center = models.ForeignKey("Center", on_delete=models.CASCADE)
|
|
29
|
+
import_meta = models.OneToOneField(
|
|
30
|
+
"VideoImportMeta", on_delete=models.CASCADE, blank=True, null=True
|
|
31
|
+
)
|
|
32
|
+
ffmpeg_meta = models.OneToOneField(
|
|
33
|
+
"FFMpegMeta", on_delete=models.CASCADE, blank=True, null=True
|
|
34
|
+
) ##
|
|
21
35
|
|
|
22
|
-
|
|
36
|
+
@classmethod
|
|
37
|
+
def create_from_file(
|
|
38
|
+
cls,
|
|
39
|
+
file_path: Path,
|
|
40
|
+
center: Optional["Center"],
|
|
41
|
+
processor: Optional["EndoscopyProcessor"] = None,
|
|
42
|
+
endoscope: Optional["Endoscope"] = None,
|
|
43
|
+
):
|
|
44
|
+
"""Create a new VideoMeta from a file."""
|
|
45
|
+
meta = cls.objects.create(center=center)
|
|
46
|
+
meta.update_meta(file_path)
|
|
47
|
+
|
|
48
|
+
if processor:
|
|
49
|
+
meta.processor = processor
|
|
50
|
+
meta.get_endo_roi()
|
|
51
|
+
if endoscope:
|
|
52
|
+
meta.endoscope = endoscope
|
|
53
|
+
|
|
54
|
+
meta.save()
|
|
55
|
+
|
|
56
|
+
return meta
|
|
23
57
|
|
|
58
|
+
def __str__(self):
|
|
24
59
|
processor_name = self.processor.name if self.processor is not None else "None"
|
|
25
60
|
endoscope_name = self.endoscope.name if self.endoscope is not None else "None"
|
|
26
61
|
center_name = self.center.name if self.center is not None else "None"
|
|
62
|
+
ffmpeg_meta_str = self.ffmpeg_meta.__str__()
|
|
63
|
+
import_meta_str = self.import_meta.__str__()
|
|
27
64
|
|
|
28
65
|
result_html = ""
|
|
29
66
|
|
|
30
|
-
result_html += f"Processor: {processor_name}
|
|
31
|
-
result_html += f"Endoscope: {endoscope_name}
|
|
32
|
-
result_html += f"Center: {center_name}
|
|
67
|
+
result_html += f"Processor: {processor_name}\n"
|
|
68
|
+
result_html += f"Endoscope: {endoscope_name}\n"
|
|
69
|
+
result_html += f"Center: {center_name}\n"
|
|
70
|
+
result_html += f"FFMpeg Meta: {ffmpeg_meta_str}\n"
|
|
71
|
+
result_html += f"Import Meta: {import_meta_str}\n"
|
|
33
72
|
|
|
34
73
|
return result_html
|
|
35
|
-
|
|
74
|
+
|
|
36
75
|
# import meta should be created when video meta is created
|
|
37
76
|
def save(self, *args, **kwargs):
|
|
38
77
|
if self.import_meta is None:
|
|
@@ -50,15 +89,18 @@ class VideoMeta(models.Model):
|
|
|
50
89
|
self.save()
|
|
51
90
|
|
|
52
91
|
def get_endo_roi(self):
|
|
53
|
-
|
|
92
|
+
from endoreg_db.models import EndoscopyProcessor
|
|
93
|
+
|
|
94
|
+
processor: EndoscopyProcessor = self.processor
|
|
95
|
+
endo_roi = processor.get_roi_endoscope_image()
|
|
54
96
|
return endo_roi
|
|
55
97
|
|
|
56
98
|
def get_fps(self):
|
|
57
99
|
if not self.ffmpeg_meta:
|
|
58
100
|
return None
|
|
59
|
-
|
|
101
|
+
|
|
60
102
|
return self.ffmpeg_meta.frame_rate
|
|
61
|
-
|
|
103
|
+
|
|
62
104
|
|
|
63
105
|
class FFMpegMeta(models.Model):
|
|
64
106
|
# Existing fields
|
|
@@ -66,7 +108,7 @@ class FFMpegMeta(models.Model):
|
|
|
66
108
|
width = models.IntegerField(blank=True, null=True)
|
|
67
109
|
height = models.IntegerField(blank=True, null=True)
|
|
68
110
|
frame_rate = models.FloatField(blank=True, null=True)
|
|
69
|
-
|
|
111
|
+
|
|
70
112
|
# New fields for comprehensive information
|
|
71
113
|
video_codec = models.CharField(max_length=50, blank=True, null=True)
|
|
72
114
|
audio_codec = models.CharField(max_length=50, blank=True, null=True)
|
|
@@ -77,45 +119,67 @@ class FFMpegMeta(models.Model):
|
|
|
77
119
|
|
|
78
120
|
@classmethod
|
|
79
121
|
def create_from_file(cls, file_path: Path):
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
122
|
+
cmd = [
|
|
123
|
+
"ffprobe",
|
|
124
|
+
"-v",
|
|
125
|
+
"quiet",
|
|
126
|
+
"-print_format",
|
|
127
|
+
"json",
|
|
128
|
+
"-show_streams",
|
|
129
|
+
str(file_path),
|
|
130
|
+
]
|
|
131
|
+
proc = subprocess.run(cmd, capture_output=True, text=True, check=True)
|
|
132
|
+
probe = json.loads(proc.stdout)
|
|
133
|
+
|
|
134
|
+
video_stream = next(
|
|
135
|
+
(stream for stream in probe["streams"] if stream["codec_type"] == "video"),
|
|
136
|
+
None,
|
|
137
|
+
)
|
|
138
|
+
audio_streams = [
|
|
139
|
+
stream for stream in probe["streams"] if stream["codec_type"] == "audio"
|
|
140
|
+
]
|
|
141
|
+
|
|
142
|
+
if not video_stream:
|
|
94
143
|
return None
|
|
95
144
|
|
|
96
|
-
# Extract and store video metadata
|
|
97
145
|
metadata = {
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
146
|
+
"duration": float(video_stream.get("duration", 0)),
|
|
147
|
+
"width": int(video_stream.get("width", 0)),
|
|
148
|
+
"height": int(video_stream.get("height", 0)),
|
|
149
|
+
"frame_rate": float(
|
|
150
|
+
next(iter(video_stream.get("avg_frame_rate", "").split("/")), 0)
|
|
151
|
+
),
|
|
152
|
+
"video_codec": video_stream.get("codec_name", ""),
|
|
103
153
|
}
|
|
104
154
|
|
|
105
|
-
# If there are audio streams, extract and store audio metadata from the first stream
|
|
106
155
|
if audio_streams:
|
|
107
156
|
first_audio_stream = audio_streams[0]
|
|
108
|
-
metadata.update(
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
157
|
+
metadata.update(
|
|
158
|
+
{
|
|
159
|
+
"audio_codec": first_audio_stream.get("codec_name", ""),
|
|
160
|
+
"audio_channels": int(first_audio_stream.get("channels", 0)),
|
|
161
|
+
"audio_sample_rate": int(first_audio_stream.get("sample_rate", 0)),
|
|
162
|
+
}
|
|
163
|
+
)
|
|
113
164
|
|
|
114
|
-
# Create and return the FFMpegMeta instance
|
|
115
165
|
return cls.objects.create(**metadata)
|
|
116
166
|
|
|
117
|
-
|
|
167
|
+
def __str__(self):
|
|
168
|
+
result_html = ""
|
|
169
|
+
|
|
170
|
+
result_html += f"Duration: {self.duration}\n"
|
|
171
|
+
result_html += f"Width: {self.width}\n"
|
|
172
|
+
result_html += f"Height: {self.height}\n"
|
|
173
|
+
result_html += f"Frame Rate: {self.frame_rate}\n"
|
|
174
|
+
result_html += f"Video Codec: {self.video_codec}\n"
|
|
175
|
+
result_html += f"Audio Codec: {self.audio_codec}\n"
|
|
176
|
+
result_html += f"Audio Channels: {self.audio_channels}\n"
|
|
177
|
+
result_html += f"Audio Sample Rate: {self.audio_sample_rate}\n"
|
|
118
178
|
|
|
179
|
+
return result_html
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class VideoImportMeta(models.Model):
|
|
119
183
|
video_anonymized = models.BooleanField(default=False)
|
|
120
184
|
video_patient_data_detected = models.BooleanField(default=False)
|
|
121
185
|
outside_detected = models.BooleanField(default=False)
|
|
@@ -125,9 +189,11 @@ class VideoImportMeta(models.Model):
|
|
|
125
189
|
def __str__(self):
|
|
126
190
|
result_html = ""
|
|
127
191
|
|
|
128
|
-
result_html += f"Video anonymized: {self.video_anonymized}
|
|
129
|
-
result_html +=
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
result_html += f"Outside
|
|
133
|
-
|
|
192
|
+
result_html += f"Video anonymized: {self.video_anonymized}\n"
|
|
193
|
+
result_html += (
|
|
194
|
+
f"Video patient data detected: {self.video_patient_data_detected}\n"
|
|
195
|
+
)
|
|
196
|
+
result_html += f"Outside detected: {self.outside_detected}\n"
|
|
197
|
+
result_html += f"Patient data removed: {self.patient_data_removed}\n"
|
|
198
|
+
result_html += f"Outside removed: {self.outside_removed}\n"
|
|
199
|
+
return result_html
|
|
@@ -1,82 +1,52 @@
|
|
|
1
|
-
from django.db import models
|
|
2
|
-
from ..center import Center
|
|
3
|
-
import hashlib
|
|
4
1
|
from datetime import date, time
|
|
2
|
+
from django.db import models
|
|
3
|
+
from .base_classes.abstract_pdf import AbstractPdfFile
|
|
4
|
+
|
|
5
5
|
|
|
6
|
-
class ReportFile(
|
|
7
|
-
pdf = models.FileField(upload_to="raw_report_pdfs", blank=True, null=True)
|
|
8
|
-
pdf_hash = models.CharField(max_length=255, unique=True)
|
|
9
|
-
center = models.ForeignKey(Center, on_delete=models.CASCADE)
|
|
6
|
+
class ReportFile(AbstractPdfFile):
|
|
10
7
|
meta = models.JSONField(blank=True, null=True)
|
|
11
8
|
text = models.TextField(blank=True, null=True)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
# sensitive_meta = models.ForeignKey(
|
|
10
|
+
# "SensitiveMeta",
|
|
11
|
+
# on_delete=models.SET_NULL,
|
|
12
|
+
# related_name="report_files",
|
|
13
|
+
# null=True,
|
|
14
|
+
# blank=True,
|
|
15
|
+
# )
|
|
16
|
+
# examination = models.ForeignKey(
|
|
17
|
+
# "PatientExamination",
|
|
18
|
+
# on_delete=models.SET_NULL,
|
|
19
|
+
# blank=True,
|
|
20
|
+
# null=True,
|
|
21
|
+
# related_name="report_files",
|
|
22
|
+
# )
|
|
23
|
+
# patient = models.ForeignKey(
|
|
24
|
+
# "Patient", on_delete=models.DO_NOTHING, blank=True, null=True
|
|
25
|
+
# )
|
|
26
|
+
# examiner = models.ForeignKey(
|
|
27
|
+
# "Examiner", on_delete=models.DO_NOTHING, blank=True, null=True
|
|
28
|
+
# )
|
|
15
29
|
date = models.DateField(blank=True, null=True)
|
|
16
30
|
time = models.TimeField(blank=True, null=True)
|
|
17
31
|
|
|
18
|
-
def
|
|
19
|
-
#FIXME should use endoreg_db.utils.get_pdf_hash in future
|
|
20
|
-
pdf = self.pdf
|
|
21
|
-
pdf_hash = None
|
|
22
|
-
|
|
23
|
-
if pdf:
|
|
24
|
-
# Open the file in binary mode and read its contents
|
|
25
|
-
with pdf.open(mode='rb') as f:
|
|
26
|
-
pdf_contents = f.read()
|
|
27
|
-
# Create a hash object using SHA-256 algorithm
|
|
28
|
-
hash_object = hashlib.sha256(pdf_contents, usedforsecurity=False)
|
|
29
|
-
# Get the hexadecimal representation of the hash
|
|
30
|
-
pdf_hash = hash_object.hexdigest()
|
|
31
|
-
assert len(pdf_hash) <= 255, "Hash length exceeds 255 characters"
|
|
32
|
-
|
|
33
|
-
return pdf_hash
|
|
34
|
-
|
|
35
|
-
def initialize_metadata_in_db(self, report_meta=None):
|
|
36
|
-
if not report_meta:
|
|
37
|
-
report_meta = self.meta
|
|
38
|
-
self.set_examination_date_and_time(report_meta)
|
|
39
|
-
self.patient, created = self.get_or_create_patient(report_meta)
|
|
40
|
-
self.examiner, created = self.get_or_create_examiner(report_meta)
|
|
41
|
-
self.save()
|
|
42
|
-
|
|
43
|
-
def get_or_create_patient(self, report_meta=None):
|
|
44
|
-
from ..persons import Patient
|
|
45
|
-
if not report_meta:
|
|
46
|
-
report_meta = self.meta
|
|
47
|
-
patient_first_name = report_meta['patient_first_name']
|
|
48
|
-
patient_last_name = report_meta['patient_last_name']
|
|
49
|
-
patient_dob = report_meta['patient_dob']
|
|
50
|
-
|
|
51
|
-
patient, created = Patient.objects.get_or_create(
|
|
52
|
-
first_name=patient_first_name,
|
|
53
|
-
last_name=patient_last_name,
|
|
54
|
-
dob=patient_dob
|
|
55
|
-
)
|
|
56
|
-
|
|
57
|
-
return patient, created
|
|
58
|
-
|
|
59
|
-
def get_or_create_examiner(self, report_meta= None):
|
|
32
|
+
def get_or_create_examiner(self, examiner_first_name, examiner_last_name):
|
|
60
33
|
from ..persons import Examiner
|
|
61
|
-
|
|
62
|
-
report_meta = self.meta
|
|
63
|
-
examiner_first_name = report_meta['examiner_first_name']
|
|
64
|
-
examiner_last_name = report_meta['examiner_last_name']
|
|
34
|
+
|
|
65
35
|
examiner_center = self.center
|
|
66
36
|
|
|
67
37
|
examiner, created = Examiner.objects.get_or_create(
|
|
68
38
|
first_name=examiner_first_name,
|
|
69
39
|
last_name=examiner_last_name,
|
|
70
|
-
center=examiner_center
|
|
40
|
+
center=examiner_center,
|
|
71
41
|
)
|
|
72
42
|
|
|
73
43
|
return examiner, created
|
|
74
|
-
|
|
44
|
+
|
|
75
45
|
def set_examination_date_and_time(self, report_meta=None):
|
|
76
46
|
if not report_meta:
|
|
77
47
|
report_meta = self.meta
|
|
78
|
-
examination_date_str = report_meta[
|
|
79
|
-
examination_time_str = report_meta[
|
|
48
|
+
examination_date_str = report_meta["examination_date"]
|
|
49
|
+
examination_time_str = report_meta["examination_time"]
|
|
80
50
|
|
|
81
51
|
if examination_date_str:
|
|
82
52
|
# TODO: get django DateField compatible date from string (e.g. "2021-01-01")
|
|
@@ -84,6 +54,3 @@ class ReportFile(models.Model):
|
|
|
84
54
|
if examination_time_str:
|
|
85
55
|
# TODO: get django TimeField compatible time from string (e.g. "12:00")
|
|
86
56
|
self.time = time.fromisoformat(examination_time_str)
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|