minor bugs

This commit is contained in:
2026-06-23 20:17:32 +05:30
parent c81e503af0
commit bf019f86c3
6 changed files with 133 additions and 60 deletions

View File

@@ -790,7 +790,7 @@ async fn stats(State(s): State<AppState>, user: AuthUser, AxPath(id): AxPath<i32
COALESCE((SELECT SUM(v2.annotation_time_ms) FROM videos v2
WHERE v2.project_id=$1 AND v2.primary_annotator = a.annotated_by), 0)::bigint
FROM annotations a JOIN videos v ON a.video_id=v.id
WHERE v.project_id=$1 AND a.annotated_by <> ''
WHERE v.project_id=$1 AND a.annotated_by <> '' AND lower(a.annotated_by) <> 'auto'
GROUP BY a.annotated_by"#,
)
.bind(id)
@@ -812,14 +812,17 @@ async fn stats(State(s): State<AppState>, user: AuthUser, AxPath(id): AxPath<i32
let ann_by_user: std::collections::HashMap<String, i64> =
leaderboard.iter().map(|r| (r.user.clone(), r.annotations)).collect();
// Verification leaderboard: ALL assigned users (members) ranked by completion
// SPEED (avg verify time per video, fastest first). Anyone who has completed
// videos but isn't a member is included too; members with no completions sort
// last (unranked). Refreshed every poll → "live" ranking.
// Verification leaderboard: ALL assigned users (members) ranked by ANNOTATION
// THROUGHPUT — annotations added per minute of work (most annotations in the least
// time first), not by video count. Anyone who has completed videos but isn't a
// member is included too; users with no annotations/time sort last (unranked).
// The synthetic 'auto' author (auto-segmented/imported) is excluded. Refreshed
// every poll → "live" ranking.
let ver_rows: Vec<(String, i64, i64)> = sqlx::query_as(
r#"SELECT completed_by, COUNT(*), COALESCE(SUM(verify_time_ms),0)::bigint
FROM videos
WHERE project_id=$1 AND status='verified' AND completed_by IS NOT NULL AND completed_by <> ''
WHERE project_id=$1 AND status='verified' AND completed_by IS NOT NULL
AND completed_by <> '' AND lower(completed_by) <> 'auto'
GROUP BY completed_by"#,
)
.bind(id)
@@ -834,29 +837,48 @@ async fn stats(State(s): State<AppState>, user: AuthUser, AxPath(id): AxPath<i32
let mut stats_by_user: std::collections::HashMap<String, (i64, i64)> =
ver_rows.into_iter().map(|(u, v, t)| (u, (v, t))).collect();
for m in &members {
if m.eq_ignore_ascii_case("auto") {
continue;
}
stats_by_user.entry(m.clone()).or_insert((0, 0));
}
let mut verifiers: Vec<VerifierRow> = stats_by_user
.into_iter()
.map(|(user, (videos, total_time_ms))| VerifierRow {
avg_time_ms: if videos > 0 { total_time_ms / videos } else { 0 },
videos,
total_time_ms,
annotations: ann_by_user.get(&user).copied().unwrap_or(0),
rank: 0,
user,
.map(|(user, (videos, total_time_ms))| {
let annotations = ann_by_user.get(&user).copied().unwrap_or(0);
// annotations added per minute of work; 0 when no time recorded yet.
let annotations_per_min = if total_time_ms > 0 {
annotations as f64 / (total_time_ms as f64 / 60_000.0)
} else {
0.0
};
VerifierRow {
avg_time_ms: if videos > 0 { total_time_ms / videos } else { 0 },
videos,
total_time_ms,
annotations,
annotations_per_min,
rank: 0,
user,
}
})
.collect();
// Fastest first: completers by avg ascending; non-completers last by name.
verifiers.sort_by(|a, b| match (a.videos > 0, b.videos > 0) {
// Highest throughput first (most annotations per minute). A user is rankable only
// once they have both annotations AND recorded time; everyone else sorts last by name.
let rankable = |v: &VerifierRow| v.annotations > 0 && v.annotations_per_min > 0.0;
verifiers.sort_by(|a, b| match (rankable(a), rankable(b)) {
(true, false) => std::cmp::Ordering::Less,
(false, true) => std::cmp::Ordering::Greater,
(true, true) => a.avg_time_ms.cmp(&b.avg_time_ms).then_with(|| a.user.cmp(&b.user)),
(true, true) => b
.annotations_per_min
.partial_cmp(&a.annotations_per_min)
.unwrap_or(std::cmp::Ordering::Equal)
.then_with(|| a.user.cmp(&b.user)),
(false, false) => a.user.cmp(&b.user),
});
let mut rank = 0i64;
for v in verifiers.iter_mut() {
if v.videos > 0 {
if rankable(v) {
rank += 1;
v.rank = rank;
}