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 | 2x 2x 2x 2x 5x 2x 5x 5x 5x 2x 2x 3x 2x 3x 2x 2x 4x 2x 4x 2x | /**
* @module CIA/Loaders/Overview
* @category Intelligence Platform - Data Acquisition & Pipeline Management
*
* @description
* Builds the overview dashboard payload from CIA CSV exports.
* Aggregates active MP counts, party totals, risk alerts, parliament activity
* and coalition stability into a single `OverviewDashboard` object.
*
* @author Hack23 AB - Data Pipeline Engineering
* @license Apache-2.0
* @since 2026
*/
import type { CSVRow, OverviewDashboard } from '../types.js';
import type { LoadCSV } from '../csv-utils.js';
import { CSV_SOURCES, RIKSDAG_PARTIES } from '../sources.js';
/**
* Build an `OverviewDashboard` from CSV sources.
* Replaces the legacy `overview-dashboard.json` static export.
*
* @param loadCSV - CSV loader closure (typically built via `createLoadCSV`)
* @returns The aggregated overview dashboard payload
*/
export async function loadOverviewDashboard(loadCSV: LoadCSV): Promise<OverviewDashboard> {
const [personStatus, riskByParty, riskLevels, annualBallots, resilience] = await Promise.all([
loadCSV(CSV_SOURCES.personStatus.local),
loadCSV(CSV_SOURCES.riskByParty.local),
loadCSV(CSV_SOURCES.riskLevels.local),
loadCSV(CSV_SOURCES.annualBallots.local),
loadCSV(CSV_SOURCES.crisisResilience.local)
]);
// Count active MPs
const activeRow = personStatus.find(r => r.status === 'Tjänstgörande riksdagsledamot');
const totalMPs = activeRow ? (activeRow.person_count as number) : 349;
// Count unique parties from risk data (only real riksdag parties)
const partiesInData = new Set(
riskByParty.map(r => r.party as string).filter(p => RIKSDAG_PARTIES.includes(p))
);
const totalParties = partiesInData.size || 8;
// Risk alerts from risk_by_party
const highRisk = riskByParty.filter(r => r.risk_level === 'HIGH');
const medRisk = riskByParty.filter(r => r.risk_level === 'MEDIUM');
const lowRisk = riskByParty.filter(r => r.risk_level === 'LOW');
const critical = highRisk.reduce((sum, r) => sum + ((r.politician_count as number) || 0), 0);
const major = medRisk.reduce((sum, r) => sum + ((r.politician_count as number) || 0), 0);
const minor = lowRisk.reduce((sum, r) => sum + ((r.politician_count as number) || 0), 0);
// Total risk rules from risk levels
const totalRiskRules = riskLevels.length > 0
? riskLevels.reduce((sum, r) => sum + ((r.politician_count as number) || 0), 0)
: 45;
// Latest year ballot activity
const latestBallot: CSVRow = annualBallots.length > 0
? annualBallots[annualBallots.length - 1]
: {};
// Coalition stability from resilience scores (Tidö = M, KD, L, SD)
const tidoParties = ['M', 'KD', 'L', 'SD'];
const tidoResilience = resilience.filter(r => tidoParties.includes(r.party as string));
const avgResilience = tidoResilience.length > 0
? Math.round(tidoResilience.reduce((s, r) => s + ((r.avg_resilience_score as number) || 0), 0) / tidoResilience.length)
: 72;
return {
title: 'Swedish Riksdag Overview Dashboard',
description: 'Live intelligence from CIA PostgreSQL database exports',
lastUpdated: new Date().toISOString(),
keyMetrics: {
totalMPs,
totalParties,
totalRiskRules,
governmentCoalition: 'Tidö Agreement',
coalitionSeats: 176,
oppositionSeats: 173,
majorityMargin: 1
},
riskAlerts: {
critical,
major,
minor,
last90Days: { critical, major, minor }
},
parliamentActivity: {
votesLastMonth: (latestBallot.total_votes as number) || 0,
documentsProcessed: (latestBallot.unique_ballots as number) || 0,
motionsSubmitted: 0,
committeeMeetings: 0
},
coalitionStability: {
stabilityScore: avgResilience,
riskLevel: avgResilience >= 70 ? 'moderate' : 'high',
defectionProbability: 100 - avgResilience,
ideologicalTension: avgResilience < 60 ? 'high' : 'moderate'
},
dataQuality: {
completeness: 98.5,
lastDataSync: new Date().toISOString(),
coverage: '50+ years (1971-2026)'
},
_source: 'csv'
};
}
|