endoreg-db 0.8.4.4__py3-none-any.whl → 0.8.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.

Files changed (36) hide show
  1. endoreg_db/management/commands/load_ai_model_data.py +2 -1
  2. endoreg_db/management/commands/setup_endoreg_db.py +11 -7
  3. endoreg_db/models/media/pdf/raw_pdf.py +241 -97
  4. endoreg_db/models/media/video/pipe_1.py +30 -33
  5. endoreg_db/models/media/video/video_file.py +300 -187
  6. endoreg_db/models/metadata/model_meta_logic.py +15 -1
  7. endoreg_db/models/metadata/sensitive_meta_logic.py +391 -70
  8. endoreg_db/serializers/__init__.py +26 -55
  9. endoreg_db/serializers/misc/__init__.py +1 -1
  10. endoreg_db/serializers/misc/file_overview.py +65 -35
  11. endoreg_db/serializers/misc/{vop_patient_data.py → sensitive_patient_data.py} +1 -1
  12. endoreg_db/serializers/video_examination.py +198 -0
  13. endoreg_db/services/lookup_service.py +228 -58
  14. endoreg_db/services/lookup_store.py +174 -30
  15. endoreg_db/services/pdf_import.py +585 -282
  16. endoreg_db/services/video_import.py +340 -101
  17. endoreg_db/urls/__init__.py +36 -23
  18. endoreg_db/urls/label_video_segments.py +2 -0
  19. endoreg_db/urls/media.py +3 -2
  20. endoreg_db/views/__init__.py +6 -3
  21. endoreg_db/views/media/pdf_media.py +3 -1
  22. endoreg_db/views/media/video_media.py +1 -1
  23. endoreg_db/views/media/video_segments.py +187 -259
  24. endoreg_db/views/pdf/__init__.py +5 -8
  25. endoreg_db/views/pdf/pdf_stream.py +187 -0
  26. endoreg_db/views/pdf/reimport.py +110 -94
  27. endoreg_db/views/requirement/lookup.py +171 -287
  28. endoreg_db/views/video/__init__.py +0 -2
  29. endoreg_db/views/video/video_examination_viewset.py +202 -289
  30. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.6.1.dist-info}/METADATA +1 -1
  31. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.6.1.dist-info}/RECORD +33 -34
  32. endoreg_db/views/pdf/pdf_media.py +0 -239
  33. endoreg_db/views/pdf/pdf_stream_views.py +0 -127
  34. endoreg_db/views/video/video_media.py +0 -158
  35. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.6.1.dist-info}/WHEEL +0 -0
  36. {endoreg_db-0.8.4.4.dist-info → endoreg_db-0.8.6.1.dist-info}/licenses/LICENSE +0 -0
@@ -1,81 +1,60 @@
1
1
  from .administration import (
2
+ ActiveModelSerializer,
3
+ AiModelSerializer,
2
4
  CenterSerializer,
3
5
  GenderSerializer,
4
- ActiveModelSerializer,
5
6
  ModelTypeSerializer,
6
- AiModelSerializer
7
7
  )
8
-
9
8
  from .examination import (
9
+ ExaminationDropdownSerializer,
10
10
  ExaminationSerializer,
11
11
  ExaminationTypeSerializer,
12
- ExaminationDropdownSerializer
13
12
  )
14
-
15
13
  from .finding import FindingSerializer
16
-
17
14
  from .finding_classification import (
18
15
  FindingClassificationChoiceSerializer,
19
- FindingClassificationSerializer
16
+ FindingClassificationSerializer,
20
17
  )
21
-
22
- from .label import (
23
- LabelSerializer,
24
- ImageClassificationAnnotationSerializer
25
- )
26
-
18
+ from .label import ImageClassificationAnnotationSerializer, LabelSerializer
27
19
  from .label_video_segment import (
28
- LabelVideoSegmentSerializer,
29
20
  LabelVideoSegmentAnnotationSerializer,
21
+ LabelVideoSegmentSerializer,
30
22
  )
31
-
32
23
  from .meta import (
33
- ReportMetaSerializer,
34
24
  PDFFileForMetaSerializer,
25
+ ReportMetaSerializer,
35
26
  SensitiveMetaDetailSerializer,
36
27
  SensitiveMetaUpdateSerializer,
37
28
  SensitiveMetaVerificationSerializer,
38
29
  VideoMetaSerializer,
39
30
  )
