vargha commited on
Commit
5dc5cb8
Β·
1 Parent(s): 6cf928e

progress tracking

Browse files
Files changed (2) hide show
  1. components/review_dashboard_page.py +54 -69
  2. data/models.py +1 -20
components/review_dashboard_page.py CHANGED
@@ -12,11 +12,6 @@ from config import conf
12
  from utils.database import get_db
13
  from data.models import Annotation, TTSData, Annotator, Validation
14
  from data.repository.annotator_workload_repo import AnnotatorWorkloadRepo
15
- from utils.user_progress import (
16
- get_next_unreviewed_annotation,
17
- update_user_progress,
18
- get_annotations_from_position
19
- )
20
 
21
  log = Logger()
22
  LOADER = CloudServerAudioLoader(conf.FTP_URL)
@@ -307,7 +302,7 @@ class ReviewDashboardPage:
307
  log.warning(f"No target annotator found for reviewer {username}")
308
  return [], 0, "", "", "", "", "", "", "", "", gr.update(value=None, autoplay=False), gr.update(visible=False, value=""), False, gr.update(value="❌ Reject")
309
 
310
- # Load annotations with PROGRESS TRACKING
311
  with get_db() as db:
312
  # Get target annotator's ID
313
  target_annotator_obj = db.query(Annotator).filter_by(name=target_annotator).first()
@@ -317,28 +312,33 @@ class ReviewDashboardPage:
317
 
318
  log.info(f"Found target annotator with ID: {target_annotator_obj.id}")
319
 
320
- # 🎯 PROGRESS TRACKING: Find next unreviewed annotation position
321
- next_annotation_id, next_position = get_next_unreviewed_annotation(db, user_id, target_annotator_obj.id)
 
322
 
323
- # Load batch size for responsive loading
324
- INITIAL_BATCH_SIZE = 10 # Increased from 5 to 10 for better UX
325
-
326
- # Load annotations starting from the next unreviewed position
327
- annotations_data = get_annotations_from_position(db, target_annotator_obj.id, next_position, INITIAL_BATCH_SIZE)
 
 
 
 
 
 
 
328
 
329
  # Get total count for progress info (this is fast)
330
  total_count = db.query(Annotation).filter(
331
  Annotation.annotator_id == target_annotator_obj.id
332
  ).count()
333
 
334
- log.info(f"Progress-aware load: Starting from position {next_position}, loaded {len(annotations_data)} annotations out of {total_count} total for target annotator ID {target_annotator_obj.id}")
335
 
336
  # Process items with minimal data - validation status will be loaded on-demand
337
  items = []
338
- for annotation in annotations_data:
339
- # Get TTS data
340
- tts_data = annotation.tts_data
341
-
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
@@ -346,8 +346,8 @@ class ReviewDashboardPage:
346
  items.append({
347
  "annotation_id": annotation.id,
348
  "tts_id": annotation.tts_data_id,
349
- "filename": tts_data.filename,
350
- "sentence": tts_data.sentence,
351
  "annotated_sentence": annotated_sentence_display,
352
  "is_deleted": is_deleted,
353
  "annotated_at": annotation.annotated_at.isoformat() if annotation.annotated_at else "",
@@ -355,28 +355,36 @@ class ReviewDashboardPage:
355
  "validation_loaded": False # Track if validation status has been loaded
356
  })
357
 
358
- # 🎯 PROGRESS TRACKING: Start from first item (index 0) since we loaded from the correct position
359
  initial_idx = 0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
360
 
361
  # Set initial display
362
  if items:
363
  initial_item = items[initial_idx]
