Press n or j to go to the next uncovered block, b, p or k for the previous block.
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 | 4x 376x 6x 6x 4x 30x 30x 30x 30x 9x 1x 10x 10x 53x 53x 53x 53x 53x 53x 1x 52x 52x 52x 10x 30x 30x 30x 30x 30x 30x 107x 30x 3x 27x 10x | /**
* @module CIA/Loaders/Election
* @category Intelligence Platform - Data Acquisition & Pipeline Management
*
* @description
* Builds the election analysis payload from CIA CSV exports.
* Parses seat forecasts and coalition scenarios with strict numeric/boolean
* validation; rows missing required fields are dropped.
*
* @author Hack23 AB - Data Pipeline Engineering
* @license Apache-2.0
* @since 2026
*/
import type { ElectionAnalysis } from '../types.js';
import type { LoadCSV } from '../csv-utils.js';
import { CSV_SOURCES } from '../sources.js';
const toFiniteNumber = (value: unknown): number | undefined => {
if (typeof value === 'number' && Number.isFinite(value)) return value;
Iif (typeof value === 'string' && value.trim() !== '') {
const num = Number(value);
if (Number.isFinite(num)) return num;
}
return undefined;
};
const toBoolean = (value: unknown): boolean | undefined => {
Iif (typeof value === 'boolean') return value;
Eif (typeof value === 'string') {
const normalized = value.trim().toLowerCase();
if (normalized === 'true') return true;
if (normalized === 'false') return false;
}
return undefined;
};
/**
* Build `ElectionAnalysis` from CSV sources.
* Replaces the legacy `election-analysis.json` static export.
*
* @param loadCSV - CSV loader closure
* @returns Election forecast with seat predictions, coalition scenarios and key factors
*/
export async function loadElectionAnalysis(loadCSV: LoadCSV): Promise<ElectionAnalysis> {
const [forecastRows, scenarioRows] = await Promise.all([
loadCSV(CSV_SOURCES.electionForecast.local),
loadCSV(CSV_SOURCES.coalitionScenarios.local)
]);
const parties = forecastRows.flatMap(r => {
const name = String(r.name ?? '').trim();
const currentSeats = toFiniteNumber(r.currentSeats);
const predictedSeats = toFiniteNumber(r.predictedSeats);
const change = toFiniteNumber(r.change);
const voteShare = toFiniteNumber(r.voteShare);
if (!name || currentSeats === undefined || predictedSeats === undefined || change === undefined || voteShare === undefined) {
return [];
}
const confidenceMin = toFiniteNumber(r.confidenceMin);
const confidenceMax = toFiniteNumber(r.confidenceMax);
return [{
name,
currentSeats,
predictedSeats,
change,
voteShare,
confidenceInterval:
confidenceMin !== undefined && confidenceMax !== undefined
? { min: confidenceMin, max: confidenceMax }
: undefined
}];
});
const coalitionScenarios = scenarioRows.flatMap(r => {
const name = String(r.name ?? '').trim();
const probability = toFiniteNumber(r.probability);
const totalSeats = toFiniteNumber(r.totalSeats);
const majority = toBoolean(r.majority);
const riskLevel = String(r.riskLevel ?? '').trim();
const composition = String(r.composition ?? '')
.split(',')
.map(s => s.trim())
.filter(Boolean);
if (
!name ||
probability === undefined ||
totalSeats === undefined ||
majority === undefined ||
!riskLevel ||
composition.length === 0
) {
return [];
}
return [{
name,
probability,
composition,
totalSeats,
majority,
riskLevel
}];
});
return {
forecast: { parties },
coalitionScenarios,
keyFactors: [
'Economic conditions',
'Immigration policy',
'Climate change priorities',
'Healthcare reform',
'NATO membership impact'
],
electionDate: '2026-09-13'
};
}
|