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.

Files changed (28) hide show
  1. endoreg_db/data/distribution/numeric/data.yaml +1 -1
  2. endoreg_db/mermaid/Overall_flow_patient_finding_intervention.md +10 -0
  3. endoreg_db/mermaid/anonymized_image_annotation.md +20 -0
  4. endoreg_db/mermaid/binary_classification_annotation.md +50 -0
  5. endoreg_db/mermaid/classification.md +8 -0
  6. endoreg_db/mermaid/examination.md +8 -0
  7. endoreg_db/mermaid/findings.md +7 -0
  8. endoreg_db/mermaid/image_classification.md +28 -0
  9. endoreg_db/mermaid/interventions.md +8 -0
  10. endoreg_db/mermaid/morphology.md +8 -0
  11. endoreg_db/mermaid/patient_creation.md +14 -0
  12. endoreg_db/mermaid/video_segmentation_annotation.md +17 -0
  13. endoreg_db/models/ai_model/ai_model.py +1 -1
  14. endoreg_db/models/ai_model/model_meta.py +1 -1
  15. endoreg_db/models/data_file/base_classes/utils.py +1 -1
  16. endoreg_db/serializers/raw_pdf_anony_text_validation.py +137 -0
  17. endoreg_db/serializers/raw_pdf_meta_validation.py +223 -0
  18. endoreg_db/serializers/raw_video_meta_validation.py +163 -1
  19. endoreg_db/serializers/video_segmentation.py +207 -112
  20. endoreg_db/urls.py +110 -11
  21. endoreg_db/views/raw_pdf_anony_text_validation_views.py +95 -0
  22. endoreg_db/views/raw_pdf_meta_validation_views.py +111 -0
  23. endoreg_db/views/raw_video_meta_validation_views.py +128 -18
  24. endoreg_db/views/video_segmentation_views.py +28 -11
  25. {endoreg_db-0.6.1.dist-info → endoreg_db-0.6.3.dist-info}/METADATA +11 -1
  26. {endoreg_db-0.6.1.dist-info → endoreg_db-0.6.3.dist-info}/RECORD +28 -13
  27. {endoreg_db-0.6.1.dist-info → endoreg_db-0.6.3.dist-info}/WHEEL +0 -0
  28. {endoreg_db-0.6.1.dist-info → endoreg_db-0.6.3.dist-info}/licenses/LICENSE +0 -0
@@ -1,22 +1,19 @@
1
1
  from pathlib import Path
2
2
  from rest_framework import serializers
3
- from django.http import FileResponse, Http404,StreamingHttpResponse
4
- from ..models import RawVideoFile,Label
3
+ from django.http import FileResponse, Http404, StreamingHttpResponse
4
+ from ..models import RawVideoFile, Label, LabelRawVideoSegment, RawVideoPredictionMeta
5
5
  import subprocess, cv2
6
6
  from django.conf import settings
7
7
 
8
8
 
9
-
10
9
  class VideoFileSerializer(serializers.ModelSerializer):
11
-
12
-
13
10
  """
14
11
  Serializer that dynamically handles video retrieval and streaming.
15
12
  Ensures file returns the relative file path (not MEDIA_URL)
16
13
  Computes full_video_path using the correct storage path (/home/admin/test-data)-need to change make it dynamic
17
14
  Returns video_url for frontend integration
18
15
  Serves the video file when needed
19
-
16
+
20
17
  """
21
18
 
22
19
  video_url = serializers.SerializerMethodField()
@@ -24,53 +21,67 @@ class VideoFileSerializer(serializers.ModelSerializer):
24
21
  file = serializers.SerializerMethodField() # Override file to remove incorrect MEDIA_URL behavior,otherwise:Django's FileField automatically generates a URL based on MEDIA_URL
25
22
  # Video dropdown field for frontend selection (currently shows video ID, but can be changed later)
26
23
  video_selection_field = serializers.SerializerMethodField()
