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 124 125 126 127 128 129 130 131 132 133 134 135 | 1x 1x 1x 2x 1x 1x 1x 4x 4x 4x 4x 4x 1x 4x 4x 4x 4x 4x 4x 4x 4x 1x 2x 1x 1x 2x 1x 1x 1x | /**
* @module CIA/Loaders/Committees
* @category Intelligence Platform - Data Acquisition & Pipeline Management
*
* @description
* Builds the committee network analysis from CIA CSV exports.
* Filters out INACTIVE committees with no measured output, deduplicates by
* name, and produces both the committee table and a derived productivity
* similarity network graph.
*
* @author Hack23 AB - Data Pipeline Engineering
* @license Apache-2.0
* @since 2026
*/
import type {
CSVRow,
CommitteeEntry,
CommitteeNetwork,
NetworkEdge,
NetworkNode
} from '../types.js';
import type { LoadCSV } from '../csv-utils.js';
import {
COMMITTEE_DOCS_PER_MEETING_ESTIMATE,
COMMITTEE_ORG_CODES,
CSV_SOURCES
} from '../sources.js';
/**
* Build `CommitteeNetwork` analysis from CSV sources.
* Replaces the legacy `committee-network.json` static export.
*
* @param loadCSV - CSV loader closure
* @returns Committee table plus a derived productivity-similarity network graph
*/
export async function loadCommitteeNetwork(loadCSV: LoadCSV): Promise<CommitteeNetwork> {
const [productivity, activity] = await Promise.all([
loadCSV(CSV_SOURCES.committeeProductivity.local),
loadCSV(CSV_SOURCES.committeeActivity.local)
]);
// Build activity lookup by org code
const activityMap: Record<string, number> = {};
activity.forEach(a => {
activityMap[a.org as string] = (a.document_count as number) || 0;
});
const nameToOrgCode = COMMITTEE_ORG_CODES;
// Deduplicate committees by name, keeping the entry with the most data
const bestByName: Record<string, CSVRow> = {};
productivity.forEach(c => {
const name = c.committee_name as string;
Iif (!name) return;
const existing = bestByName[name];
Eif (!existing || (c.total_documents as number) > (existing.total_documents as number) ||
((c.total_documents as number) === (existing.total_documents as number) &&
(c.total_members as number) > (existing.total_members as number))) {
bestByName[name] = c;
}
});
// Normalize committee metrics first so filtering and rendering use one consistent source.
const committees: CommitteeEntry[] = Object.values(bestByName)
.map(c => {
const name = c.committee_name as string;
const code = nameToOrgCode[name] || name.substring(0, 3).toUpperCase();
const totalDocuments = (c.total_documents as number) || 0;
const activityDocs = activityMap[code] || 0;
const documentsProcessed = Math.max(totalDocuments, activityDocs);
const productivityLevel = (c.productivity_level as string) || '';
return {
id: code,
name,
memberCount: (c.total_members as number) || 0,
influenceScore: c.docs_per_member
? Math.round((c.docs_per_member as number) * 100)
: 0,
documentsProcessed,
productivityLevel,
meetingsPerYear:
documentsProcessed > 0
? Math.round(documentsProcessed / COMMITTEE_DOCS_PER_MEETING_ESTIMATE)
: 0,
keyIssues: [productivityLevel || 'N/A'],
_source: 'csv'
};
})
.filter(c => {
// Keep real committees only; skip generic node and inactive rows with no measured output.
return (
c.name !== 'Riksdagen' &&
c.memberCount > 0 &&
(c.productivityLevel !== 'INACTIVE' || c.documentsProcessed > 0)
);
})
.sort((a, b) => b.documentsProcessed - a.documentsProcessed);
// Build simple network graph from committees
const nodes: NetworkNode[] = committees.map(c => ({
id: c.id,
name: c.name,
size: c.influenceScore
}));
// Create edges between committees that share similar productivity levels
const edges: NetworkEdge[] = [];
for (let i = 0; i < committees.length; i++) {
for (let j = i + 1; j < committees.length && edges.length < 10; j++) {
Eif (
committees[i].productivityLevel === committees[j].productivityLevel &&
committees[i].productivityLevel !== 'INACTIVE'
) {
edges.push({
source: committees[i].id,
target: committees[j].id,
weight: Math.min(committees[i].documentsProcessed, committees[j].documentsProcessed),
type: 'productivity_similarity'
});
}
}
}
return {
title: 'Committee Network Analysis',
description: 'Committee data from CIA committee productivity view',
lastUpdated: new Date().toISOString(),
committees,
networkGraph: { nodes, edges },
crossCommitteeMPs: [],
_source: 'csv'
};
}
|