- {RESULTS_AVAILABLE ? (
- VOTE_PROCESSING_PCT >= 100 ? (
- <>
-
-
{t('leaderboard.processingComplete')}
- >
+
+
+
+ {t('leaderboard.resultsProcessing')}
+
+
+ {RESULTS_AVAILABLE ? (
+ VOTE_PROCESSING_PCT >= 100 ? (
+ <>
+
+
100%
+
+ >
+ ) : (
+ <>
+
+ {VOTE_PROCESSING_PCT}%
+
+
+ >
+ )
) : (
<>
-
-
{VOTE_PROCESSING_PCT}%
+
0%
+
+
{t('leaderboard.processingAwaiting')}
>
- )
- ) : (
- <>
-
-
0%
-
{t('leaderboard.processingAwaiting')}
- >
- )}
+ )}
+
+
+
+
+ {t('leaderboard.resultsParticipation')}
+
+
+ {RESULTS_AVAILABLE ? `${REFERENCE_RESULT.participationRate}%` : '—'}
+
diff --git a/shared/types.ts b/shared/types.ts
index 98245ab..db0e370 100644
--- a/shared/types.ts
+++ b/shared/types.ts
@@ -2,7 +2,7 @@ export type PartyId = 'mkkp' | 'tisza' | 'mi_hazank' | 'dk' | 'fidesz_kdnp'
export const PARTY_IDS: PartyId[] = ['mkkp', 'tisza', 'mi_hazank', 'dk', 'fidesz_kdnp']
-export const TOTAL_ELIGIBLE_VOTERS = 7_618_706
+export const TOTAL_ELIGIBLE_VOTERS = 8_114_688
export type PredictionVisibility = 'public' | 'private'
export type PredictionStatus = 'draft' | 'finalized'
@@ -188,7 +188,7 @@ export type BestGroupEntry = {
}
export const RESULTS_AVAILABLE = false
-export const VOTE_PROCESSING_PCT = 80
+export const VOTE_PROCESSING_PCT = 100
export const CUTOFF_AT = '2026-04-12T06:00:00+02:00'
export const PARTY_LIST_THRESHOLD = 5
@@ -198,18 +198,28 @@ export type ElectionResult = {
percentages: Record
pctNationalities: number
participationRate: number
+ mandates: Record
+ nationalitiesMandate: number
}
export const REFERENCE_RESULT: ElectionResult = {
listWinnerId: 'tisza' as PartyId,
pmWinnerId: 'tisza' as PartyId,
percentages: {
- mkkp: 2.8,
- tisza: 44.6,
- mi_hazank: 6.9,
- dk: 4.3,
- fidesz_kdnp: 35.2,
+ mkkp: 0.61,
+ tisza: 56.94,
+ mi_hazank: 4.89,
+ dk: 0.75,
+ fidesz_kdnp: 36.20,
},
- pctNationalities: 0.44,
- participationRate: 68.0,
+ pctNationalities: 0.61,
+ participationRate: 74.23,
+ mandates: {
+ mkkp: 0,
+ tisza: 133,
+ mi_hazank: 0,
+ dk: 0,
+ fidesz_kdnp: 65,
+ },
+ nationalitiesMandate: 1,
}
diff --git a/worker/package.json b/worker/package.json
index 935c5e1..1860112 100644
--- a/worker/package.json
+++ b/worker/package.json
@@ -30,6 +30,9 @@
"db:score": "tsx scripts/recalculate-scores.ts local",
"db:score:dev": "tsx scripts/recalculate-scores.ts dev",
"db:score:prod": "tsx scripts/recalculate-scores.ts prod",
+ "db:score:null": "tsx scripts/recalculate-scores.ts local --null-only",
+ "db:score:null:dev": "tsx scripts/recalculate-scores.ts dev --null-only",
+ "db:score:null:prod": "tsx scripts/recalculate-scores.ts prod --null-only",
"telex:screenshot": "tsx scripts/test-telex-screenshot.ts",
"telex:screenshot:mobile": "tsx scripts/test-telex-screenshot.ts --mobile"
},
diff --git a/worker/scripts/recalculate-scores.ts b/worker/scripts/recalculate-scores.ts
index ab859f9..338fe65 100644
--- a/worker/scripts/recalculate-scores.ts
+++ b/worker/scripts/recalculate-scores.ts
@@ -9,6 +9,7 @@
*
* Flags:
* --dry-run Print counts and first rows; no SQL writes
+ * --null-only Only process rows where score IS NULL (skip already-scored rows)
* --confirm-prod Required when target is prod
*/
@@ -37,15 +38,17 @@ type D1Row = Record
function parseArgs(argv: string[]) {
let target: Target = 'local'
let dryRun = false
+ let nullOnly = false
let confirmProd = false
const unknown: string[] = []
for (const a of argv) {
if (a === '--dry-run') dryRun = true
+ else if (a === '--null-only') nullOnly = true
else if (a === '--confirm-prod') confirmProd = true
else if (a === 'local' || a === 'dev' || a === 'prod') target = a
else unknown.push(a)
}
- return { target, dryRun, confirmProd, unknown }
+ return { target, dryRun, nullOnly, confirmProd, unknown }
}
function d1ExecuteJson(dbName: string, extraArgs: string[], command: string): D1Row[] {
@@ -106,10 +109,10 @@ function rowToInput(row: D1Row) {
}
}
-const SELECT_SQL = `SELECT token, list_winner_id, pm_winner_id, pct_mkkp, pct_tisza, pct_mi_hazank, pct_dk, pct_fidesz_kdnp, pct_nationalities, participation_rate, score FROM predictions WHERE status = 'finalized'`
+const SELECT_COLS = `token, list_winner_id, pm_winner_id, pct_mkkp, pct_tisza, pct_mi_hazank, pct_dk, pct_fidesz_kdnp, pct_nationalities, participation_rate, score`
function main() {
- const { target, dryRun, confirmProd, unknown } = parseArgs(process.argv.slice(2))
+ const { target, dryRun, nullOnly, confirmProd, unknown } = parseArgs(process.argv.slice(2))
if (unknown.length > 0) {
console.error('Unknown arguments:', unknown.join(' '))
process.exit(1)
@@ -122,9 +125,12 @@ function main() {
const dbName = TARGET_DB[target]
const wranglerDbArgs = target === 'local' ? (['--local'] as const) : (['--remote'] as const)
- console.error(`Target: ${target} (${dbName})${dryRun ? ' [dry-run]' : ''}`)
+ const selectSql = `SELECT ${SELECT_COLS} FROM predictions WHERE status = 'finalized'${nullOnly ? ' AND score IS NULL' : ''}`
- const rows = d1ExecuteJson(dbName, [...wranglerDbArgs], SELECT_SQL)
+ const flags = [dryRun && '[dry-run]', nullOnly && '[null-only]'].filter(Boolean).join(' ')
+ console.error(`Target: ${target} (${dbName})${flags ? ' ' + flags : ''}`)
+
+ const rows = d1ExecuteJson(dbName, [...wranglerDbArgs], selectSql)
console.error(`Finalized predictions: ${rows.length}`)
const updates: { token: string; score: number; prev: number | null }[] = []