27
- #classification_data = serializers.SerializerMethodField() #data from database (smooth prediction values but currently hardcoded one)
28
- #The Meta class tells Django what data to include when serializing a RawVideoFile object.
24
+ # classification_data = serializers.SerializerMethodField() #data from database (smooth prediction values but currently hardcoded one)
25
+ # The Meta class tells Django what data to include when serializing a RawVideoFile object.
29
26
  sequences = serializers.SerializerMethodField()
30
27
  label_names = serializers.SerializerMethodField()
31
- # Convert selected label frames into time segments (seconds)
28
+ # Convert selected label frames into time segments (seconds)
32
29
  label_time_segments = serializers.SerializerMethodField()
33
- #label_predictions = serializers.SerializerMethodField()
30
+ # label_predictions = serializers.SerializerMethodField()
34
31
  original_file_name = serializers.CharField()
35
32
  duration = serializers.SerializerMethodField()
36
33
 
37
-
38
-
39
-
40
-
41
34
  class Meta:
42
35
  model = RawVideoFile
43
- #he fields list defines which data should be included in the API response.
44
- fields = ['id','original_file_name', 'file','duration', 'video_url', 'full_video_path','video_selection_field','label_names','sequences','label_time_segments'] # Ensure computed fields are included
45
- #@staticmethod #using @staticmethod makes it reusable without needing to create a serializer instance.
36
+ # he fields list defines which data should be included in the API response.
37
+ fields = [
38
+ "id",
39
+ "original_file_name",
40
+ "file",
41
+ "duration",
42
+ "video_url",
43
+ "full_video_path",
44
+ "video_selection_field",
45
+ "label_names",
46
+ "sequences",
47
+ "label_time_segments",
48
+ ] # Ensure computed fields are included
49
+
50
+ # @staticmethod #using @staticmethod makes it reusable without needing to create a serializer instance.
46
51
  # Without @staticmethod, you would need to instantiate the serializer before calling the method, which is unnecessary her
47
- def get_video_selection_field(self,obj):
52
+ def get_video_selection_field(self, obj):
48
53
  """
49
54
  Returns the field used for video selection in the frontend dropdown.
50
55
  Currently, it shows the video ID, but this can be changed easily later.
51
56
  """
52
57
  return obj.id
53
-
54
- def get_video_url(self, obj): # when we serialize a RawVideoFile object (video metadata), the get_video_url method is automatically invoked by DRF
58
+
59
+ def get_video_url(
60
+ self, obj
61
+ ): # when we serialize a RawVideoFile object (video metadata), the get_video_url method is automatically invoked by DRF
55
62
  """
56
63
  Returns the API endpoint where the frontend can fetch the video.
57
64
  """
58
65
  if not obj.id:
59
66
  return {"error": "Invalid video ID"}
60
-
61
- request= self.context.get('request') #Gets the request object (provided by DRF).
67
+
68
+ request = self.context.get(
69
+ "request"
70
+ ) # Gets the request object (provided by DRF).
62
71
  if request:
63
72
  return request.build_absolute_uri(f"/api/video/{obj.id}/")
64
-
73
+
65
74
  return {"error": "Video URL not avalaible"}
66
-
75
+
67
76
  def get_duration(self, obj):
68
77
  """
69
78
  Returns the total duration of the video in seconds.
70
79
  If duration is not stored in the database, it extracts it dynamically using OpenCV.
71
80
  """
72
81
  if hasattr(obj, "duration") and obj.duration:
73
- return obj.duration # If duration is stored in the database, return it directly.
82
+ return (
83
+ obj.duration
84
+ ) # If duration is stored in the database, return it directly.
74
85
 
75
86
  # Dynamically extract duration if not stored
76
87
  video_path = obj.file.path
@@ -83,8 +94,9 @@ class VideoFileSerializer(serializers.ModelSerializer):
83
94
  total_frames = cap.get(cv2.CAP_PROP_FRAME_COUNT)
84
95
  cap.release()
85
96
 
