From f200eda6920132ba00cb7ad28a5f4dcb5e8f10fe Mon Sep 17 00:00:00 2001
From: Matt
Date: Fri, 27 Feb 2026 11:14:02 +0100
Subject: [PATCH] fix: score distribution chart bars + add binaryDecision
backfill script
Chart: fixed bars not rendering by using explicit h-[160px] + min-h-0 on
the bar container so percentage-based heights resolve correctly.
Script: one-off backfill copies the custom "Move to the Next Stage?" boolean
criterion value into binaryDecision for evaluations where it's null.
Run: npx tsx scripts/backfill-binary-decision.ts
Co-Authored-By: Claude Opus 4.6
---
scripts/backfill-binary-decision.ts | 84 +++++++++++++++++++
.../admin/round/score-distribution.tsx | 6 +-
2 files changed, 87 insertions(+), 3 deletions(-)
create mode 100644 scripts/backfill-binary-decision.ts
diff --git a/scripts/backfill-binary-decision.ts b/scripts/backfill-binary-decision.ts
new file mode 100644
index 0000000..82fb608
--- /dev/null
+++ b/scripts/backfill-binary-decision.ts
@@ -0,0 +1,84 @@
+/**
+ * One-off script: backfill binaryDecision from custom boolean criterion
+ * "Move to the Next Stage?" for evaluations where binaryDecision is null.
+ *
+ * Usage: npx tsx scripts/backfill-binary-decision.ts
+ *
+ * What it does:
+ * 1. Finds all rounds with a boolean criterion labeled "Move to the Next Stage?"
+ * 2. For evaluations in those rounds where binaryDecision IS NULL,
+ * copies the boolean value from criterionScoresJson into binaryDecision
+ */
+
+import { PrismaClient } from '@prisma/client'
+
+const prisma = new PrismaClient()
+
+type CriterionConfig = {
+ id: string
+ label: string
+ type?: string
+}
+
+async function main() {
+ // Find all rounds that have evaluation config with criteria
+ const rounds = await prisma.round.findMany({
+ where: { roundType: 'EVALUATION' },
+ select: { id: true, name: true, configJson: true },
+ })
+
+ let totalUpdated = 0
+
+ for (const round of rounds) {
+ const config = round.configJson as Record | null
+ if (!config) continue
+
+ const criteria = (config.criteria ?? config.evaluationCriteria ?? []) as CriterionConfig[]
+
+ // Find the boolean criterion for "Move to the Next Stage?"
+ const boolCriterion = criteria.find(
+ (c) =>
+ (c.type === 'boolean') &&
+ c.label?.toLowerCase().includes('move to the next stage'),
+ )
+
+ if (!boolCriterion) continue
+
+ console.log(`Round "${round.name}" (${round.id}): found criterion "${boolCriterion.label}" (${boolCriterion.id})`)
+
+ // Find evaluations in this round where binaryDecision is null
+ const evaluations = await prisma.evaluation.findMany({
+ where: {
+ assignment: { roundId: round.id },
+ binaryDecision: null,
+ status: 'SUBMITTED',
+ criterionScoresJson: { not: undefined },
+ },
+ select: { id: true, criterionScoresJson: true },
+ })
+
+ let updated = 0
+ for (const ev of evaluations) {
+ const scores = ev.criterionScoresJson as Record | null
+ if (!scores) continue
+
+ const value = scores[boolCriterion.id]
+ if (typeof value !== 'boolean') continue
+
+ await prisma.evaluation.update({
+ where: { id: ev.id },
+ data: { binaryDecision: value },
+ })
+ updated++
+ }
+
+ console.log(` Updated ${updated}/${evaluations.length} evaluations`)
+ totalUpdated += updated
+ }
+
+ console.log(`\nDone. Total evaluations updated: ${totalUpdated}`)
+}
+
+main()
+ .catch(console.error)
+ .finally(() => prisma.$disconnect())
diff --git a/src/components/admin/round/score-distribution.tsx b/src/components/admin/round/score-distribution.tsx
index 6ac8432..f6f84a2 100644
--- a/src/components/admin/round/score-distribution.tsx
+++ b/src/components/admin/round/score-distribution.tsx
@@ -38,13 +38,13 @@ export function ScoreDistribution({ roundId }: ScoreDistributionProps) {
No evaluations submitted yet
) : (
-
+
{dist.globalDistribution.map((bucket) => {
const heightPct = (bucket.count / maxCount) * 100
return (
-