364
- review_info_text = f"πŸ” **Phase 2 Review Mode** - Continuing from position {next_position + 1}/{total_count}. Loaded {len(items)} items."
365
-
366
- # Load validation status for the first item immediately
367
- try:
368
- annotation_obj = db.query(Annotation).filter_by(id=initial_item["annotation_id"]).first()
369
- if annotation_obj:
370
- validation_status, is_deleted = get_validation_status_for_item(db, initial_item["annotation_id"], user_id, annotation_obj)
371
- initial_item["validation_status"] = validation_status
372
- initial_item["is_deleted"] = is_deleted
373
- initial_item["validation_loaded"] = True
374
-
375
- if is_deleted:
376
- initial_item["annotated_sentence"] = "[DELETED ANNOTATION]"
377
- except Exception as e:
378
- log.warning(f"Failed to load initial validation status: {e}")
379
-
380
  rejection_reason_val = ""
381
  rejection_visible_val = False
382
  if initial_item["validation_status"].startswith("Rejected"):
@@ -386,8 +394,6 @@ class ReviewDashboardPage:
386
  rejection_reason_val = initial_item["validation_status"][start_paren+1:end_paren]
387
  rejection_visible_val = True
388
 
389
- log.info(f"🎯 User {username} resuming review from position {next_position}, annotation ID {initial_item['annotation_id']}")
390
-
391
  return (
392
  items,
393
  initial_idx,
@@ -405,9 +411,15 @@ class ReviewDashboardPage:
405
  gr.update(value="❌ Reject") # Reset reject button
406
  )
407
  else:
408
- # All items have been reviewed
409
- review_info_text = f"πŸŽ‰ **Review Complete!** - All {total_count} annotations have been reviewed for {target_annotator}."
410
- return [], 0, review_info_text, "", "", "", "", "", "", "", gr.update(value=None, autoplay=False), gr.update(visible=False, value=""), False, gr.update(value="❌ Reject")
 
 
 
 
 
 
411
 
412
  def show_current_review_item_fn(items, idx, session):
413
  if not items or idx >= len(items) or idx < 0:
@@ -537,33 +549,6 @@ class ReviewDashboardPage:
537
  db.commit()
538
  log.info(f"Validation saved successfully for annotation_id: {annotation_id}")
539
 
540
- # 🎯 UPDATE USER PROGRESS TRACKING
541
- try:
542
- username = session.get("username")
543
- if username:
544
- # Find target annotator for this user
545
- target_annotator = None
546
- for annotator_name, reviewer_name in conf.REVIEW_MAPPING.items():
547
- if reviewer_name == username:
548
- target_annotator = annotator_name
549
- break
550
-
551
- if target_annotator:
552
- target_annotator_obj = db.query(Annotator).filter_by(name=target_annotator).first()
553
- if target_annotator_obj:
554
- # Calculate the current position in the review list
555
- current_position = db.query(Annotation).filter(
556
- Annotation.annotator_id == target_annotator_obj.id,
557
- Annotation.id <= annotation_id
558
- ).count() - 1 # Convert to 0-based index
559
-
560
- # Update user progress
561
- update_user_progress(db, user_id, target_annotator_obj.id, annotation_id, current_position)
562
- log.info(f"🎯 Updated progress for user {user_id}: annotation {annotation_id} at position {current_position}")
563
- except Exception as e:
564
- log.warning(f"Failed to update user progress: {e}")
565
- # Don't fail the validation save if progress tracking fails
566
-
567
  items[idx]["validation_status"] = "Approved" if approved else f"Rejected ({rejection_reason})" if rejection_reason else "Rejected"
568
 
569
  # Show rejection reason input only if rejected, otherwise hide and clear
 
12
  from utils.database import get_db
13
  from data.models import Annotation, TTSData, Annotator, Validation
14
  from data.repository.annotator_workload_repo import AnnotatorWorkloadRepo
 
 
 
 
 
15
 
16
  log = Logger()
17
  LOADER = CloudServerAudioLoader(conf.FTP_URL)
 
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
 
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
 