86
- return round(total_frames / fps, 2) if fps > 0 else None # Return duration in seconds
87
-
97
+ return (
98
+ round(total_frames / fps, 2) if fps > 0 else None
99
+ ) # Return duration in seconds
88
100
 
89
101
  def get_file(self, obj):
90
102
  """
@@ -92,15 +104,16 @@ class VideoFileSerializer(serializers.ModelSerializer):
92
104
  """
93
105
  if not obj.file:
94
106
  return {"error": "No file associated with this entry"}
95
- #obj.file.name is an attribute of FieldFile that returns the file path as a string and name is not the database attribute, it is an attribute of Django’s FieldFile object that holds the file path as a string.
96
- if not hasattr(obj.file,'name') or not obj.file.name.strip():
107
+ # obj.file.name is an attribute of FieldFile that returns the file path as a string and name is not the database attribute, it is an attribute of Django’s FieldFile object that holds the file path as a string.
108
+ if not hasattr(obj.file, "name") or not obj.file.name.strip():
97
109
  return {"error": "Invalid file name"}
98
110
 
99
- return str(obj.file.name).strip() # Only return the file path, no URL,#obj.file returning a FieldFile object instead of a string
100
-
111
+ return str(
112
+ obj.file.name
113
+ ).strip() # Only return the file path, no URL,#obj.file returning a FieldFile object instead of a string
101
114
 
102
- '''The error "muxer does not support non-seekable output"
103
- happens because MP4 format requires seeking, but FFmpeg does not support writing MP4 directly to a non-seekable stream (like STDOUT).'''
115
+ """The error "muxer does not support non-seekable output"
116
+ happens because MP4 format requires seeking, but FFmpeg does not support writing MP4 directly to a non-seekable stream (like STDOUT)."""
104
117
 
105
118
  def get_full_video_path(self, obj):
106
119
  """
@@ -109,39 +122,30 @@ class VideoFileSerializer(serializers.ModelSerializer):
109
122
  """
110
123
  if not obj.file:
111
124
  return {"error": "No video file associated with this entry"}
112
-
125
+
113
126
  video_relative_path = str(obj.file.name).strip() # Convert FieldFile to string
114
127
  if not video_relative_path:
115
- return {"error":"Video file path is empty or invalid"} # none might cause, 500 error, Handle edge case where the file name is empty
116
-
128
+ return {
129
+ "error": "Video file path is empty or invalid"
130
+ } # none might cause, 500 error, Handle edge case where the file name is empty
131
+
117
132
  print("-----------------------------------------")
118
- # pseudo_dir = settings.PSEUDO_DIR
119
- #print(f"Using pseudo directory: {pseudo_dir}")
133
+ # pseudo_dir = settings.PSEUDO_DIR
134
+ # print(f"Using pseudo directory: {pseudo_dir}")
120
135
 
121
136
  # full path using the actual storage directory~
122
- #actual_storage_dir = Path("~/test-data") # need to change
137
+ # actual_storage_dir = Path("~/test-data") # need to change
123
138
  actual_storage_dir = Path("/home/admin/test-data") # need to change
124
- #actual_storage_dir = pseudo_dir
139
+ # actual_storage_dir = pseudo_dir
125
140
  full_path = actual_storage_dir / video_relative_path
126
- #full_path = Path("/home/admin/test-data/video/lux-gastro-video.mp4")
127
-
128
-
129
- return str(full_path) if full_path.exists() else {"error":f"file not found at: {full_path}"}
130
-
131
- '''
132
- ffmpeg_command = [
133
- "ffmpeg", "-i", str(full_path), # Input video
134
- "-vf", "drawtext=text='OUTSIDE':fontcolor=white:fontsize=24:x=(w-text_w)/2:y=30:enable='lt(t,10)'",
135
- "-c:v", "libx264", "-preset", "ultrafast", # Encode quickly for streaming
136
- "-f", "mp4", "-" # Output to STDOUT (no file saving)
137
- ]'''
138
- '''ffmpeg_command = [
139
- "ffmpeg", "-i", str(full_path), # Input video
140
- "-vf", "drawtext=text='OUTSIDE':fontcolor=white:fontsize=24:x=(w-text_w)/2:y=30:enable='lt(t,10)'",
141
- "-c:v", "libx264", "-preset", "ultrafast",
142
- "-f", "mpegts", "pipe:1" # Output as MPEG-TS to STDOUT
143
- ]'''
144
-
141
+ # full_path = Path("/home/admin/test-data/video/lux-gastro-video.mp4")
142
+
143
+ return (
144
+ str(full_path)
145
+ if full_path.exists()
146
+ else {"error": f"file not found at: {full_path}"}
147
+ )
148
+
145
149
  def get_sequences(self, obj):
