feat: side panel shows per-juror baseline and balanced contribution
Extends the ranking router's roundEvaluationScores response with per-juror grading stats (mean, stddev, count) plus the round's overall mean/stddev. The side-sheet juror rows render 'typical X.XX → contributes Y.YY' next to each Score badge whenever balanced is on, making the z-rescaling visible per individual rather than only as a project-level number. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1136,6 +1136,28 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
|
|||||||
</Badge>
|
</Badge>
|
||||||
)}
|
)}
|
||||||
<Badge variant="outline">Score: {a.evaluation?.globalScore?.toFixed(1) ?? '—'}</Badge>
|
<Badge variant="outline">Score: {a.evaluation?.globalScore?.toFixed(1) ?? '—'}</Badge>
|
||||||
|
{useBalanced && (() => {
|
||||||
|
const userId = a.user?.id
|
||||||
|
const score = a.evaluation?.globalScore
|
||||||
|
if (!userId || score == null) return null
|
||||||
|
const stats = evalScores?.jurorStats?.[userId]
|
||||||
|
const overallMean = evalScores?.overallMean
|
||||||
|
const overallStddev = evalScores?.overallStddev
|
||||||
|
if (!stats || overallMean == null || overallStddev == null || overallStddev === 0) return null
|
||||||
|
const z = stats.stddev > 0
|
||||||
|
? (score - stats.mean) / stats.stddev
|
||||||
|
: (score - overallMean) / overallStddev
|
||||||
|
const contributesAs = overallMean + z * overallStddev
|
||||||
|
return (
|
||||||
|
<span
|
||||||
|
className="text-xs text-muted-foreground"
|
||||||
|
title={`Their typical score in this round; rescaled contribution after juror balancing`}
|
||||||
|
onClick={(e) => e.stopPropagation()}
|
||||||
|
>
|
||||||
|
typical {stats.mean.toFixed(2)} → contributes {contributesAs.toFixed(2)}
|
||||||
|
</span>
|
||||||
|
)
|
||||||
|
})()}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{isExpanded && a.evaluation?.feedbackText && (
|
{isExpanded && a.evaluation?.feedbackText && (
|
||||||
|
|||||||
@@ -537,6 +537,19 @@ export const rankingRouter = router({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return { byProject, balanced }
|
// Per-juror grading stats so the side panel can render each juror's
|
||||||
|
// personal baseline and rescaled contribution.
|
||||||
|
const jurorStats: Record<string, { mean: number; stddev: number; count: number }> = {}
|
||||||
|
for (const [userId, s] of balanceCtx.jurorStats.entries()) {
|
||||||
|
jurorStats[userId] = { mean: s.mean, stddev: s.stddev, count: s.count }
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
byProject,
|
||||||
|
balanced,
|
||||||
|
jurorStats,
|
||||||
|
overallMean: balanceCtx.overallMean,
|
||||||
|
overallStddev: balanceCtx.overallStddev,
|
||||||
|
}
|
||||||
}),
|
}),
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user