minor bugs
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user