146
150
  """
147
151
  Extracts the sequences field from the RawVideoFile model.
@@ -152,8 +156,10 @@ class VideoFileSerializer(serializers.ModelSerializer):
152
156
  "kolonpolyp": [[91, 126]]
153
157
  }
154
158
  """
155
- return obj.sequences or {"error":"no sequence found, check database first"} # Get from sequences, return {} if missing
156
-
159
+ return obj.sequences or {
160
+ "error": "no sequence found, check database first"
161
+ } # Get from sequences, return {} if missing
162
+
157
163
  def get_label_names(self, obj):
158
164
  """
159
165
  Extracts only label names from the sequences data.
@@ -162,7 +168,6 @@ class VideoFileSerializer(serializers.ModelSerializer):
162
168
  """
163
169
  sequences = self.get_sequences(obj)
164
170
  return list(sequences.keys()) if sequences else []
165
-
166
171
 
167
172
  def get_label_time_segments(self, obj):
168
173
  """
@@ -176,9 +181,15 @@ class VideoFileSerializer(serializers.ModelSerializer):
176
181
  - segment_start and segment_end (in frame index format, not divided by FPS)
177
182
  """
178
183
 
179
- fps = obj.fps if hasattr(obj, "fps") and obj.fps is not None else obj.get_fps() if hasattr(obj, "get_fps") and obj.get_fps() is not None else 50
184
+ fps = (
185
+ obj.fps
186
+ if hasattr(obj, "fps") and obj.fps is not None
187
+ else obj.get_fps()
188
+ if hasattr(obj, "get_fps") and obj.get_fps() is not None
189
+ else 50
190
+ )
180
191
 
181
- print("here is fps::::::::::::::::::.-----------::::::",fps)
192
+ print("here is fps::::::::::::::::::.-----------::::::", fps)
182
193
  sequences = self.get_sequences(obj) # Fetch sequence data
183
194
  readable_predictions = obj.readable_predictions # Predictions from DB
184
195
 
@@ -205,51 +216,64 @@ class VideoFileSerializer(serializers.ModelSerializer):
205
216
 
206
217
  # Fetch predictions for frames within this range
207
218
  for frame_num in range(start_frame, end_frame + 1):
208
- if 0 <= frame_num < len(readable_predictions): # Ensure index is valid
219
+ if (
220
+ 0 <= frame_num < len(readable_predictions)
221
+ ): # Ensure index is valid
209
222
  frame_filename = f"frame_{str(frame_num).zfill(7)}.jpg" # Frame filename format
210
- frame_path = frame_dir / frame_filename # Full path to the frame
223
+ frame_path = (
224
+ frame_dir / frame_filename
225
+ ) # Full path to the frame
211
226
 
212
227
  frame_data[frame_num] = {
213
228
  "frame_filename": frame_filename,
214
229
  "frame_file_path": str(frame_path),
215
- "predictions": readable_predictions[frame_num]
230
+ "predictions": readable_predictions[frame_num],
216
231
  }
217
232
 
218
233
  # Store frame-wise predictions in frame_predictions
219
234
  frame_predictions[frame_num] = readable_predictions[frame_num]
220
235
 
221
236
  # Append the converted time segment
