vargha commited on
Commit
4d1a43b
·
1 Parent(s): 8dcb829

Resume Feature Implementation

Browse files
Files changed (1) hide show
  1. components/review_dashboard_page.py +95 -41
components/review_dashboard_page.py CHANGED
@@ -278,6 +278,42 @@ class ReviewDashboardPage:
278
  log.error(f"Error calculating review progress for user {user_id}: {e}")
279
  return f"⚠️ **Error calculating progress**"
280
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
281
  def load_review_items_fn(session):
282
  user_id = session.get("user_id")
283
  username = session.get("username")
@@ -302,7 +338,7 @@ class ReviewDashboardPage:
302
  log.warning(f"No target annotator found for reviewer {username}")
303
  return [], 0, "", "", "", "", "", "", "", "", gr.update(value=None, autoplay=False), gr.update(visible=False, value=""), False, gr.update(value="❌ Reject")
304
 
305
- # Load annotations from target annotator with FAST INITIAL LOADING
306
  with get_db() as db:
307
  # Get target annotator's ID
308
  target_annotator_obj = db.query(Annotator).filter_by(name=target_annotator).first()
@@ -312,33 +348,60 @@ class ReviewDashboardPage:
312
 
313
  log.info(f"Found target annotator with ID: {target_annotator_obj.id}")
314
 
315
- # FAST INITIAL QUERY: Load only essential data without complex validation processing
316
- # Reduced batch size for instant loading in HuggingFace spaces
317
- INITIAL_BATCH_SIZE = 5 # Load only 5 items initially for instant response
318
-
319
- # Simple query to get basic annotation data quickly
320
- initial_query = db.query(
321
- Annotation,
322
- TTSData.filename,
323
- TTSData.sentence
324
- ).join(
325
- TTSData, Annotation.tts_data_id == TTSData.id
326
- ).filter(
327
- Annotation.annotator_id == target_annotator_obj.id
328
- ).order_by(Annotation.id).limit(INITIAL_BATCH_SIZE)
329
-
330
- initial_results = initial_query.all()
331
 
332
- # Get total count for progress info (this is fast)
333
  total_count = db.query(Annotation).filter(
334
  Annotation.annotator_id == target_annotator_obj.id
335
  ).count()
336
 
337
- log.info(f"Fast initial load: {len(initial_results)} annotations out of {total_count} total for target annotator ID {target_annotator_obj.id}")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
338
 
339
  # Process items with minimal data - validation status will be loaded on-demand
340
  items = []
341
- for annotation, filename, sentence in initial_results:
342
  # Check if annotation is deleted (minimal processing)
343
  is_deleted = not annotation.annotated_sentence or annotation.annotated_sentence.strip() == ""
344
  annotated_sentence_display = "[DELETED ANNOTATION]" if is_deleted else annotation.annotated_sentence
@@ -355,29 +418,20 @@ class ReviewDashboardPage:
355
  "validation_loaded": False # Track if validation status has been loaded
356
  })
357
 
358
- # Find the first item that is not reviewed (prioritize non-deleted annotations)
359
  initial_idx = 0
360
  if items:
361
- found_unreviewed = False
362
- # First, try to find unreviewed non-deleted annotations
363
- for i, item_data in enumerate(items):
364
- if (item_data["validation_status"] == "Not Reviewed" and
365
- not item_data.get("is_deleted", False)):
366
- initial_idx = i
367
- found_unreviewed = True
368
- break
369
-
370
- # If no unreviewed non-deleted items, look for any unreviewed items
371
- if not found_unreviewed:
372
- for i, item_data in enumerate(items):
373
- if item_data["validation_status"].startswith("Not Reviewed"):
374
- initial_idx = i
375
- found_unreviewed = True
376
- break
377
-
378
- # If no unreviewed items at all, use the last item
379
- if not found_unreviewed:
380
- initial_idx = len(items) - 1 if items else 0
381
 
382
  # Set initial display
383
  if items:
 
278
  log.error(f"Error calculating review progress for user {user_id}: {e}")
279
  return f"⚠️ **Error calculating progress**"
280
 
281
+ def find_first_unreviewed_annotation_index(db, target_annotator_obj, reviewer_user_id):
282
+ """
283
+ Find the index (0-based) of the first annotation that hasn't been reviewed by the current reviewer.
284
+ Returns the global index within all annotations for the target annotator.
285
+ """
286
+ try:
287
+ # Query to find the first annotation that doesn't have a validation record from this reviewer
288
+ # We use LEFT JOIN to include annotations without validations
289
+ first_unreviewed = db.query(Annotation).outerjoin(
290
+ Validation,
291
+ (Annotation.id == Validation.annotation_id) &
292
+ (Validation.validator_id == reviewer_user_id)
293
+ ).filter(
294
+ Annotation.annotator_id == target_annotator_obj.id,
295
+ Validation.id.is_(None) # No validation record exists
296
+ ).order_by(Annotation.id).first()
297
+
298
+ if first_unreviewed:
299
+ # Find the index of this annotation among all annotations for the target annotator
300
+ all_annotations = db.query(Annotation).filter(
301
+ Annotation.annotator_id == target_annotator_obj.id
302
+ ).order_by(Annotation.id).all()
303
+
304
+ for idx, annotation in enumerate(all_annotations):
305
+ if annotation.id == first_unreviewed.id:
306
+ log.info(f"Found first unreviewed annotation at index {idx} (ID: {first_unreviewed.id})")
307
+ return idx
308
+
309
+ # If no unreviewed annotation found, return -1
310
+ log.info("No unreviewed annotations found")
311
+ return -1
312
+
313
+ except Exception as e:
314
+ log.error(f"Error finding first unreviewed annotation: {e}")
315
+ return -1
316
+
317
  def load_review_items_fn(session):
