All files / src/browser/cia/loaders parties.ts

100% Statements 22/22
63.41% Branches 26/41
100% Functions 7/7
100% Lines 20/20

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'
  };
}