222
- label_times.append({
223
- "segment_start": start_frame, # Raw start frame (not divided by FPS)
224
- "segment_end": end_frame, # Raw end frame (not divided by FPS)
225
- "start_time": round(start_time, 2), # Converted start time in seconds
226
- "end_time": round(end_time, 2), # Converted end time in seconds
227
- "frames": frame_data # Attach frame details
228
- })
237
+ label_times.append(
238
+ {
239
+ "segment_start": start_frame, # Raw start frame (not divided by FPS)
240
+ "segment_end": end_frame, # Raw end frame (not divided by FPS)
241
+ "start_time": round(
242
+ start_time, 2
243
+ ), # Converted start time in seconds
244
+ "end_time": round(end_time, 2), # Converted end time in seconds
245
+ "frames": frame_data, # Attach frame details
246
+ }
247
+ )
229
248
 
230
249
  # Store time segments and frame_predictions under the label
231
250
  time_segments[label] = {
232
251
  "time_ranges": label_times,
233
- "frame_predictions": frame_predictions # Ensure frame_predictions is correctly assigned
252
+ "frame_predictions": frame_predictions, # Ensure frame_predictions is correctly assigned
234
253
  }
235
254
 
236
255
  return time_segments
237
256
 
257
+
238
258
  class VideoListSerializer(serializers.ModelSerializer):
239
259
  """
240
260
  Minimal serializer to return only `id` and `original_file_name`
241
261
  for the video selection dropdown in Vue.js.
242
262
  """
263
+
243
264
  class Meta:
244
265
  model = RawVideoFile
245
- fields = ['id', 'original_file_name'] # Only fetch required fields
246
-
266
+ fields = ["id", "original_file_name"] # Only fetch required fields
247
267
 
248
268
 
249
269
  from pathlib import Path
250
270
  from rest_framework import serializers
251
271
  from django.http import FileResponse, Http404, StreamingHttpResponse
252
- from ..models import RawVideoFile, Label, LabelRawVideoSegment # Importing necessary models
272
+ from ..models import (
273
+ RawVideoFile,
274
+ Label,
275
+ LabelRawVideoSegment,
276
+ ) # Importing necessary models
253
277
  import subprocess
254
278
  from django.conf import settings
255
279
  from django.db.models import Q # Import Q for better querying
@@ -260,24 +284,33 @@ class LabelSerializer(serializers.ModelSerializer):
260
284
  Serializer for fetching labels from the `endoreg_db_label` table.
261
285
  Includes `id` (for backend processing) and `name` (for dropdown display in Vue.js).
262
286
  """
287
+
263
288
  class Meta:
264
289
  model = Label
265
- fields = ['id', 'name']
290
+ fields = ["id", "name"]
266
291
 
267
292
 
268
293
  class LabelSegmentSerializer(serializers.ModelSerializer):
269
294
  """
270
295
  Serializer for retrieving label segments from `endoreg_db_labelrawvideosegment`.
271
296
  """
297
+
272
298
  class Meta:
273
299
  model = LabelRawVideoSegment
274
- fields = ['id', 'video_id', 'label_id', 'start_frame_number', 'end_frame_number']
300
+ fields = [
301
+ "id",
302
+ "video_id",
303
+ "label_id",
304
+ "start_frame_number",
305
+ "end_frame_number",
306
+ ]
275
307
 
276
308
 
277
309
  from django.db import transaction
278
310
 
279
311
  from django.db import transaction
280
312
 
313
+
281
314
  class LabelSegmentUpdateSerializer(serializers.Serializer):
282
315
  """
283
316
  Serializer for updating label segments.
@@ -308,10 +341,14 @@ class LabelSegmentUpdateSerializer(serializers.Serializer):
308
341
 
309
342
  for segment in data["segments"]:
310
343
  if "start_frame_number" not in segment or "end_frame_number" not in segment:
311
- raise serializers.ValidationError("Each segment must have `start_frame_number` and `end_frame_number`.")
344
+ raise serializers.ValidationError(
345
+ "Each segment must have `start_frame_number` and `end_frame_number`."
346
+ )
312
347
 
