Spaces:
Running
Running
progress tracking
Browse files- utils/user_progress.py +149 -0
utils/user_progress.py
ADDED
@@ -0,0 +1,149 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
"""
|
2 |
+
User Progress Management Utilities
|
3 |
+
Handles tracking and resuming review progress for Phase 2 reviewers
|
4 |
+
"""
|
5 |
+
|
6 |
+
from datetime import datetime
|
7 |
+
from sqlalchemy.orm import Session
|
8 |
+
from sqlalchemy import func, and_
|
9 |
+
|
10 |
+
from data.models import UserProgress, Annotation, Annotator, Validation
|
11 |
+
from utils.logger import get_logger
|
12 |
+
|
13 |
+
log = get_logger(__name__)
|
14 |
+
|
15 |
+
|
16 |
+
def get_user_progress(db: Session, user_id: int, target_annotator_id: int) -> UserProgress:
|
17 |
+
"""Get or create a progress record for the user reviewing the target annotator"""
|
18 |
+
progress = db.query(UserProgress).filter(
|
19 |
+
and_(
|
20 |
+
UserProgress.user_id == user_id,
|
21 |
+
UserProgress.target_annotator_id == target_annotator_id
|
22 |
+
)
|
23 |
+
).first()
|
24 |
+
|
25 |
+
if not progress:
|
26 |
+
# Create new progress record
|
27 |
+
progress = UserProgress(
|
28 |
+
user_id=user_id,
|
29 |
+
target_annotator_id=target_annotator_id,
|
30 |
+
last_reviewed_annotation_id=None,
|
31 |
+
last_position=0,
|
32 |
+
updated_at=datetime.now()
|
33 |
+
)
|
34 |
+
db.add(progress)
|
35 |
+
db.commit()
|
36 |
+
log.info(f"Created new progress record for user {user_id} reviewing annotator {target_annotator_id}")
|
37 |
+
|
38 |
+
return progress
|
39 |
+
|
40 |
+
|
41 |
+
def update_user_progress(db: Session, user_id: int, target_annotator_id: int, annotation_id: int, position: int):
|
42 |
+
"""Update user progress after they review an annotation"""
|
43 |
+
progress = get_user_progress(db, user_id, target_annotator_id)
|
44 |
+
|
45 |
+
progress.last_reviewed_annotation_id = annotation_id
|
46 |
+
progress.last_position = position
|
47 |
+
progress.updated_at = datetime.now()
|
48 |
+
|
49 |
+
db.commit()
|
50 |
+
log.info(f"Updated progress for user {user_id}: annotation {annotation_id} at position {position}")
|
51 |
+
|
52 |
+
|
53 |
+
def get_next_unreviewed_annotation(db: Session, user_id: int, target_annotator_id: int):
|
54 |
+
"""Find the next annotation that needs to be reviewed by this user"""
|
55 |
+
|
56 |
+
# Get all annotations for the target annotator that haven't been reviewed by this user
|
57 |
+
unreviewed_query = db.query(Annotation).filter(
|
58 |
+
Annotation.annotator_id == target_annotator_id
|
59 |
+
).outerjoin(
|
60 |
+
Validation,
|
61 |
+
and_(
|
62 |
+
Validation.annotation_id == Annotation.id,
|
63 |
+
Validation.validator_id == user_id
|
64 |
+
)
|
65 |
+
).filter(
|
66 |
+
Validation.id.is_(None) # No validation record means not reviewed
|
67 |
+
).order_by(Annotation.id)
|
68 |
+
|
69 |
+
first_unreviewed = unreviewed_query.first()
|
70 |
+
|
71 |
+
if first_unreviewed:
|
72 |
+
# Calculate the position of this annotation in the full list
|
73 |
+
position = db.query(Annotation).filter(
|
74 |
+
and_(
|
75 |
+
Annotation.annotator_id == target_annotator_id,
|
76 |
+
Annotation.id <= first_unreviewed.id
|
77 |
+
)
|
78 |
+
).count() - 1 # Convert to 0-based index
|
79 |
+
|
80 |
+
return first_unreviewed.id, position
|
81 |
+
else:
|
82 |
+
# All annotations have been reviewed, return the last position
|
83 |
+
total_count = db.query(Annotation).filter(
|
84 |
+
Annotation.annotator_id == target_annotator_id
|
85 |
+
).count()
|
86 |
+
|
87 |
+
return None, max(0, total_count - 1)
|
88 |
+
|
89 |
+
|
90 |
+
def get_last_reviewed_position(db: Session, user_id: int, target_annotator_id: int) -> int:
|
91 |
+
"""Get the last position this user reviewed"""
|
92 |
+
progress = get_user_progress(db, user_id, target_annotator_id)
|
93 |
+
return progress.last_position
|
94 |
+
|
95 |
+
|
96 |
+
def get_annotations_from_position(db: Session, target_annotator_id: int, start_position: int, batch_size: int = 10):
|
97 |
+
"""Load annotations starting from a specific position"""
|
98 |
+
|
99 |
+
annotations_query = db.query(
|
100 |
+
Annotation
|
101 |
+
).join(
|
102 |
+
Annotation.tts_data
|
103 |
+
).filter(
|
104 |
+
Annotation.annotator_id == target_annotator_id
|
105 |
+
).order_by(Annotation.id).offset(start_position).limit(batch_size)
|
106 |
+
|
107 |
+
return annotations_query.all()
|
108 |
+
|
109 |
+
|
110 |
+
def get_review_summary(db: Session, user_id: int, target_annotator_id: int):
|
111 |
+
"""Get a summary of review progress"""
|
112 |
+
|
113 |
+
# Total annotations for target annotator
|
114 |
+
total_count = db.query(Annotation).filter(
|
115 |
+
Annotation.annotator_id == target_annotator_id
|
116 |
+
).count()
|
117 |
+
|
118 |
+
# Reviewed count by this user
|
119 |
+
reviewed_count = db.query(Annotation).join(
|
120 |
+
Validation, Annotation.id == Validation.annotation_id
|
121 |
+
).filter(
|
122 |
+
and_(
|
123 |
+
Annotation.annotator_id == target_annotator_id,
|
124 |
+
Validation.validator_id == user_id
|
125 |
+
)
|
126 |
+
).count()
|
127 |
+
|
128 |
+
# Approved count
|
129 |
+
approved_count = db.query(Annotation).join(
|
130 |
+
Validation, Annotation.id == Validation.annotation_id
|
131 |
+
).filter(
|
132 |
+
and_(
|
133 |
+
Annotation.annotator_id == target_annotator_id,
|
134 |
+
Validation.validator_id == user_id,
|
135 |
+
Validation.validated == True
|
136 |
+
)
|
137 |
+
).count()
|
138 |
+
|
139 |
+
# Rejected count
|
140 |
+
rejected_count = reviewed_count - approved_count
|
141 |
+
|
142 |
+
return {
|
143 |
+
"total": total_count,
|
144 |
+
"reviewed": reviewed_count,
|
145 |
+
"approved": approved_count,
|
146 |
+
"rejected": rejected_count,
|
147 |
+
"remaining": total_count - reviewed_count,
|
148 |
+
"progress_percentage": (reviewed_count / total_count * 100) if total_count > 0 else 0
|
149 |
+
}
|