346
  items.append({
347
  "annotation_id": annotation.id,
348
  "tts_id": annotation.tts_data_id,
349
+ "filename": filename,
350
+ "sentence": sentence,
351
  "annotated_sentence": annotated_sentence_display,
352
  "is_deleted": is_deleted,
353
  "annotated_at": annotation.annotated_at.isoformat() if annotation.annotated_at else "",
 
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:
384
  initial_item = items[initial_idx]
385
+ review_info_text = f"πŸ” **Phase 2 Review Mode** - Reviewing assigned annotations. Loaded {len(items)} of {total_count} total items."
386
+ # Ensure correct order of return values for 12 outputs
387
+ # items, idx, review_info, tts_id, filename, sentence, ann_sentence, annotated_at, validation_status, annotator_placeholder, audio_update, rejection_reason_update
 
 
 
 
 
 
 
 
 
 
 
 
 
388
  rejection_reason_val = ""
389
  rejection_visible_val = False
390
  if initial_item["validation_status"].startswith("Rejected"):
 
394
  rejection_reason_val = initial_item["validation_status"][start_paren+1:end_paren]
395
  rejection_visible_val = True
396
 
 
 
397
  return (
398
  items,
399
  initial_idx,
 
411
  gr.update(value="❌ Reject") # Reset reject button
412
  )
413
  else:
414
+ # Ensure correct order and number of return values for empty items (14 outputs)
415
+ return [], 0, f"πŸ” **Phase 2 Review Mode** - No annotations found for review.", "", "", "", "", "", "", "", gr.update(value=None, autoplay=False), gr.update(visible=False, value=""), False, gr.update(value="❌ Reject")
416
+
417
+ # except Exception as e:
418
+ # log.error(f"Error loading review items: {e}")
419
+ # sentry_sdk.capture_exception(e)
420
+ # gr.Error(f"Failed to load review data: {e}")
421
+ # # Ensure correct order and number of return values for error case (14 outputs)
422
+ # return [], 0, "", "", "", "", "", "", "", "", gr.update(value=None, autoplay=False), gr.update(visible=False, value=""), False, gr.update(value="❌ Reject")
423
 
424
  def show_current_review_item_fn(items, idx, session):
425
  if not items or idx >= len(items) or idx < 0:
 
549
  db.commit()
550
  log.info(f"Validation saved successfully for annotation_id: {annotation_id}")
551
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
552
  items[idx]["validation_status"] = "Approved" if approved else f"Rejected ({rejection_reason})" if rejection_reason else "Rejected"
553
 
554
  # Show rejection reason input only if rejected, otherwise hide and clear
data/models.py CHANGED
@@ -158,23 +158,4 @@ class Validation(Base):
158
  validated_at = Column(DateTime, nullable=False)
159
 
160
  annotation = relationship("Annotation")
161
- validator = relationship("Annotator", foreign_keys=[validator_id]) # Fixed: should reference Annotator
162
-
163
-
164
- # --------------------------------------------------------------------------- #
165
- # UserProgress #
166
- # --------------------------------------------------------------------------- #
167
- class UserProgress(Base):
168
- __tablename__ = "user_progress"
169
-
170
- id = Column(Integer, primary_key=True)
171
- user_id = Column(Integer, ForeignKey("annotators.id"), nullable=False)
172
- target_annotator_id = Column(Integer, ForeignKey("annotators.id"), nullable=False)
173
- last_reviewed_annotation_id = Column(Integer, ForeignKey("annotations.id"), nullable=True)
174
- last_position = Column(Integer, default=0) # Position in the review list
175
- updated_at = Column(DateTime, nullable=False)
176
-
177
- # Relationships
178
- user = relationship("Annotator", foreign_keys=[user_id])
179
- target_annotator = relationship("Annotator", foreign_keys=[target_annotator_id])
180
- last_reviewed_annotation = relationship("Annotation", foreign_keys=[last_reviewed_annotation_id])
 
158
  validated_at = Column(DateTime, nullable=False)
159
 
160
  annotation = relationship("Annotation")
161
+ validator = relationship("Annotator", foreign_keys=[validator_id]) # Fixed: should reference Annotator