313
348
  if segment["start_frame_number"] > segment["end_frame_number"]:
314
- raise serializers.ValidationError("Start frame must be less than or equal to end frame.")
349
+ raise serializers.ValidationError(
350
+ "Start frame must be less than or equal to end frame."
351
+ )
315
352
 
316
353
  return data
317
354
 
@@ -331,20 +368,58 @@ class LabelSegmentUpdateSerializer(serializers.Serializer):
331
368
  label_id = self.validated_data["label_id"]
332
369
  new_segments = self.validated_data["segments"]
333
370
 
334
- # Fetch all existing segments for this video and label from the database
335
- existing_segments = LabelRawVideoSegment.objects.filter(video_id=video_id, label_id=label_id)
371
+ # Fetch the correct `prediction_meta_id` based on `video_id`
372
+ prediction_meta_entry = RawVideoPredictionMeta.objects.filter(
373
+ video_id=video_id
374
+ ).first()
375
+ if not prediction_meta_entry:
376
+ raise serializers.ValidationError(
377
+ {"error": "No prediction metadata found for this video."}
378
+ )
379
+
380
+ prediction_meta_id = (
381
+ prediction_meta_entry.id
382
+ ) # Get the correct prediction_meta_id
383
+
384
+ existing_segments = LabelRawVideoSegment.objects.filter(
385
+ video_id=video_id, label_id=label_id
386
+ )
336
387
 
337
388
  # Convert existing segments into a dictionary for quick lookup
338
389
  # Key format: (start_frame_number, end_frame_number)
339
- existing_segments_dict = {(float(seg.start_frame_number), float(seg.end_frame_number)): seg for seg in existing_segments}
390
+ existing_segments_dict = {
391
+ (float(seg.start_frame_number), float(seg.end_frame_number)): seg
392
+ for seg in existing_segments
393
+ }
394
+ existing_segments_dict = {
395
+ (float(seg.start_frame_number), float(seg.end_frame_number)): seg
396
+ for seg in existing_segments
397
+ }
340
398
 
341
399
  # Prepare lists for batch processing
342
400
  updated_segments = [] # Stores segments that need to be updated
343
401
  new_entries = [] # Stores segments that need to be created
344
- existing_keys = set(existing_segments_dict.keys()) # Existing database segment keys
345
- new_keys = set((float(seg["start_frame_number"]), float(seg["end_frame_number"])) for seg in new_segments) # New frontend segment keys
402
+ existing_keys = set(
403
+ existing_segments_dict.keys()
404
+ ) # Existing database segment keys
405
+ new_keys = set(
406
+ (float(seg["start_frame_number"]), float(seg["end_frame_number"]))
407
+ for seg in new_segments
408
+ ) # New frontend segment keys
346
409
 
347
410
  # Start a transaction to ensure database consistency
411
+ updated_segments = []
412
+ new_entries = []
413
+ existing_keys = set(existing_segments_dict.keys())
414
+ new_keys = set(
415
+ (float(seg["start_frame_number"]), float(seg["end_frame_number"]))
416
+ for seg in new_segments
417
+ )
418
+
419
+ print(f" Before Update: Found {existing_segments.count()} existing segments.")
420
+ print(f" New Segments Received: {len(new_segments)}")
421
+ print(f" Using prediction_meta_id: {prediction_meta_id}")
422
+
348
423
  with transaction.atomic():
349
424
  for segment in new_segments:
350
425
  start_frame = float(segment["start_frame_number"])
@@ -358,7 +433,7 @@ class LabelSegmentUpdateSerializer(serializers.Serializer):
358
433
  existing_segment = LabelRawVideoSegment.objects.filter(
359
434
  video_id=video_id,
360
435
  label_id=label_id,
361
- start_frame_number=start_frame
436
+ start_frame_number=start_frame,
362
437
  ).first()
363
438
 
364
439
  if existing_segment:
@@ -369,16 +444,32 @@ class LabelSegmentUpdateSerializer(serializers.Serializer):
369
444
  updated_segments.append(existing_segment)
