tts_labeling / utils /user_progress.py
vargha's picture
progress tracking
6cf928e
"""
User Progress Management Utilities
Handles tracking and resuming review progress for Phase 2 reviewers
"""
from datetime import datetime
from sqlalchemy.orm import Session
from sqlalchemy import func, and_
from data.models import UserProgress, Annotation, Annotator, Validation
from utils.logger import get_logger
log = get_logger(__name__)
def get_user_progress(db: Session, user_id: int, target_annotator_id: int) -> UserProgress:
"""Get or create a progress record for the user reviewing the target annotator"""
progress = db.query(UserProgress).filter(
and_(
UserProgress.user_id == user_id,
UserProgress.target_annotator_id == target_annotator_id
)
).first()
if not progress:
# Create new progress record
progress = UserProgress(
user_id=user_id,
target_annotator_id=target_annotator_id,
last_reviewed_annotation_id=None,
last_position=0,
updated_at=datetime.now()
)
db.add(progress)
db.commit()
log.info(f"Created new progress record for user {user_id} reviewing annotator {target_annotator_id}")
return progress
def update_user_progress(db: Session, user_id: int, target_annotator_id: int, annotation_id: int, position: int):
"""Update user progress after they review an annotation"""
progress = get_user_progress(db, user_id, target_annotator_id)
progress.last_reviewed_annotation_id = annotation_id
progress.last_position = position
progress.updated_at = datetime.now()
db.commit()
log.info(f"Updated progress for user {user_id}: annotation {annotation_id} at position {position}")
def get_next_unreviewed_annotation(db: Session, user_id: int, target_annotator_id: int):
"""Find the next annotation that needs to be reviewed by this user"""
# Get all annotations for the target annotator that haven't been reviewed by this user
unreviewed_query = db.query(Annotation).filter(
Annotation.annotator_id == target_annotator_id
).outerjoin(
Validation,
and_(
Validation.annotation_id == Annotation.id,
Validation.validator_id == user_id
)
).filter(
Validation.id.is_(None) # No validation record means not reviewed
).order_by(Annotation.id)
first_unreviewed = unreviewed_query.first()
if first_unreviewed:
# Calculate the position of this annotation in the full list
position = db.query(Annotation).filter(
and_(
Annotation.annotator_id == target_annotator_id,
Annotation.id <= first_unreviewed.id
)
).count() - 1 # Convert to 0-based index
return first_unreviewed.id, position
else:
# All annotations have been reviewed, return the last position
total_count = db.query(Annotation).filter(
Annotation.annotator_id == target_annotator_id
).count()
return None, max(0, total_count - 1)
def get_last_reviewed_position(db: Session, user_id: int, target_annotator_id: int) -> int:
"""Get the last position this user reviewed"""
progress = get_user_progress(db, user_id, target_annotator_id)
return progress.last_position
def get_annotations_from_position(db: Session, target_annotator_id: int, start_position: int, batch_size: int = 10):
"""Load annotations starting from a specific position"""
annotations_query = db.query(
Annotation
).join(
Annotation.tts_data
).filter(
Annotation.annotator_id == target_annotator_id
).order_by(Annotation.id).offset(start_position).limit(batch_size)
return annotations_query.all()
def get_review_summary(db: Session, user_id: int, target_annotator_id: int):
"""Get a summary of review progress"""
# Total annotations for target annotator
total_count = db.query(Annotation).filter(
Annotation.annotator_id == target_annotator_id
).count()
# Reviewed count by this user
reviewed_count = db.query(Annotation).join(
Validation, Annotation.id == Validation.annotation_id
).filter(
and_(
Annotation.annotator_id == target_annotator_id,
Validation.validator_id == user_id
)
).count()
# Approved count
approved_count = db.query(Annotation).join(
Validation, Annotation.id == Validation.annotation_id
).filter(
and_(
Annotation.annotator_id == target_annotator_id,
Validation.validator_id == user_id,
Validation.validated == True
)
).count()
# Rejected count
rejected_count = reviewed_count - approved_count
return {
"total": total_count,
"reviewed": reviewed_count,
"approved": approved_count,
"rejected": rejected_count,
"remaining": total_count - reviewed_count,
"progress_percentage": (reviewed_count / total_count * 100) if total_count > 0 else 0
}