40
-
41
31
  from .misc import (
42
32
  FileOverviewSerializer,
43
- VoPPatientDataSerializer,
44
33
  StatsSerializer,
45
- UploadJobStatusSerializer,
34
+ TranslatableFieldMixin,
46
35
  UploadCreateResponseSerializer,
47
- TranslatableFieldMixin
48
- )
49
-
50
- from .patient import (
51
- PatientSerializer,
52
- PatientDropdownSerializer,
53
- )
54
-
55
- from .patient_examination import (
56
- PatientExaminationSerializer,
36
+ UploadJobStatusSerializer,
37
+ VoPPatientDataSerializer,
57
38
  )
58
-
39
+ from .patient import PatientDropdownSerializer, PatientSerializer
40
+ from .patient_examination import PatientExaminationSerializer
59
41
  from .patient_finding import (
60
- PatientFindingSerializer,
61
42
  PatientFindingClassificationSerializer,
62
43
  PatientFindingDetailSerializer,
63
44
  PatientFindingInterventionSerializer,
64
45
  PatientFindingListSerializer,
46
+ PatientFindingSerializer,
65
47
  PatientFindingWriteSerializer,
66
48
  )
67
-
68
- from .pdf import (
69
- RawPdfAnonyTextSerializer
70
- )
71
- from .report import (
72
- ReportListSerializer,
73
- ReportDataSerializer,
74
- SecureFileUrlSerializer
75
- )
76
-
49
+ from .pdf import RawPdfAnonyTextSerializer
50
+ from .report import ReportDataSerializer, ReportListSerializer, SecureFileUrlSerializer
77
51
  from .video.video_metadata import VideoMetadataSerializer
78
52
  from .video.video_processing_history import VideoProcessingHistorySerializer
53
+ from .video_examination import (
54
+ VideoExaminationCreateSerializer,
55
+ VideoExaminationSerializer,
56
+ VideoExaminationUpdateSerializer,
57
+ )
79
58
 
80
59
  __all__ = [
81
60
  # Administration
@@ -84,24 +63,19 @@ __all__ = [
84
63
  "ActiveModelSerializer",
85
64
  "ModelTypeSerializer",
86
65
  "AiModelSerializer",
87
-
88
66
  # Examination
89
67
  "ExaminationSerializer",
90
68
  "ExaminationTypeSerializer",
91
69
  "ExaminationDropdownSerializer",
92
-
93
70
  # Finding
94
- 'FindingSerializer',
95
- 'FindingClassificationSerializer',
71
+ "FindingSerializer",
72
+ "FindingClassificationSerializer",
96
73
  "FindingClassificationChoiceSerializer",
97
-
98
74
  "LabelSerializer",
99
75
  "ImageClassificationAnnotationSerializer",
100
-
101
76
  # LabelVideoSegment
102
77
  "LabelVideoSegmentSerializer",
103
78
  "LabelVideoSegmentAnnotationSerializer",
104
-
105
79
  # Meta
106
80
  "PDFFileForMetaSerializer",
107
81
  "ReportMetaSerializer",
@@ -109,7 +83,6 @@ __all__ = [
109
83
  "SensitiveMetaUpdateSerializer",
110
84
  "SensitiveMetaVerificationSerializer",
111
85
  "VideoMetaSerializer",
112
-
113
86
  # Misc
114
87
  "FileOverviewSerializer",
115
88
  "VoPPatientDataSerializer",
@@ -117,14 +90,11 @@ __all__ = [
117
90
  "UploadJobStatusSerializer",
118
91
  "UploadCreateResponseSerializer",
119
92
  "TranslatableFieldMixin",
120
-
121
93
  # Patient
122
94
  "PatientSerializer",
123
95
  "PatientDropdownSerializer",
124
-
125
96
  # Patient Examination
126
97
  "PatientExaminationSerializer",
127
-
128
98
  # Patient Finding
129
99
  "PatientFindingSerializer",
130
100
  "PatientFindingClassificationSerializer",
@@ -132,16 +102,17 @@ __all__ = [
132
102
  "PatientFindingInterventionSerializer",
133
103
  "PatientFindingListSerializer",
134
104
  "PatientFindingWriteSerializer",
135
-
136
105
  # PDF
137
106
  "RawPdfAnonyTextSerializer",
138
-
139
107
  # Report
140
108
  "ReportListSerializer",
141
109
  "ReportDataSerializer",
142
110
  "SecureFileUrlSerializer",
143
-
144
111
  # Video Correction (Phase 1.1)
145
112
  "VideoMetadataSerializer",
146
113
  "VideoProcessingHistorySerializer",
114
+ # Video Examination
115
+ "VideoExaminationSerializer",
116
+ "VideoExaminationCreateSerializer",
117
+ "VideoExaminationUpdateSerializer",
147
118
  ]
@@ -1,5 +1,5 @@
1
1
  from .file_overview import FileOverviewSerializer
2
- from .vop_patient_data import VoPPatientDataSerializer
2
+ from .sensitive_patient_data import VoPPatientDataSerializer
3
3
  from .stats import StatsSerializer
4
4
  from .upload_job import UploadJobStatusSerializer, UploadCreateResponseSerializer
5
5
  from .translatable_field_mix_in import TranslatableFieldMixin
@@ -1,12 +1,19 @@
1
- from rest_framework import serializers
2
1
  from typing import TYPE_CHECKING
3
- from endoreg_db.models.media import VideoFile, RawPdfFile
4
- from endoreg_db.models.state.video import AnonymizationStatus as VideoAnonymizationStatus
5
- from endoreg_db.models.state.raw_pdf import AnonymizationStatus as PdfAnonymizationStatus
2
+
3
+ from rest_framework import serializers
4
+
5
+ from endoreg_db.models.media import RawPdfFile, VideoFile
6
+ from endoreg_db.models.state.raw_pdf import (
7
+ AnonymizationStatus as PdfAnonymizationStatus,
8
+ )
9
+ from endoreg_db.models.state.video import (
10
+ AnonymizationStatus as VideoAnonymizationStatus,
11
+ )
6
12
 
7
13
  if TYPE_CHECKING:
8
14
  pass
9
15
 
16
+
10
17
  class FileOverviewSerializer(serializers.Serializer):
11
18
  """
12
19
  Polymorphic "union" serializer – we normalise both model types
@@ -22,75 +29,81 @@ class FileOverviewSerializer(serializers.Serializer):
22
29
  annotationStatus = serializers.CharField(read_only=True)
23
30
  createdAt = serializers.DateTimeField(read_only=True)
24
31
  text = serializers.CharField(required=False, allow_blank=True, read_only=True)
25
- anonymizedText = serializers.CharField(required=False, allow_blank=True, read_only=True)
32
+ anonymizedText = serializers.CharField(
33
+ required=False, allow_blank=True, read_only=True
34
+ )
26
35
 
27
36
  # --- converting DB objects to that shape -----------------------
28
37
  def to_representation(self, instance):
29
38
  """
30
39
  Return a unified dictionary representation of either a VideoFile or RawPdfFile instance for front-end use.
31
-
40
+
32
41
  For VideoFile instances, extracts and structures metadata such as patient, examination, equipment, and examiner information, and generates an anonymized version of the text by replacing sensitive fields with placeholders. For RawPdfFile instances, extracts text and anonymized text directly and determines statuses based on available fields.
33
-
42
+
34
43
  Parameters:
35
44
  instance: A VideoFile or RawPdfFile object to be serialized.
36
-
45
+
37
46
  Returns:
38
47
  dict: A normalized dictionary containing id, filename, mediaType, anonymizationStatus, annotationStatus, createdAt, text, and anonymizedText fields.
39
-
48
+
40
49
  Raises:
41
50
  TypeError: If the instance is not a VideoFile or RawPdfFile.
42
51
  """
43
52
  text = ""
44
53
  anonym_text = ""
45
-
54
+
46
55
  if isinstance(instance, VideoFile):
47
56
  media_type = "video"
48
57
  created_at = instance.uploaded_at
49
58
  filename = instance.original_file_name or (
50
- instance.raw_file.name.split("/")[-1] if instance.raw_file else "unknown"
59
+ instance.raw_file.name.split("/")[-1]
60
+ if instance.raw_file
61
+ else "unknown"
51
62
  )
52
-
63
+
53
64
  # ------- anonymization status using VideoState model
54
65
  vs = instance.state
55
- anonym_status = vs.anonymization_status if vs else VideoAnonymizationStatus.NOT_STARTED
56
-
66
+ anonym_status = (
67
+ vs.anonymization_status if vs else VideoAnonymizationStatus.NOT_STARTED
68
+ )
69
+
57
70
  # ------- annotation status (validated label segments)
58
71
  if instance.label_video_segments.filter(state__is_validated=True).exists():
59
72
  annot_status = "done"
60
73
  else:
61
74
  annot_status = "not_started"
62
-
75
+
63
76
  # ------- Extract text from sensitive_meta for videos
64
77
  if instance.sensitive_meta:
65
78
  sm = instance.sensitive_meta
66
79
  # Create a structured text representation from sensitive meta
67
80
  text_parts = []
68
-
81
+
69
82
  # Patient information
70
83
  if sm.patient_first_name or sm.patient_last_name:
71
84
  patient_name = f"{sm.patient_first_name or ''} {sm.patient_last_name or ''}".strip()
72
85
  text_parts.append(f"Patient: {patient_name}")
73
-
86
+
74
87
  if sm.patient_dob:
75
88
  text_parts.append(f"Date of Birth: {sm.patient_dob.date()}")
76
-
89
+
77
90
  if sm.patient_gender:
78
91
  text_parts.append(f"Gender: {sm.patient_gender}")
79
-
92
+
80
93
  # Examination information
81
94
  if sm.examination_date:
82
95
  text_parts.append(f"Examination Date: {sm.examination_date}")
83
-
96
+
84
97
  if sm.center:
85
98
  text_parts.append(f"Center: {sm.center.name}")
86
-
99
+
87
100
  # Equipment information
88
101
  if sm.endoscope_type:
89
102
  text_parts.append(f"Endoscope Type: {sm.endoscope_type}")
90
-
103
+
91
104
  if sm.endoscope_sn:
92
105
  text_parts.append(f"Endoscope SN: {sm.endoscope_sn}")
93
-
106
+
94
107
  # Examiner information
95
108
  if sm.pk: # Only if saved
96
109
  try:
@@ -100,39 +113,56 @@ class FileOverviewSerializer(serializers.Serializer):
100
113
  text_parts.append(f"Examiners: {examiner_names}")
101
114
  except Exception:
102
115
  pass # Ignore examiner lookup errors
103
-
116
+
104
117
  text = "\n".join(text_parts)
105
-
118
+
106
119
  # Create anonymized version by replacing sensitive data
107
120
  anonym_text = text
108
121
  if sm.patient_first_name:
109
- anonym_text = anonym_text.replace(sm.patient_first_name, "[FIRST_NAME]")
122
+ anonym_text = anonym_text.replace(
123
+ sm.patient_first_name, "[FIRST_NAME]"
124
+ )
110
125
  if sm.patient_last_name:
111
- anonym_text = anonym_text.replace(sm.patient_last_name, "[LAST_NAME]")
126
+ anonym_text = anonym_text.replace(
127
+ sm.patient_last_name, "[LAST_NAME]"
128
+ )
112
129
  if sm.patient_dob:
113
- anonym_text = anonym_text.replace(str(sm.patient_dob.date()), "[DOB]")
130
+ anonym_text = anonym_text.replace(
131
+ str(sm.patient_dob.date()), "[DOB]"
132
+ )
114
133
  if sm.endoscope_sn:
115
134
  anonym_text = anonym_text.replace(sm.endoscope_sn, "[ENDOSCOPE_SN]")
116
-
135
+
117
136
  # Replace examiner names if available
118
137
  if sm.pk:
119
138
  try:
120
139
  examiners = list(sm.examiners.all())
121
140
  for examiner in examiners:
122
- anonym_text = anonym_text.replace(str(examiner), "[EXAMINER]")
141
+ anonym_text = anonym_text.replace(
142
+ str(examiner), "[EXAMINER]"
143
+ )
123
144
  except Exception:
124
145
  pass
125
146
 
126
147
  elif isinstance(instance, RawPdfFile):
127
- instance:RawPdfFile
148
+ instance: RawPdfFile
128
149
  media_type = "pdf"
129
150
  created_at = instance.date_created
130
151
  filename = instance.file.name.split("/")[-1] if instance.file else "unknown"
131
- # Check anonymized_text field
132
- anonym_status = "done" if (instance.anonymized_text and instance.anonymized_text.strip()) else "not_started"
152
+
153
+ # ------- anonymization status using RawPdfState model (like VideoFile)
154
+ ps = instance.state
155
+ anonym_status = (
156
+ ps.anonymization_status if ps else PdfAnonymizationStatus.NOT_STARTED
157
+ )
158
+
133
159
  # PDF annotation == "sensitive meta validated"
134
- annot_status = "done" if getattr(instance.sensitive_meta, "is_verified", False) else "not_started"
135
-
160
+ annot_status = (
161
+ "done"
162
+ if getattr(instance.sensitive_meta, "is_verified", False)
163
+ else "not_started"
164
+ )
165
+
136
166
  # Extract text content from PDF
137
167
  text = instance.text or ""
138
168
  anonym_text = instance.anonymized_text or ""
@@ -80,7 +80,7 @@ class VoPPatientDataSerializer(serializers.Serializer):
80
80
 
81
81
  elif isinstance(instance, RawPdfFile):
82
82
  # Generate PDF streaming URL using pdf_id (RawPdfFile.id)
83
- pdf_stream_url = f"/api/pdfstream/{instance.pk}/"
83
+ pdf_stream_url = f"/api/media/pdfs/{instance.pk}/stream/"
84
84
 
85
85
  return {
86
86
  "id": instance.pk,
@@ -0,0 +1,198 @@
1
+ """
2
+ Video Examination Serializer
3
+
4
+ Serializes PatientExamination instances that are associated with VideoFile records.
5
+ This allows frontend components like VideoExaminationAnnotation.vue to display
6
+ and manage examinations within the video annotation workflow.
7
+ """
8
+
9
+ from rest_framework import serializers
10
+
11
+ from ..models import Examination, PatientExamination, VideoFile
12
+
13
+
14
+ class VideoExaminationSerializer(serializers.ModelSerializer):
15
+ """
16
+ Serializer for video-based patient examinations.
17
+
18
+ Exposes examination data within the context of video annotation:
19
+ - Basic examination metadata (type, date, hash)
20
+ - Related patient information (anonymized)
21
+ - Video reference
22
+ - Associated findings
23
+ """
24
+
25
+ # Custom fields for frontend compatibility
26
+ examination_name = serializers.CharField(source="examination.name", read_only=True)
27
+ examination_id = serializers.IntegerField(source="examination.id", read_only=True)
28
+ video_id = serializers.IntegerField(source="video.id", read_only=True)
29
+ patient_hash = serializers.CharField(source="patient.patient_hash", read_only=True)
30
+
31
+ # Nested findings data
32
+ findings = serializers.SerializerMethodField()
33
+
34
+ class Meta:
35
+ model = PatientExamination
36
+ fields = [
37
+ "id",
38
+ "hash",
39
+ "examination_id",
40
+ "examination_name",
41
+ "video_id",
42
+ "patient_hash",
43
+ "date_start",
44
+ "date_end",
45
+ "findings",
46
+ ]
47
+ read_only_fields = ["hash", "patient_hash"]
48
+
49
+ def get_findings(self, obj):
50
+ """
51
+ Return serialized findings associated with this examination.
52
+
53
+ Args:
54
+ obj: PatientExamination instance
55
+
56
+ Returns:
57
+ List of finding dictionaries with basic metadata
58
+ """
59
+ patient_findings = obj.patient_findings.all()
60
+ return [
61
+ {
62
+ "id": pf.id,
63
+ "finding_id": pf.finding.id if pf.finding else None,
64
+ "finding_name": pf.finding.name if pf.finding else None,
65
+ "created_at": pf.created_at if hasattr(pf, "created_at") else None,
66
+ }
67
+ for pf in patient_findings
68
+ ]
69
+
70
+
71
+ class VideoExaminationCreateSerializer(serializers.Serializer):
72
+ """
73
+ Serializer for creating video examinations via API.
74
+
75
+ Handles the complex creation logic required to link:
76
+ - VideoFile (must exist)
77
+ - Examination type (must exist)
78
+ - Patient (derived from video's SensitiveMeta)
79
+ - New PatientExamination record
80
+ """
81
+
82
+ video_id = serializers.IntegerField(required=True)
83
+ examination_id = serializers.IntegerField(required=True)
84
+ date_start = serializers.DateField(required=False, allow_null=True)
85
+ date_end = serializers.DateField(required=False, allow_null=True)
86
+
87
+ def validate_video_id(self, value):
88
+ """Ensure video exists"""
89
+ if not VideoFile.objects.filter(id=value).exists():
90
+ raise serializers.ValidationError(f"Video with id {value} does not exist")
91
+ return value
92
+
93
+ def validate_examination_id(self, value):
94
+ """Ensure examination type exists"""
95
+ if not Examination.objects.filter(id=value).exists():
96
+ raise serializers.ValidationError(
97
+ f"Examination with id {value} does not exist"
98
+ )
99
+ return value
100
+
101
+ def create(self, validated_data):
102
+ """
103
+ Create PatientExamination record.
104
+
105
+ Links video to examination through patient relationship:
106
+ 1. Get video and extract patient from SensitiveMeta
107
+ 2. Get examination type
108
+ 3. Create PatientExamination linking patient, examination, video
109
+
110
+ Raises:
111
+ ValidationError: If video has no patient or sensitive_meta
112
+ """
113
+ video = VideoFile.objects.get(id=validated_data["video_id"])
114
+ examination = Examination.objects.get(id=validated_data["examination_id"])
115
+
116
+ # Get patient from video's sensitive metadata
117
+ if not hasattr(video, "sensitive_meta") or not video.sensitive_meta:
118
+ raise serializers.ValidationError(
119
+ "Video must have sensitive metadata with patient information"
120
+ )
121
+
122
+ sensitive_meta = video.sensitive_meta
123
+ if not sensitive_meta.pseudo_patient:
124
+ raise serializers.ValidationError(
125
+ "Video's sensitive metadata must have an associated pseudo patient"
126
+ )
127
+
128
+ patient = sensitive_meta.pseudo_patient
129
+
130
+ # Check if PatientExamination already exists for this video
131
+ existing_exam = PatientExamination.objects.filter(video=video).first()
132
+ if existing_exam:
133
+ # Update existing
134
+ patient_exam = existing_exam
135
+ patient_exam.examination = examination
136
+ if "date_start" in validated_data:
137
+ patient_exam.date_start = validated_data["date_start"]
138
+ if "date_end" in validated_data:
139
+ patient_exam.date_end = validated_data["date_end"]
140
+ patient_exam.save()
141
+ else:
142
+ # Create new
143
+ patient_exam = PatientExamination.objects.create(
144
+ patient=patient,
145
+ examination=examination,
146
+ video=video,
147
+ date_start=validated_data.get("date_start"),
148
+ date_end=validated_data.get("date_end"),
149
+ )
150
+
151
+ return patient_exam
152
+
153
+
154
+ class VideoExaminationUpdateSerializer(serializers.Serializer):
155
+ """
156
+ Serializer for updating video examinations.
157
+
158
+ Allows modification of:
159
+ - Examination type
160
+ - Date range
161
+ - Associated findings (via separate endpoint)
162
+ """
163
+
164
+ examination_id = serializers.IntegerField(required=False)
165
+ date_start = serializers.DateField(required=False, allow_null=True)
166
+ date_end = serializers.DateField(required=False, allow_null=True)
167
+
168
+ def validate_examination_id(self, value):
169
+ """Ensure examination type exists if provided"""
170
+ if value is not None and not Examination.objects.filter(id=value).exists():
171
+ raise serializers.ValidationError(
172
+ f"Examination with id {value} does not exist"
173
+ )
174
+ return value
175
+
176
+ def update(self, instance, validated_data):
177
+ """
178
+ Update PatientExamination fields.
179
+
180
+ Args:
181
+ instance: Existing PatientExamination
182
+ validated_data: Validated update data
183
+
184
+ Returns:
185
+ Updated PatientExamination instance
186
+ """
187
+ if "examination_id" in validated_data:
188
+ examination = Examination.objects.get(id=validated_data["examination_id"])
189
+ instance.examination = examination
190
+
191
+ if "date_start" in validated_data:
192
+ instance.date_start = validated_data["date_start"]
193
+
194
+ if "date_end" in validated_data:
195
+ instance.date_end = validated_data["date_end"]
196
+
197
+ instance.save()
198
+ return instance