370
445
  else:
371
446
  # If no existing segment matches, create a new one
372
- new_entries.append(LabelRawVideoSegment(
373
- video_id=video_id,
374
- label_id=label_id,
375
- start_frame_number=start_frame,
376
- end_frame_number=end_frame
377
- ))
447
+ new_entries.append(
448
+ LabelRawVideoSegment(
449
+ video_id=video_id,
450
+ label_id=label_id,
451
+ start_frame_number=start_frame,
452
+ end_frame_number=end_frame,
453
+ )
454
+ )
455
+ print(
456
+ f" Adding new segment: Start {start_frame} → End {end_frame}"
457
+ )
458
+ new_entries.append(
459
+ LabelRawVideoSegment(
460
+ video_id=video_id,
461
+ label_id=label_id,
462
+ start_frame_number=start_frame,
463
+ end_frame_number=end_frame,
464
+ prediction_meta_id=prediction_meta_id, # Assign correct prediction_meta_id
465
+ )
466
+ )
378
467
 
379
468
  # Delete segments that are no longer present in the frontend data
380
469
  segments_to_delete = existing_segments.exclude(
381
- start_frame_number__in=[float(seg["start_frame_number"]) for seg in new_segments]
470
+ start_frame_number__in=[
471
+ float(seg["start_frame_number"]) for seg in new_segments
472
+ ]
382
473
  )
383
474
  deleted_count = segments_to_delete.count()
384
475
  segments_to_delete.delete()
@@ -388,24 +479,32 @@ class LabelSegmentUpdateSerializer(serializers.Serializer):
388
479
  LabelRawVideoSegment.objects.bulk_create(new_entries)
389
480
 
390
481
  # Return the updated, new, and deleted segment information
391
- print("------------------------------,",updated_segments,"-----------------------",new_segments,"_-------",deleted_count)
482
+ print(
483
+ "------------------------------,",
484
+ updated_segments,
485
+ "-----------------------",
486
+ new_segments,
487
+ "_-------",
488
+ deleted_count,
489
+ )
490
+ print(
491
+ f" After Update: Updated {len(updated_segments)} segments, Added {len(new_entries)}, Deleted {deleted_count}"
492
+ )
493
+
392
494
  return {
393
- "updated_segments": LabelSegmentSerializer(updated_segments, many=True).data,
495
+ "updated_segments": LabelSegmentSerializer(
496
+ updated_segments, many=True
497
+ ).data,
394
498
  "new_segments": LabelSegmentSerializer(new_entries, many=True).data,
395
- "deleted_segments": deleted_count
499
+ "deleted_segments": deleted_count,
396
500
  }
397
501
 
398
-
399
-
400
-
401
-
402
-
403
-
404
502
  # Use StreamingHttpResponse to stream FFmpeg output to browser
405
- #return StreamingHttpResponse(subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE).stdout, content_type="video/mp4")
503
+ # return StreamingHttpResponse(subprocess.Popen(ffmpeg_command, stdout=subprocess.PIPE).stdout, content_type="video/mp4")
504
+
505
+ # Ensure the path exists before returning
506
+ # return str(full_path) if full_path.exists() else None
406
507
 
407
- # Ensure the path exists before returning
408
- # return str(full_path) if full_path.exists() else None
409
508
 
410
509
  ''' def get_classification_data(self, obj):
411
510
  """
@@ -437,10 +536,6 @@ class LabelSegmentUpdateSerializer(serializers.Serializer):
437
536
  return classifications'''
438
537
 
439
538
 
440
-
441
-
442
-
443
-
444
539
  """
445
540
  await import('https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js');
446
541
  const videoId = 1;
@@ -489,4 +584,4 @@ axios.put(`http://localhost:8000/api/video/${videoIdUpdate}/label/${labelIdUpdat
489
584
  .catch(error => {
490
585
  console.error(" Error Updating Segments:", error.response ? error.response.data : error);
491
586
  });
492
- """''
587
+ """ ""