318
  user_id = session.get("user_id")
319
  username = session.get("username")
 
338
  log.warning(f"No target annotator found for reviewer {username}")
339
  return [], 0, "", "", "", "", "", "", "", "", gr.update(value=None, autoplay=False), gr.update(visible=False, value=""), False, gr.update(value="❌ Reject")
340
 
341
+ # Load annotations from target annotator with RESUME LOGIC
342
  with get_db() as db:
343
  # Get target annotator's ID
344
  target_annotator_obj = db.query(Annotator).filter_by(name=target_annotator).first()
 
348
 
349
  log.info(f"Found target annotator with ID: {target_annotator_obj.id}")
350
 
351
+ # --- RESUME LOGIC: Find first unreviewed annotation ---
352
+ resume_index = find_first_unreviewed_annotation_index(db, target_annotator_obj, user_id)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
353
 
354
+ # Get total count for progress info
355
  total_count = db.query(Annotation).filter(
356
  Annotation.annotator_id == target_annotator_obj.id
357
  ).count()
358
 
359
+ # Calculate how many items to load initially to include the resume index
360
+ INITIAL_BATCH_SIZE = 5 # Minimum batch size
361
+ if resume_index >= 0:
362
+ # Load enough items to include the resume index, but at least INITIAL_BATCH_SIZE
363
+ items_to_load = max(INITIAL_BATCH_SIZE, resume_index + 1)
364
+ # Cap it to avoid loading too many items at once
365
+ items_to_load = min(items_to_load, 20)
366
+ else:
367
+ # No unreviewed items found, load standard batch from the end
368
+ items_to_load = INITIAL_BATCH_SIZE
369
+
370
+ # Load items based on resume logic
371
+ if resume_index >= 0:
372
+ # Load from the beginning to include the resume index
373
+ query = db.query(
374
+ Annotation,
375
+ TTSData.filename,
376
+ TTSData.sentence
377
+ ).join(
378
+ TTSData, Annotation.tts_data_id == TTSData.id
379
+ ).filter(
380
+ Annotation.annotator_id == target_annotator_obj.id
381
+ ).order_by(Annotation.id).limit(items_to_load)
382
+ else:
383
+ # All items reviewed, load from the end
384
+ query = db.query(
385
+ Annotation,
386
+ TTSData.filename,
387
+ TTSData.sentence
388
+ ).join(
389
+ TTSData, Annotation.tts_data_id == TTSData.id
390
+ ).filter(
391
+ Annotation.annotator_id == target_annotator_obj.id
392
+ ).order_by(Annotation.id.desc()).limit(items_to_load)
393
+
394
+ results = query.all()
395
+
396
+ # If we loaded from the end (all reviewed), reverse the results to maintain chronological order
397
+ if resume_index < 0:
398
+ results = list(reversed(results))
399
+
400
+ log.info(f"Loaded {len(results)} annotations out of {total_count} total for target annotator ID {target_annotator_obj.id}")
401
 
402
  # Process items with minimal data - validation status will be loaded on-demand
403
  items = []
404
+ for annotation, filename, sentence in results:
405
  # Check if annotation is deleted (minimal processing)
406
  is_deleted = not annotation.annotated_sentence or annotation.annotated_sentence.strip() == ""
407
  annotated_sentence_display = "[DELETED ANNOTATION]" if is_deleted else annotation.annotated_sentence
 
418
  "validation_loaded": False # Track if validation status has been loaded
419
  })
420
 
421
+ # --- Calculate initial index based on resume logic ---
422
  initial_idx = 0
423
  if items:
424
+ if resume_index >= 0 and resume_index < len(items):
425
+ # Resume from the first unreviewed item
426
+ initial_idx = resume_index
427
+ log.info(f"User '{username}' resuming at first unreviewed item, index: {initial_idx} (annotation ID: {items[initial_idx]['annotation_id']})")
428
+ else:
429
+ # All items reviewed or no unreviewed items in current batch, start from the last item
430
+ initial_idx = len(items) - 1 if items else 0
431
+ if items:
432
+ log.info(f"User '{username}' has all loaded items reviewed, starting at last item index: {initial_idx} (annotation ID: {items[initial_idx]['annotation_id']})")
433
+ else:
434
+ log.info(f"User '{username}' has no items assigned, starting at index 0.")
 
 
 
 
 
 
 
 
 
435
 
436
  # Set initial display
437
  if items: