endoreg-db 0.6.1__py3-none-any.whl → 0.6.3__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/data/distribution/numeric/data.yaml +1 -1
- endoreg_db/mermaid/Overall_flow_patient_finding_intervention.md +10 -0
- endoreg_db/mermaid/anonymized_image_annotation.md +20 -0
- endoreg_db/mermaid/binary_classification_annotation.md +50 -0
- endoreg_db/mermaid/classification.md +8 -0
- endoreg_db/mermaid/examination.md +8 -0
- endoreg_db/mermaid/findings.md +7 -0
- endoreg_db/mermaid/image_classification.md +28 -0
- endoreg_db/mermaid/interventions.md +8 -0
- endoreg_db/mermaid/morphology.md +8 -0
- endoreg_db/mermaid/patient_creation.md +14 -0
- endoreg_db/mermaid/video_segmentation_annotation.md +17 -0
- endoreg_db/models/ai_model/ai_model.py +1 -1
- endoreg_db/models/ai_model/model_meta.py +1 -1
- endoreg_db/models/data_file/base_classes/utils.py +1 -1
- endoreg_db/serializers/raw_pdf_anony_text_validation.py +137 -0
- endoreg_db/serializers/raw_pdf_meta_validation.py +223 -0
- endoreg_db/serializers/raw_video_meta_validation.py +163 -1
- endoreg_db/serializers/video_segmentation.py +207 -112
- endoreg_db/urls.py +110 -11
- endoreg_db/views/raw_pdf_anony_text_validation_views.py +95 -0
- endoreg_db/views/raw_pdf_meta_validation_views.py +111 -0
- endoreg_db/views/raw_video_meta_validation_views.py +128 -18
- endoreg_db/views/video_segmentation_views.py +28 -11
- {endoreg_db-0.6.1.dist-info → endoreg_db-0.6.3.dist-info}/METADATA +11 -1
- {endoreg_db-0.6.1.dist-info → endoreg_db-0.6.3.dist-info}/RECORD +28 -13
- {endoreg_db-0.6.1.dist-info → endoreg_db-0.6.3.dist-info}/WHEEL +0 -0
- {endoreg_db-0.6.1.dist-info → endoreg_db-0.6.3.dist-info}/licenses/LICENSE +0 -0
endoreg_db/urls.py
CHANGED
|
@@ -10,20 +10,23 @@ from .views.csrf import csrf_token_view
|
|
|
10
10
|
#from .views.feature_selection_view import FetchSingleFramePredictionView // its implemented in ando-ai other project need to add here
|
|
11
11
|
from .views.video_segmentation_views import VideoView, VideoLabelView,UpdateLabelSegmentsView
|
|
12
12
|
from .views.views_for_timeline import video_timeline_view
|
|
13
|
-
from .views.raw_video_meta_validation_views import VideoFileForMetaView
|
|
13
|
+
from .views.raw_video_meta_validation_views import VideoFileForMetaView, VideoFileForMetaView
|
|
14
|
+
from .views.raw_pdf_meta_validation_views import PDFFileForMetaView
|
|
15
|
+
from .views.raw_pdf_meta_validation_views import UpdateSensitiveMetaView
|
|
16
|
+
from .views.raw_pdf_anony_text_validation_views import RawPdfAnonyTextView, UpdateAnonymizedTextView
|
|
14
17
|
router = DefaultRouter()
|
|
15
18
|
router.register(r'patients', PatientViewSet)
|
|
16
19
|
|
|
20
|
+
|
|
17
21
|
urlpatterns = [
|
|
18
22
|
path('start-examination/', start_examination, name="start_examination"),
|
|
19
23
|
path('get-location-choices/<int:location_id>/', get_location_choices, name="get_location_choices"),
|
|
20
24
|
path('get-morphology-choices/<int:morphology_id>/', get_morphology_choices, name="get_morphology_choices"),
|
|
21
25
|
path('api/', include(router.urls)),
|
|
22
26
|
path('api/conf/', csrf_token_view, name='csrf_token'),
|
|
23
|
-
|
|
24
27
|
|
|
25
28
|
|
|
26
|
-
#--------------------------------------VIDEO SEGMENTATION END POINTS--------------------------------------
|
|
29
|
+
#--------------------------------------START : VIDEO SEGMENTATION END POINTS--------------------------------------
|
|
27
30
|
|
|
28
31
|
# The dropdown contains video names and their corresponding IDs, which are retrieved from the database(RawVideoFile). Additionally, this route(api/videos) also fetches labels along with their names and IDs from the label table.
|
|
29
32
|
# We will modify this implementation later as per our requirements.
|
|
@@ -153,21 +156,117 @@ urlpatterns = [
|
|
|
153
156
|
#
|
|
154
157
|
path("api/video/<int:video_id>/label/<int:label_id>/update_segments/", UpdateLabelSegmentsView.as_view(), name="update_label_segments"),
|
|
155
158
|
|
|
156
|
-
#----------------------------------END--VIDEO SEGMENTATION SECTION-------------------------------
|
|
157
|
-
#this is for to test the timeline
|
|
158
|
-
#need to delete this url and also endoreg_db_production/endoreg_db/views/views_for_timeline.py and endoreg_db_production/endoreg_db/templates/timeline.html
|
|
159
|
-
path('video/<int:video_id>/timeline/', video_timeline_view, name='video_timeline'),
|
|
160
159
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
160
|
+
#----------------------------------START : SENSITIVE META AND RAWVIDEOFILE VIDEO PATIENT DETAILS-------------------------------
|
|
161
|
+
|
|
162
|
+
# API Endpoint for fetching video metadata or streaming the next available video
|
|
163
|
+
# This endpoint is used by the frontend to fetch:
|
|
164
|
+
# - The first available video if `last_id` is NOT provided.
|
|
165
|
+
# - The next available video where `id > last_id` if `last_id` is provided.
|
|
166
|
+
# - If `Accept: application/json` is set in headers, it returns video metadata as JSON.
|
|
167
|
+
# - If no videos are available, it returns {"error": "No more videos available."}.
|
|
164
168
|
# const url = lastId ? `http://localhost:8000/api/video/meta/?last_id=${lastId}` : "http://localhost:8000/api/video/meta/";
|
|
165
169
|
path("api/video/sensitivemeta/", VideoFileForMetaView.as_view(), name="video_meta"), # Single endpoint for both first and next video
|
|
166
|
-
]
|
|
167
170
|
|
|
168
171
|
|
|
169
172
|
|
|
173
|
+
# This API endpoint allows updating specific patient details (SensitiveMeta)
|
|
174
|
+
# linked to a video. It is used to correct or modify the patient's first name,
|
|
175
|
+
# last name, date of birth, and examination date.
|
|
176
|
+
# Fetch video metadata and update patient details
|
|
177
|
+
# The frontend should send a JSON request body like this:
|
|
178
|
+
# {
|
|
179
|
+
# "sensitive_meta_id": 2, # The ID of the SensitiveMeta entry (REQUIRED)
|
|
180
|
+
# "patient_first_name": "John", # New first name (REQUIRED, cannot be empty)
|
|
181
|
+
# "patient_last_name": "Doe", # New last name (REQUIRED, cannot be empty)
|
|
182
|
+
# "patient_dob": "1985-06-15", # New Date of Birth (REQUIRED, format YYYY-MM-DD)
|
|
183
|
+
# "examination_date": "2024-03-20" # New Examination Date (OPTIONAL, format YYYY-MM-DD)
|
|
184
|
+
# }
|
|
185
|
+
# - The frontend sends a PATCH request to this endpoint with updated patient data.
|
|
186
|
+
# - The backend validates the input using the serializer (`SensitiveMetaUpdateSerializer`).
|
|
187
|
+
# - If validation passes, the patient information is updated in the database.
|
|
188
|
+
# - If there are errors (e.g., missing fields, incorrect date format),
|
|
189
|
+
# the API returns structured error messages.
|
|
190
|
+
path("api/video/update_sensitivemeta/", VideoFileForMetaView.as_view(), name="update_patient_meta"),
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
#----------------------------------START : SENSITIVE META AND RAWPDFOFILE PDF PATIENT DETAILS-------------------------------
|
|
196
|
+
|
|
197
|
+
#The first request (without id) fetches the first available PDF metadata.
|
|
198
|
+
#The "Next" button (with id) fetches the next available PDF.
|
|
199
|
+
#If an id is provided, the API returns the actual PDF file instead of JSON.
|
|
200
|
+
path("api/pdf/sensitivemeta/", PDFFileForMetaView.as_view(), name="pdf_meta"),
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
|
|
205
|
+
# This API endpoint allows updating specific patient details (SensitiveMeta)
|
|
206
|
+
# linked to a PDF record. It enables modifying the patient's first name,
|
|
207
|
+
# last name, date of birth, and examination date.
|
|
208
|
+
|
|
209
|
+
# The frontend should send a JSON request body like this:
|
|
210
|
+
# {
|
|
211
|
+
# "sensitive_meta_id": 2, # The ID of the SensitiveMeta entry (REQUIRED)
|
|
212
|
+
# "patient_first_name": "John", # New first name (OPTIONAL, if provided, cannot be empty)
|
|
213
|
+
# "patient_last_name": "Doe", # New last name (OPTIONAL, if provided, cannot be empty)
|
|
214
|
+
# "patient_dob": "1985-06-15", # New Date of Birth (OPTIONAL, format YYYY-MM-DD)
|
|
215
|
+
# "examination_date": "2024-03-20" # New Examination Date (OPTIONAL, format YYYY-MM-DD)
|
|
216
|
+
# }
|
|
217
|
+
|
|
218
|
+
# - The frontend sends a PATCH request to this endpoint with the updated patient data.
|
|
219
|
+
# - The backend processes the request and updates only the fields that are provided.
|
|
220
|
+
# - If validation passes, the corresponding SensitiveMeta entry is updated in the database.
|
|
221
|
+
# - If errors occur (e.g., invalid ID, empty fields, incorrect date format),
|
|
222
|
+
# the API returns structured error messages.
|
|
223
|
+
|
|
224
|
+
path("api/pdf/update_sensitivemeta/", UpdateSensitiveMetaView.as_view(), name="update_pdf_meta"),
|
|
225
|
+
|
|
170
226
|
|
|
171
227
|
|
|
172
228
|
|
|
229
|
+
|
|
230
|
+
# API Endpoint for Fetching PDF Data (Including Anonymized Text)
|
|
231
|
+
# - This endpoint is used when the page loads.
|
|
232
|
+
# - Fetches the first available PDF (if no `last_id` is provided).
|
|
233
|
+
# - If `last_id` is given, fetches the next available PDF.
|
|
234
|
+
# - The frontend calls this endpoint on **page load** and when clicking the **next button**.
|
|
235
|
+
# Example frontend usage:
|
|
236
|
+
# const url = lastId ? `http://localhost:8000/api/pdf/anony_text/?last_id=${lastId}`
|
|
237
|
+
# : "http://localhost:8000/api/pdf/anony_text/";
|
|
238
|
+
path("api/pdf/anony_text/", RawPdfAnonyTextView.as_view(), name="pdf_anony_text"),
|
|
239
|
+
|
|
240
|
+
# API Endpoint for Updating the `anonymized_text` Field in `RawPdfFile`
|
|
241
|
+
# - This endpoint is called when the user edits the anonymized text and clicks **Save**.
|
|
242
|
+
# - Updates only the `anonymized_text` field for the specified PDF `id`.
|
|
243
|
+
# - The frontend sends a **PATCH request** to this endpoint with the updated text.
|
|
244
|
+
# Example frontend usage:
|
|
245
|
+
# axios.patch("http://localhost:8000/api/pdf/update_anony_text/", { id: 1, anonymized_text: "Updated text" });
|
|
246
|
+
path("api/pdf/update_anony_text/", UpdateAnonymizedTextView.as_view(), name="update_pdf_anony_text"),
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
#this is for, to test the timeline
|
|
263
|
+
#need to delete this url and also endoreg_db_production/endoreg_db/views/views_for_timeline.py and endoreg_db_production/endoreg_db/templates/timeline.html
|
|
264
|
+
path('video/<int:video_id>/timeline/', video_timeline_view, name='video_timeline'),
|
|
265
|
+
]
|
|
266
|
+
|
|
267
|
+
|
|
173
268
|
#https://biigle.de/manual/tutorials/videos/navigating-timeline#for time line example
|
|
269
|
+
from django.conf import settings
|
|
270
|
+
from django.conf.urls.static import static
|
|
271
|
+
if settings.DEBUG:
|
|
272
|
+
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
from rest_framework.views import APIView
|
|
2
|
+
from rest_framework.response import Response
|
|
3
|
+
from rest_framework import status
|
|
4
|
+
from django.http import FileResponse, Http404
|
|
5
|
+
import os, mimetypes
|
|
6
|
+
from ..models import RawPdfFile
|
|
7
|
+
from ..serializers.raw_pdf_anony_text_validation import RawPdfAnonyTextSerializer
|
|
8
|
+
|
|
9
|
+
class RawPdfAnonyTextView(APIView):
|
|
10
|
+
"""
|
|
11
|
+
API for:
|
|
12
|
+
- Fetching PDF metadata including `anonymized_text`.
|
|
13
|
+
- Fetching the next available PDF.
|
|
14
|
+
- Serving the actual PDF file.
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
def get(self, request):
|
|
18
|
+
"""
|
|
19
|
+
Handles:
|
|
20
|
+
- First available PDF if `last_id` is NOT provided.
|
|
21
|
+
- Next available PDF if `last_id` is provided.
|
|
22
|
+
- Returns the actual PDF file if `id` is provided.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
pdf_id = request.GET.get("id")
|
|
26
|
+
last_id = request.GET.get("last_id")
|
|
27
|
+
|
|
28
|
+
if pdf_id:
|
|
29
|
+
return self.serve_pdf_file(pdf_id)
|
|
30
|
+
else:
|
|
31
|
+
return self.fetch_pdf_metadata(last_id)
|
|
32
|
+
|
|
33
|
+
def fetch_pdf_metadata(self, last_id):
|
|
34
|
+
"""
|
|
35
|
+
Fetches the next available PDF metadata, including `anonymized_text`.
|
|
36
|
+
"""
|
|
37
|
+
pdf_entry = RawPdfAnonyTextSerializer.get_next_pdf(last_id)
|
|
38
|
+
|
|
39
|
+
if pdf_entry is None:
|
|
40
|
+
return Response({"error": "No more PDFs available."}, status=status.HTTP_404_NOT_FOUND)
|
|
41
|
+
|
|
42
|
+
serialized_pdf = RawPdfAnonyTextSerializer(pdf_entry, context={'request': self.request})
|
|
43
|
+
return Response(serialized_pdf.data, status=status.HTTP_200_OK)
|
|
44
|
+
|
|
45
|
+
def serve_pdf_file(self, pdf_id):
|
|
46
|
+
"""
|
|
47
|
+
Serves the actual PDF file for viewing.
|
|
48
|
+
"""
|
|
49
|
+
try:
|
|
50
|
+
pdf_entry = RawPdfFile.objects.get(id=pdf_id)
|
|
51
|
+
if not pdf_entry.file:
|
|
52
|
+
return Response({"error": "PDF file not found."}, status=status.HTTP_404_NOT_FOUND)
|
|
53
|
+
|
|
54
|
+
full_pdf_path = pdf_entry.file.path
|
|
55
|
+
if not os.path.exists(full_pdf_path):
|
|
56
|
+
raise Http404("PDF file not found on server.")
|
|
57
|
+
|
|
58
|
+
mime_type, _ = mimetypes.guess_type(full_pdf_path)
|
|
59
|
+
response = FileResponse(open(full_pdf_path, "rb"), content_type=mime_type or "application/pdf")
|
|
60
|
+
response["Content-Disposition"] = f'inline; filename="{os.path.basename(full_pdf_path)}"'
|
|
61
|
+
return response
|
|
62
|
+
|
|
63
|
+
except RawPdfFile.DoesNotExist:
|
|
64
|
+
return Response({"error": "Invalid PDF ID."}, status=status.HTTP_400_BAD_REQUEST)
|
|
65
|
+
except Exception as e:
|
|
66
|
+
return Response({"error": f"Internal error: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class UpdateAnonymizedTextView(APIView):
|
|
70
|
+
"""
|
|
71
|
+
API to update only `anonymized_text` in `RawPdfFile`
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
def patch(self, request):
|
|
75
|
+
"""
|
|
76
|
+
Updates `anonymized_text` for a given `pdf_id`.
|
|
77
|
+
"""
|
|
78
|
+
pdf_id = request.data.get("id")
|
|
79
|
+
|
|
80
|
+
if not pdf_id:
|
|
81
|
+
return Response({"error": "pdf_id is required."}, status=status.HTTP_400_BAD_REQUEST)
|
|
82
|
+
|
|
83
|
+
try:
|
|
84
|
+
pdf_entry = RawPdfFile.objects.get(id=pdf_id)
|
|
85
|
+
except RawPdfFile.DoesNotExist:
|
|
86
|
+
return Response({"error": "PDF not found."}, status=status.HTTP_404_NOT_FOUND)
|
|
87
|
+
|
|
88
|
+
serializer = RawPdfAnonyTextSerializer(pdf_entry, data=request.data, partial=True)
|
|
89
|
+
|
|
90
|
+
if serializer.is_valid():
|
|
91
|
+
serializer.save()
|
|
92
|
+
return Response({"message": "PDF anonymized_text updated successfully.", "updated_data": serializer.data},
|
|
93
|
+
status=status.HTTP_200_OK)
|
|
94
|
+
|
|
95
|
+
return Response({"error": "Invalid data.", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
from rest_framework.views import APIView
|
|
2
|
+
from rest_framework.response import Response
|
|
3
|
+
from rest_framework import status
|
|
4
|
+
from django.http import FileResponse, Http404
|
|
5
|
+
import mimetypes
|
|
6
|
+
import os
|
|
7
|
+
from ..models import RawPdfFile
|
|
8
|
+
from ..serializers.raw_pdf_meta_validation import PDFFileForMetaSerializer
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class PDFFileForMetaView(APIView):
|
|
12
|
+
"""
|
|
13
|
+
API endpoint to:
|
|
14
|
+
- Fetch PDF metadata if `id` is NOT provided.
|
|
15
|
+
- Serve the actual PDF file if `id` is provided.
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
def get(self, request):
|
|
19
|
+
"""
|
|
20
|
+
Handles both:
|
|
21
|
+
Fetching PDF metadata** (if `id` is NOT provided)
|
|
22
|
+
Serving the actual PDF file** (if `id` is provided)
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
pdf_id = request.GET.get("id") # Check if 'id' is provided in the query params
|
|
26
|
+
last_id = request.GET.get("last_id") # Check if 'last_id' is provided for pagination
|
|
27
|
+
|
|
28
|
+
if pdf_id:
|
|
29
|
+
return self.serve_pdf_file(pdf_id) # Serve the actual PDF file
|
|
30
|
+
else:
|
|
31
|
+
return self.fetch_pdf_metadata(last_id) # Fetch metadata for the first or next PDF
|
|
32
|
+
|
|
33
|
+
def fetch_pdf_metadata(self, last_id):
|
|
34
|
+
"""
|
|
35
|
+
Fetches the first or next available PDF metadata.
|
|
36
|
+
"""
|
|
37
|
+
pdf_entry = PDFFileForMetaSerializer.get_next_pdf(last_id)
|
|
38
|
+
|
|
39
|
+
if pdf_entry is None:
|
|
40
|
+
return Response({"error": "No more PDFs available."}, status=status.HTTP_404_NOT_FOUND)
|
|
41
|
+
|
|
42
|
+
serialized_pdf = PDFFileForMetaSerializer(pdf_entry, context={'request': self.request})
|
|
43
|
+
|
|
44
|
+
print("Debugging API Response:")
|
|
45
|
+
print("Serialized Data:", serialized_pdf.data) # Debugging
|
|
46
|
+
return Response(serialized_pdf.data, status=status.HTTP_200_OK)
|
|
47
|
+
|
|
48
|
+
def serve_pdf_file(self, pdf_id):
|
|
49
|
+
"""
|
|
50
|
+
Serves the actual PDF file for download or viewing.
|
|
51
|
+
"""
|
|
52
|
+
try:
|
|
53
|
+
pdf_entry = RawPdfFile.objects.get(id=pdf_id) # Get the PDF file by ID
|
|
54
|
+
|
|
55
|
+
if not pdf_entry.file:
|
|
56
|
+
return Response({"error": "PDF file not found."}, status=status.HTTP_404_NOT_FOUND)
|
|
57
|
+
|
|
58
|
+
full_pdf_path = pdf_entry.file.path # Get the absolute file path
|
|
59
|
+
|
|
60
|
+
if not os.path.exists(full_pdf_path):
|
|
61
|
+
raise Http404("PDF file not found on server.")
|
|
62
|
+
|
|
63
|
+
mime_type, _ = mimetypes.guess_type(full_pdf_path) # Detect file type
|
|
64
|
+
response = FileResponse(open(full_pdf_path, "rb"), content_type=mime_type or "application/pdf")
|
|
65
|
+
|
|
66
|
+
response["Content-Disposition"] = f'inline; filename="{os.path.basename(full_pdf_path)}"' # Allows direct viewing
|
|
67
|
+
|
|
68
|
+
return response # Sends the PDF file as a stream
|
|
69
|
+
|
|
70
|
+
except RawPdfFile.DoesNotExist:
|
|
71
|
+
return Response({"error": "Invalid PDF ID."}, status=status.HTTP_400_BAD_REQUEST)
|
|
72
|
+
|
|
73
|
+
except Exception as e:
|
|
74
|
+
return Response({"error": f"Internal error: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
75
|
+
|
|
76
|
+
from rest_framework.views import APIView
|
|
77
|
+
from rest_framework.response import Response
|
|
78
|
+
from rest_framework import status
|
|
79
|
+
from ..models import SensitiveMeta
|
|
80
|
+
from ..serializers.raw_pdf_meta_validation import SensitiveMetaUpdateSerializer
|
|
81
|
+
|
|
82
|
+
class UpdateSensitiveMetaView(APIView):
|
|
83
|
+
"""
|
|
84
|
+
API endpoint to update patient details in the SensitiveMeta table.
|
|
85
|
+
Handles partial updates (only edited fields).
|
|
86
|
+
"""
|
|
87
|
+
|
|
88
|
+
def patch(self, request, *args, **kwargs):
|
|
89
|
+
"""
|
|
90
|
+
Updates the provided fields for a specific patient record.
|
|
91
|
+
Only updates fields that are sent in the request.
|
|
92
|
+
"""
|
|
93
|
+
sensitive_meta_id = request.data.get("sensitive_meta_id") # Required field
|
|
94
|
+
|
|
95
|
+
if not sensitive_meta_id:
|
|
96
|
+
return Response({"error": "sensitive_meta_id is required."}, status=status.HTTP_400_BAD_REQUEST)
|
|
97
|
+
|
|
98
|
+
try:
|
|
99
|
+
sensitive_meta = SensitiveMeta.objects.get(id=sensitive_meta_id)
|
|
100
|
+
except SensitiveMeta.DoesNotExist:
|
|
101
|
+
return Response({"error": "Patient record not found."}, status=status.HTTP_404_NOT_FOUND)
|
|
102
|
+
|
|
103
|
+
# Serialize the request data with partial=True to allow partial updates
|
|
104
|
+
serializer = SensitiveMetaUpdateSerializer(sensitive_meta, data=request.data, partial=True)
|
|
105
|
+
|
|
106
|
+
if serializer.is_valid():
|
|
107
|
+
serializer.save()
|
|
108
|
+
return Response({"message": "Patient information updated successfully.", "updated_data": serializer.data},
|
|
109
|
+
status=status.HTTP_200_OK)
|
|
110
|
+
|
|
111
|
+
return Response({"error": "Invalid data.", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
|
|
@@ -1,38 +1,148 @@
|
|
|
1
1
|
from rest_framework.views import APIView
|
|
2
2
|
from rest_framework.response import Response
|
|
3
3
|
from rest_framework import status
|
|
4
|
+
from django.http import FileResponse, Http404
|
|
5
|
+
import mimetypes
|
|
6
|
+
import os
|
|
4
7
|
from ..models import RawVideoFile
|
|
5
|
-
from ..serializers.raw_video_meta_validation import VideoFileForMetaSerializer
|
|
8
|
+
from ..serializers.raw_video_meta_validation import VideoFileForMetaSerializer,SensitiveMetaUpdateSerializer
|
|
9
|
+
from ..models import SensitiveMeta
|
|
10
|
+
|
|
6
11
|
|
|
7
12
|
class VideoFileForMetaView(APIView):
|
|
8
13
|
"""
|
|
9
14
|
API endpoint to fetch video metadata step-by-step.
|
|
10
|
-
|
|
11
|
-
If last_id is given Returns the next available video.
|
|
15
|
+
Uses the serializer to get the first or next available video.
|
|
12
16
|
"""
|
|
13
17
|
|
|
14
|
-
##need to change this fucntion , like the previous one
|
|
15
|
-
|
|
16
18
|
def get(self, request):
|
|
17
19
|
"""
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
20
|
+
- Fetches the **first available video** if `last_id` is NOT provided.
|
|
21
|
+
- Fetches the **next available video** where `id > last_id` if provided.
|
|
22
|
+
- If no video is available, returns a structured error response.
|
|
21
23
|
"""
|
|
22
|
-
last_id = request.GET.get("last_id") # Get last_id from query params
|
|
24
|
+
last_id = request.GET.get("last_id") # Get last_id from query params
|
|
25
|
+
|
|
26
|
+
# Get the next video as a model instance
|
|
27
|
+
video_entry = VideoFileForMetaSerializer.get_next_video(last_id)
|
|
28
|
+
|
|
29
|
+
if video_entry is None:
|
|
30
|
+
return Response({"error": "No more videos available."}, status=status.HTTP_404_NOT_FOUND)
|
|
31
|
+
|
|
32
|
+
serialized_video = VideoFileForMetaSerializer(video_entry, context={'request': request})
|
|
33
|
+
|
|
34
|
+
# Check if required fields are missing
|
|
35
|
+
response_data = serialized_video.data
|
|
36
|
+
missing_fields = {}
|
|
37
|
+
|
|
38
|
+
if response_data.get('file') is None:
|
|
39
|
+
missing_fields['file'] = "No file associated with this entry."
|
|
40
|
+
|
|
41
|
+
if response_data.get('video_url') is None:
|
|
42
|
+
missing_fields['video_url'] = "Video file is missing."
|
|
43
|
+
|
|
44
|
+
if response_data.get('full_video_path') is None:
|
|
45
|
+
missing_fields['full_video_path'] = "No file path found on server."
|
|
23
46
|
|
|
47
|
+
if not response_data.get('patient_first_name'):
|
|
48
|
+
missing_fields['patient_first_name'] = "Patient first name is missing."
|
|
49
|
+
|
|
50
|
+
if not response_data.get('patient_last_name'):
|
|
51
|
+
missing_fields['patient_last_name'] = "Patient last name is missing."
|
|
52
|
+
|
|
53
|
+
if not response_data.get('patient_dob'):
|
|
54
|
+
missing_fields['patient_dob'] = "Patient date of birth is missing."
|
|
55
|
+
|
|
56
|
+
if not response_data.get('examination_date'):
|
|
57
|
+
missing_fields['examination_date'] = "Examination date is missing."
|
|
58
|
+
|
|
59
|
+
if response_data.get('duration') is None:
|
|
60
|
+
missing_fields['duration'] = "Unable to determine video duration. The file might be corrupted or unreadable."
|
|
61
|
+
|
|
62
|
+
if missing_fields:
|
|
63
|
+
return Response({"error": "Missing required data.", "details": missing_fields},
|
|
64
|
+
status=status.HTTP_400_BAD_REQUEST)
|
|
65
|
+
|
|
66
|
+
return Response(serialized_video.data, status=status.HTTP_200_OK)
|
|
67
|
+
|
|
68
|
+
def serve_video_file(self, video_entry):
|
|
69
|
+
"""
|
|
70
|
+
Streams the video file dynamically.
|
|
71
|
+
"""
|
|
24
72
|
try:
|
|
25
|
-
|
|
26
|
-
# id__gt is orm syntax which is equal to SELECT * FROM rawvideofile WHERE id > 2 ORDER BY id ASC LIMIT 1;
|
|
73
|
+
full_video_path = video_entry.file.path # Get file path
|
|
27
74
|
|
|
28
|
-
|
|
29
|
-
|
|
75
|
+
if not os.path.exists(full_video_path):
|
|
76
|
+
raise Http404("Video file not found.")
|
|
30
77
|
|
|
31
|
-
|
|
32
|
-
|
|
78
|
+
mime_type, _ = mimetypes.guess_type(full_video_path) # Detects file type
|
|
79
|
+
response = FileResponse(open(full_video_path, "rb"), content_type=mime_type or "video/mp4")
|
|
33
80
|
|
|
34
|
-
|
|
35
|
-
|
|
81
|
+
response["Content-Disposition"] = f'inline; filename="{os.path.basename(full_video_path)}"' # Allows direct streaming
|
|
82
|
+
|
|
83
|
+
return response # Sends the video file as a stream
|
|
36
84
|
|
|
37
85
|
except Exception as e:
|
|
38
|
-
return Response({"error": f"Internal
|
|
86
|
+
return Response({"error": f"Internal error: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def patch(self, request, *args, **kwargs):
|
|
90
|
+
"""
|
|
91
|
+
Calls the serializer to update `SensitiveMeta` data.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
# Ensure all required fields are provided
|
|
95
|
+
required_fields = ["sensitive_meta_id", "patient_first_name", "patient_last_name", "patient_dob", "examination_date"]
|
|
96
|
+
missing_fields = [field for field in required_fields if field not in request.data]
|
|
97
|
+
|
|
98
|
+
if missing_fields:
|
|
99
|
+
return Response({"error": "Missing required fields", "missing_fields": missing_fields}, status=status.HTTP_400_BAD_REQUEST)
|
|
100
|
+
|
|
101
|
+
# Call serializer for validation
|
|
102
|
+
serializer = SensitiveMetaUpdateSerializer(data=request.data, partial=True)
|
|
103
|
+
|
|
104
|
+
if serializer.is_valid():
|
|
105
|
+
# Get the instance and update it
|
|
106
|
+
sensitive_meta = SensitiveMeta.objects.get(id=request.data["sensitive_meta_id"])
|
|
107
|
+
updated_instance = serializer.update(sensitive_meta, serializer.validated_data)
|
|
108
|
+
|
|
109
|
+
return Response({
|
|
110
|
+
"message": "Patient information updated successfully.",
|
|
111
|
+
"updated_data": SensitiveMetaUpdateSerializer(updated_instance).data
|
|
112
|
+
}, status=status.HTTP_200_OK)
|
|
113
|
+
|
|
114
|
+
# Return validation errors
|
|
115
|
+
return Response({"error": "Invalid data.", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
"""
|
|
119
|
+
await import('https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js');
|
|
120
|
+
const updatePatientInfo = async () => {
|
|
121
|
+
const updatedData = {
|
|
122
|
+
sensitive_meta_id: 2,
|
|
123
|
+
patient_first_name: "Placeholder",
|
|
124
|
+
patient_last_name: "Placeholder",
|
|
125
|
+
patient_dob: "1994-06-15",
|
|
126
|
+
examination_date: "2024-06-15"
|
|
127
|
+
};
|
|
128
|
+
|
|
129
|
+
try {
|
|
130
|
+
const response = await axios.patch("http://localhost:8000/api/video/update_sensitivemeta/", updatedData, {
|
|
131
|
+
headers: { "Content-Type": "application/json" }
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
console.log("Update Success:", response.data);
|
|
135
|
+
alert("Patient information updated successfully!");
|
|
136
|
+
|
|
137
|
+
return response.data;
|
|
138
|
+
} catch (error) {
|
|
139
|
+
console.error("Update Error:", error.response?.data || error);
|
|
140
|
+
alert("Failed to update patient information.");
|
|
141
|
+
return error.response?.data || { error: "Unknown error" };
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
updatePatientInfo().then(response => console.log("Final Response:", response));
|
|
147
|
+
|
|
148
|
+
"""
|
|
@@ -31,9 +31,14 @@ class VideoView(APIView):
|
|
|
31
31
|
Returns a list of all available videos along with available labels.
|
|
32
32
|
Used to populate the video selection dropdown in Vue.js.
|
|
33
33
|
"""
|
|
34
|
+
|
|
34
35
|
videos = RawVideoFile.objects.all()
|
|
35
36
|
labels = Label.objects.all() # Fetch all labels
|
|
36
37
|
|
|
38
|
+
if not videos.exists():
|
|
39
|
+
return Response({"error": "No videos found in the database."}, status=status.HTTP_404_NOT_FOUND)
|
|
40
|
+
|
|
41
|
+
|
|
37
42
|
video_serializer = VideoListSerializer(videos, many=True)
|
|
38
43
|
label_serializer = LabelSerializer(labels, many=True) # Serialize labels
|
|
39
44
|
|
|
@@ -134,16 +139,28 @@ class UpdateLabelSegmentsView(APIView):
|
|
|
134
139
|
|
|
135
140
|
def put(self, request, video_id, label_id):
|
|
136
141
|
"""
|
|
137
|
-
|
|
142
|
+
Updates segments for a given video & label.
|
|
138
143
|
"""
|
|
139
|
-
serializer = LabelSegmentUpdateSerializer(data=request.data)
|
|
140
144
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
145
|
+
# Ensure required fields are provided
|
|
146
|
+
required_fields = ["video_id", "label_id", "segments"]
|
|
147
|
+
missing_fields = [field for field in required_fields if field not in request.data]
|
|
148
|
+
|
|
149
|
+
if missing_fields:
|
|
150
|
+
return Response({"error": "Missing required fields", "missing": missing_fields}, status=status.HTTP_400_BAD_REQUEST)
|
|
151
|
+
|
|
152
|
+
# Validate input data
|
|
153
|
+
serializer = LabelSegmentUpdateSerializer(data=request.data, partial=True)
|
|
154
|
+
|
|
155
|
+
if not serializer.is_valid():
|
|
156
|
+
return Response({"error": "Invalid segment data", "details": serializer.errors}, status=status.HTTP_400_BAD_REQUEST)
|
|
157
|
+
|
|
158
|
+
# Process and save segment updates
|
|
159
|
+
result = serializer.save()
|
|
160
|
+
|
|
161
|
+
return Response({
|
|
162
|
+
"message": "Segments updated successfully.",
|
|
163
|
+
"updated_segments": result["updated_segments"],
|
|
164
|
+
"new_segments": result["new_segments"],
|
|
165
|
+
"deleted_segments": result["deleted_segments"]
|
|
166
|
+
}, status=status.HTTP_200_OK)
|
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: endoreg-db
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: EndoReg Db Django App
|
|
5
|
+
Project-URL: Homepage, https://info.coloreg.de
|
|
6
|
+
Project-URL: Repository, https://github.com/wg-lux/endoreg-db
|
|
7
|
+
Author: Max Hild, Hamza Zahid, Peter Kowalczyk
|
|
8
|
+
Author-email: "Thomas J. Lux" <lux_t1@ukw.de>
|
|
9
|
+
Maintainer: Max Hild, Hamza Zahid, Peter Kowalczyk
|
|
10
|
+
Maintainer-email: "Thomas J. Lux" <lux_t1@ukw.de>
|
|
5
11
|
License-File: LICENSE
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Topic :: Scientific/Engineering :: Medical Science Apps.
|
|
6
16
|
Requires-Python: >=3.11
|
|
7
17
|
Requires-Dist: agl-report-reader>=0.4.0
|
|
8
18
|
Requires-Dist: django-bootstrap5>=24.3
|