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 | 2x 4x 2x 2x 3x 3x 2x 2x 4x 4x 4x 4x 2x 2x 3x 3x 3x 3x 2x 2x | /**
* @module CIA/Loaders/Parties
* @category Intelligence Platform - Data Acquisition & Pipeline Management
*
* @description
* Builds the party performance dashboard from CIA CSV exports.
* Joins three CSV sources (performance, metrics, momentum), filters to the
* 8 Riksdag parties and emits a sorted `PartyPerformance` payload.
*
* @author Hack23 AB - Data Pipeline Engineering
* @license Apache-2.0
* @since 2026
*/
import type { CSVRow, PartyEntry, PartyPerformance } from '../types.js';
import type { LoadCSV } from '../csv-utils.js';
import { CSV_SOURCES, RIKSDAG_PARTIES } from '../sources.js';
/**
* Build `PartyPerformance` from CSV sources.
* Replaces the legacy `party-performance.json` static export.
*
* @param loadCSV - CSV loader closure
* @returns Party performance dashboard sorted by seat count descending
*/
export async function loadPartyPerformance(loadCSV: LoadCSV): Promise<PartyPerformance> {
const [performance, metrics, momentum] = await Promise.all([
loadCSV(CSV_SOURCES.partyPerformance.local),
loadCSV(CSV_SOURCES.partyMetrics.local),
loadCSV(CSV_SOURCES.partyMomentum.local)
]);
const activePerformance = performance.filter(p => RIKSDAG_PARTIES.includes(p.party as string));
// Build a lookup from the detailed metrics
const metricsMap: Record<string, CSVRow> = {};
metrics.forEach(m => {
Eif (RIKSDAG_PARTIES.includes(m.party as string)) {
metricsMap[m.party as string] = m;
}
});
// Get latest momentum per party
const latestMomentum: Record<string, CSVRow> = {};
momentum
.filter(m => RIKSDAG_PARTIES.includes(m.party as string))
.forEach(m => {
const party = m.party as string;
Eif (
!latestMomentum[party] ||
(m.year as number) > (latestMomentum[party].year as number) ||
((m.year as number) === (latestMomentum[party].year as number) &&
(m.quarter as number) > (latestMomentum[party].quarter as number))
) {
latestMomentum[party] = m;
}
});
// Known seat counts (from 2022 election results)
const seatMap: Record<string, number> = {
S: 107, SD: 73, M: 68, C: 24, V: 24, KD: 19, L: 16, MP: 18
};
const parties: PartyEntry[] = activePerformance.map(p => {
const party = p.party as string;
const m = metricsMap[party] || {};
const mom = latestMomentum[party] || {};
return {
id: party,
partyName: (p.party_name as string) || party,
shortName: party,
metrics: {
seats: seatMap[party] || 0,
voteShare: 0,
memberCount: (p.active_members as number) || 0,
documentsAuthored: (p.documents_last_year as number) || 0,
motionsSubmitted: (p.motions_last_year as number) || 0,
successRate: (m.avg_win_rate as number) || 0
},
voting: {
totalVotes: (m.total_votes_last_year as number) || 0,
cohesionScore: (m.avg_participation_rate as number) || 0,
rebellionRate: (m.avg_rebel_rate as number) || 0
},
trends: {
supportTrend: ((mom.trend_direction as string) || 'stable').toLowerCase(),
activityTrend: ((mom.stability_classification as string) || 'stable').toLowerCase(),
performanceLevel: (m.performance_level as string) || (p.performance_level as string) || ''
},
_source: 'csv'
};
});
// Sort by seats descending
parties.sort((a, b) => (b.metrics.seats || 0) - (a.metrics.seats || 0));
return {
title: 'Party Performance Dashboard',
description: 'Live party data from CIA PostgreSQL database exports',
lastUpdated: new Date().toISOString(),
parties,
_source: 'csv'
};
}
|