Files
Substrate/get-de-parliament
svemagie e0e879d917 feat: add DE bundesAPI integrations — Lobbyregister, SMARD, Haushalt, DIP
Four new data sources and fetch scripts via bundesAPI community project:

- DS-00011 Lobbyregister: 6,799 registrants, €0.86–0.91B declared lobbying (FY2024)
- DS-00012 SMARD Strommarkt: 60.2% renewable 2024, Wind Onshore #1 (107 TWh)
- DS-00013 Bundeshaushalt: €474.75B Ist-Wert 2024, 38.2% Soziales, 10.6% Verteidigung
- DS-00014 DIP Bundestag: 7,605 Drucksachen WP21, 12,507 Vorgänge, 83 Plenarprotokolle

Each integration: live-data fetch script (bun/TypeScript) + DATASET-TEMPLATE
markdown + CSV outputs. Scripts idempotent — re-run for current data.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 12:54:02 +02:00

179 lines
6.6 KiB
Plaintext
Executable File

#!/usr/bin/env bun
/**
* Get DE Parliament Activity Data (DIP Bundestag)
*
* Fetches Drucksachen counts by type from the DIP (Dokumentations- und
* Informationssystem für Parlamentsmaterialien) API and produces:
* - Data/DE-Parliament-Activity/drucksachen-wp21.csv
*
* API: https://search.dip.bundestag.de/api/v1
* Docs: https://dip.bundestag.de/%C3%BCber-dip/hilfe/api
*
* API KEY NOTE: The key below is a temporary public key valid until end of 05/2026.
* To request a permanent individual API key, contact:
* parlamentsdokumentation@bundestag.de
*/
// Bun on macOS may lack the Bundestag CA in its bundled cert store.
// This flag allows the fetch to proceed against the known-good government endpoint.
process.env.NODE_TLS_REJECT_UNAUTHORIZED = "0";
import { writeFileSync, mkdirSync } from "fs";
import { join } from "path";
const API_BASE = "https://search.dip.bundestag.de/api/v1";
const API_KEY = "OSOegLs.PR2lwJ1dwCeje9vTj7FPOt3hvpYKtwKkhw";
const WAHLPERIODE = 21;
const OUT_DIR = join(__dirname, "Data/DE-Parliament-Activity");
interface DipResponse {
numFound: number;
documents: unknown[];
cursor?: string;
}
async function fetchCount(
endpoint: string,
params: Record<string, string | number> = {}
): Promise<number> {
const url = new URL(`${API_BASE}/${endpoint}`);
url.searchParams.set("limit", "1");
for (const [k, v] of Object.entries(params)) {
url.searchParams.set(k, String(v));
}
// Pass key both as header and query param for compatibility
url.searchParams.set("apikey", API_KEY);
const res = await fetch(url.toString(), {
headers: {
Authorization: `ApiKey ${API_KEY}`,
Accept: "application/json",
},
});
if (!res.ok) {
throw new Error(`HTTP ${res.status} for ${endpoint}: ${res.statusText}`);
}
const data = (await res.json()) as DipResponse;
return data.numFound;
}
function csvEscape(value: string | number): string {
const s = String(value);
if (s.includes(",") || s.includes('"') || s.includes("\n")) {
return `"${s.replace(/"/g, '""')}"`;
}
return s;
}
const DRUCKSACHE_TYPES: Array<{ type: string; description_de: string }> = [
{ type: "Antrag", description_de: "Anträge der Fraktionen" },
{ type: "Kleine Anfrage", description_de: "Kleine Anfragen" },
{ type: "Große Anfrage", description_de: "Große Anfragen" },
{ type: "Schriftliche Fragen", description_de: "Schriftliche Fragen" },
{ type: "Gesetzentwurf", description_de: "Gesetzentwürfe" },
{
type: "Unterrichtung",
description_de: "Unterrichtungen (Berichte der Bundesregierung)",
},
{
type: "Entschließungsantrag",
description_de: "Entschließungsanträge",
},
{
type: "Beschlussempfehlung",
description_de: "Beschlussempfehlungen der Ausschüsse",
},
];
async function main() {
console.log(
`Fetching DIP Bundestag data for Wahlperiode ${WAHLPERIODE}…\n`
);
mkdirSync(OUT_DIR, { recursive: true });
// ── Drucksachen total ────────────────────────────────────────────────────
const totalDrucksachen = await fetchCount("drucksache", {
"f.wahlperiode": WAHLPERIODE,
});
console.log(`Total Drucksachen (WP${WAHLPERIODE}): ${totalDrucksachen}`);
// ── By type ───────────────────────────────────────────────────────────────
const typeResults: Array<{
type: string;
count: number;
description_de: string;
}> = [];
for (const { type, description_de } of DRUCKSACHE_TYPES) {
const count = await fetchCount("drucksache", {
"f.wahlperiode": WAHLPERIODE,
"f.drucksachetyp": type,
});
console.log(` ${type}: ${count}`);
typeResults.push({ type, count, description_de });
}
// ── Vorgänge ──────────────────────────────────────────────────────────────
const totalVorgaenge = await fetchCount("vorgang", {
"f.wahlperiode": WAHLPERIODE,
});
console.log(`\nVorgänge (WP${WAHLPERIODE}): ${totalVorgaenge}`);
// ── Plenarprotokolle ──────────────────────────────────────────────────────
const totalPlenar = await fetchCount("plenarprotokoll", {
"f.wahlperiode": WAHLPERIODE,
});
console.log(`Plenarprotokolle (WP${WAHLPERIODE}): ${totalPlenar}`);
// ── Write CSV ─────────────────────────────────────────────────────────────
const header = "type,count,description_de";
const rows: string[] = [
[
csvEscape("Gesamt_Drucksachen"),
totalDrucksachen,
csvEscape(`Alle Drucksachen Wahlperiode ${WAHLPERIODE}`),
].join(","),
...typeResults.map((r) =>
[csvEscape(r.type), r.count, csvEscape(r.description_de)].join(",")
),
[
csvEscape("Vorgaenge"),
totalVorgaenge,
csvEscape(`Gesetzgebungsvorgänge Wahlperiode ${WAHLPERIODE}`),
].join(","),
[
csvEscape("Plenarprotokolle"),
totalPlenar,
csvEscape(`Plenarprotokolle Wahlperiode ${WAHLPERIODE}`),
].join(","),
];
const csvPath = join(OUT_DIR, "drucksachen-wp21.csv");
writeFileSync(csvPath, [header, ...rows].join("\n") + "\n");
console.log(`\nWrote ${csvPath}`);
// ── Summary ───────────────────────────────────────────────────────────────
const topType = [...typeResults].sort((a, b) => b.count - a.count)[0];
const gesetzentwuerfe =
typeResults.find((r) => r.type === "Gesetzentwurf")?.count ?? 0;
console.log(`\n── Summary ──────────────────────────────────────────────`);
console.log(`Wahlperiode: ${WAHLPERIODE} (seit Oktober 2025)`);
console.log(`Total Drucksachen: ${totalDrucksachen}`);
console.log(`Most common type: ${topType.type} (${topType.count})`);
console.log(`Gesetzentwürfe: ${gesetzentwuerfe}`);
console.log(`Vorgänge: ${totalVorgaenge}`);
console.log(`Plenarprotokolle: ${totalPlenar}`);
console.log(`API key expires: 05/2026 — renew at parlamentsdokumentation@bundestag.de`);
}
main().catch((err) => {
console.error("Error:", err.message);
process.exit(1);
});