From a59d475e6b77b60e1759e1a88822fceb61b260ff Mon Sep 17 00:00:00 2001 From: Simon Diesenreiter Date: Thu, 12 Nov 2026 22:58:34 +0100 Subject: [PATCH] Add official election results and candidates' preference votes (BMI) National Council 2024 and 2019 and EU 2024: party results for Austria and the states, and preference votes of candidates who resolve to a known politician (exact name plus birth year where published). New claim type election_result, new resolver hint birth_year. Co-Authored-By: Claude Opus 5.5 --- backend/migrations/0004_election_results.sql | 7 + backend/politrack/api/schemas.py | 2 +- backend/politrack/core/resolve.py | 10 + backend/politrack/mcp_server.py | 3 +- backend/politrack/sources/elections.py | 224 ++++++++++ .../preferences_bund.expected.json | 262 ++++++++++++ .../preferences_bund.request.json | 13 + .../bmi.elections/preferences_bund.xlsx | Bin 0 -> 5664 bytes .../preferences_eu.expected.json | 250 +++++++++++ .../bmi.elections/preferences_eu.request.json | 13 + .../bmi.elections/preferences_eu.xlsx | Bin 0 -> 6202 bytes .../preferences_land.expected.json | 197 +++++++++ .../preferences_land.request.json | 13 + .../bmi.elections/preferences_land.xlsx | Bin 0 -> 5211 bytes .../result_euw2024.expected.json | 266 ++++++++++++ .../bmi.elections/result_euw2024.request.json | 12 + .../bmi.elections/result_euw2024.xlsx | Bin 0 -> 5275 bytes .../result_nrw2024.expected.json | 398 ++++++++++++++++++ .../bmi.elections/result_nrw2024.request.json | 12 + .../bmi.elections/result_nrw2024.xlsx | Bin 0 -> 5311 bytes docs/data-model.md | 6 +- docs/roadmap.md | 4 +- docs/sources.md | 2 +- web/messages/de.json | 1 + web/messages/en.json | 1 + web/src/app/[locale]/parteien/[slug]/page.tsx | 2 +- web/src/app/[locale]/personen/[slug]/page.tsx | 2 +- web/src/components/badges.tsx | 3 +- web/src/lib/api-types.ts | 2 +- 29 files changed, 1696 insertions(+), 9 deletions(-) create mode 100644 backend/migrations/0004_election_results.sql create mode 100644 backend/politrack/sources/elections.py create mode 100644 backend/tests/fixtures/bmi.elections/preferences_bund.expected.json create mode 100644 backend/tests/fixtures/bmi.elections/preferences_bund.request.json create mode 100644 backend/tests/fixtures/bmi.elections/preferences_bund.xlsx create mode 100644 backend/tests/fixtures/bmi.elections/preferences_eu.expected.json create mode 100644 backend/tests/fixtures/bmi.elections/preferences_eu.request.json create mode 100644 backend/tests/fixtures/bmi.elections/preferences_eu.xlsx create mode 100644 backend/tests/fixtures/bmi.elections/preferences_land.expected.json create mode 100644 backend/tests/fixtures/bmi.elections/preferences_land.request.json create mode 100644 backend/tests/fixtures/bmi.elections/preferences_land.xlsx create mode 100644 backend/tests/fixtures/bmi.elections/result_euw2024.expected.json create mode 100644 backend/tests/fixtures/bmi.elections/result_euw2024.request.json create mode 100644 backend/tests/fixtures/bmi.elections/result_euw2024.xlsx create mode 100644 backend/tests/fixtures/bmi.elections/result_nrw2024.expected.json create mode 100644 backend/tests/fixtures/bmi.elections/result_nrw2024.request.json create mode 100644 backend/tests/fixtures/bmi.elections/result_nrw2024.xlsx diff --git a/backend/migrations/0004_election_results.sql b/backend/migrations/0004_election_results.sql new file mode 100644 index 0000000..bfeb6bf --- /dev/null +++ b/backend/migrations/0004_election_results.sql @@ -0,0 +1,7 @@ +ALTER TABLE claim DROP CONSTRAINT claim_claim_type_check; +ALTER TABLE claim ADD CONSTRAINT claim_claim_type_check CHECK (claim_type IN ( + 'role', 'vote', 'donation', 'money_flow', 'statement', 'appearance', 'meeting', 'travel', + 'side_job', 'legal_proceeding', 'lobbying', 'media_mention', 'career', + 'parliamentary_question', 'government_decision', 'wiki_edit', 'declaration', 'ownership', + 'affiliation', 'party_finance', 'election_result', 'other')); +COMMENT ON COLUMN claim.claim_type IS 'Kind of fact. election_result: a party''s result in a region (counterpart = the elected body) or a candidate''s preference votes (person, counterpart = party).'; diff --git a/backend/politrack/api/schemas.py b/backend/politrack/api/schemas.py index a985cf8..81cc19c 100644 --- a/backend/politrack/api/schemas.py +++ b/backend/politrack/api/schemas.py @@ -11,7 +11,7 @@ from pydantic import BaseModel, ConfigDict ClaimType = Literal["role", "vote", "donation", "money_flow", "statement", "appearance", "meeting", "travel", "side_job", "legal_proceeding", "lobbying", "media_mention", "career", "parliamentary_question", "government_decision", "wiki_edit", "declaration", "ownership", "affiliation", "party_finance", - "other"] + "election_result", "other"] PersonStatus = Literal["active", "former", "party_member", "deceased"] diff --git a/backend/politrack/core/resolve.py b/backend/politrack/core/resolve.py index 8b065eb..3712f13 100644 --- a/backend/politrack/core/resolve.py +++ b/backend/politrack/core/resolve.py @@ -7,6 +7,8 @@ Order (first hit wins, ambiguity never guesses): Hint strategies for persons (documented in docs/data-model.md): surname_in_body + on members of a chamber on a date with that surname (roll-call lists) office + on holder of an office title on a date (Council of Ministers minutes) + birth_year exact name and year of birth (candidate lists); add no_name_match so that a name + alone never matches when the year doesn't """ from __future__ import annotations @@ -133,6 +135,14 @@ class Resolver: return suffix[0]["id"] if len(suffix) == 1 else None if "office" in hints: return self.office_holder(hints["office"], on) + if "birth_year" in hints and ref.name: + rows = self.conn.execute( + """SELECT DISTINCT a.entity_id AS id FROM alias a JOIN person p ON p.id = a.entity_id + WHERE a.entity_type = 'person' AND a.alias_norm = %s + AND EXTRACT(YEAR FROM p.birth_date) = %s""", + (normalize(strip_titles(ref.name)), int(hints["birth_year"])), + ).fetchall() + return rows[0]["id"] if len(rows) == 1 else None return None def office_holder(self, office: str, on: date | None) -> int | None: diff --git a/backend/politrack/mcp_server.py b/backend/politrack/mcp_server.py index 9ae9d2a..ded0493 100644 --- a/backend/politrack/mcp_server.py +++ b/backend/politrack/mcp_server.py @@ -115,7 +115,8 @@ def list_claims( ) -> str: """Claims about a person/party/organisation, newest first. claim_types: role, vote, donation, money_flow, statement, appearance, meeting, travel, side_job, legal_proceeding, lobbying, media_mention, career, - parliamentary_question, government_decision, wiki_edit, declaration, ownership, affiliation, party_finance. + parliamentary_question, government_decision, wiki_edit, declaration, ownership, affiliation, party_finance, + election_result. Dates as YYYY-MM-DD.""" with connection() as conn: return _dump(queries.list_claims( diff --git a/backend/politrack/sources/elections.py b/backend/politrack/sources/elections.py new file mode 100644 index 0000000..92e2432 --- /dev/null +++ b/backend/politrack/sources/elections.py @@ -0,0 +1,224 @@ +"""Official election results from the Federal Ministry of the Interior (BMI, Bundeswahlbehörde). + +Per election: the final result (XLSX, one row per region with votes per party) and the preference votes of the +candidates (XLSX, per list). Only the federal and state totals of the result are kept; municipal rows stay in the +raw archive. Candidates become claims only if they resolve to a known politician: by exact name plus birth year +where the file has one, otherwise by exact, unambiguous name. Unknown candidates are counted, never stored, and +their occupation is never kept. + +Not covered yet: the 2019 state and regional lists (a print layout with side-by-side columns), elections before +2019, Landtag and municipal elections (published by the states). +""" + +from __future__ import annotations + +import io +import re +from collections.abc import Iterator +from dataclasses import dataclass +from datetime import date +from typing import Any + +from politrack.core import Adapter, Request, Response, RunContext, SourceSpec, register +from politrack.core.records import Claim, Evidence, OrgRef, PersonRef +from politrack.text import strip_titles + +BMI = "https://www.bmi.gv.at/412" +SPEC = SourceSpec( + slug="bmi-wahlen", name="Bundesministerium für Inneres – Wahlergebnisse", kind="official", + homepage=f"{BMI}/start.aspx", license="Amtliches Werk (§7 UrhG); Ergebnisse auch als CC BY 4.0 auf data.gv.at", + access="bulk", trust=95, min_interval=3.0, + tos_notes="Final results and preference votes as published by the Bundeswahlbehörde (XLSX). robots.txt sets " + "no rules. Only candidates who resolve to a known politician are stored; occupations are never kept.", +) + + +@dataclass(frozen=True) +class Election: + key: str + name_de: str + name_en: str + held_on: date + body: str # slug of the elected body + path: str # folder on bmi.gv.at + result: str + preferences: tuple[tuple[str, str], ...] # (layout, file): layouts "bund", "land", "eu" + + +ELECTIONS = ( + Election("nrw2024", "Nationalratswahl 2024", "2024 National Council election", date(2024, 9, 29), "nationalrat", + "nationalratswahlen/nationalratswahl_2024/files", "endgueltiges_ergebnis_beschluss_bundeswahlbehoerde_16102024.xlsx", + (("bund", "nrw2024_aufstellung_der_vorzugsstimmen_der_bundesbewerber_16102024.xlsx"), + ("land", "nrw2024_aufstellung_der_vorzugsstimmen_der_landes_undregionalbewerber_16102024.xlsx"))), + Election("nrw2019", "Nationalratswahl 2019", "2019 National Council election", date(2019, 9, 29), "nationalrat", + "nationalratswahlen/nationalratswahl_2019/files", "endgultiges_gesamtergebnis_nrw19_16102019.xlsx", + (("bund", "nrw19_vorzugsstimmen_bundesweit_16102019.xlsx"),)), + Election("euw2024", "Europawahl 2024", "2024 European Parliament election", date(2024, 6, 9), + "europaeisches-parlament", "europawahlen/europawahl_2024/files", "endgueltiges_ergebnis_europawahl_26062024.xlsx", + (("eu", "gesamtaufstellung_erzielte_vorzugsstimmen_eu_wahl_2024_parteien.xlsx"),)), +) +BY_KEY = {e.key: e for e in ELECTIONS} + +REGION_CODE = re.compile(r"^G\d0000$") # G00000 Österreich, G10000 Burgenland ... G90000 Wien +PARTY_HEAD = re.compile(r"\(([^()]+)\)\s*$") +LIST_HEAD = re.compile(r"^(Landes|Regional)parteilisten im (?:Landes|Regional)wahlkreis\s+(.+?)\s*$") + + +def de_int(n: int) -> str: + return f"{n:,}".replace(",", ".") + + +def de_pct(p: float) -> str: + return f"{p:.2f}".replace(".", ",") + + +def sheets(body: bytes) -> list[tuple[str, list[tuple]]]: + import openpyxl + + wb = openpyxl.load_workbook(io.BytesIO(body), read_only=True, data_only=True) + return [(ws.title, list(ws.iter_rows(values_only=True))) for ws in wb.worksheets] + + +def label(*cells: Any) -> str: + return " ".join(str(c) for c in cells if c not in (None, "")).replace("\n", "").strip().lower() + + +def parse_results(rows: list[tuple]) -> list[dict[str, Any]]: + """Votes per party for Austria and the nine states. The header has party names in the first row, either + with the totals in the same row (2024) or split over two rows ("Stimmen" / "gültige", 2019 and EU 2024).""" + head, sub = rows[0], rows[1] if rows[1] and rows[1][0] is None else () + labels = [label(head[i], sub[i] if i < len(sub) else None) for i in range(len(head))] + valid_col = next(i for i, text in enumerate(labels) if text.endswith("gültige") and "ungültige" not in text) + parties = [(i, str(name).strip()) for i, name in enumerate(head) + if i > valid_col and isinstance(name, str) and name.strip() not in ("", "%")] + out = [] + for row in rows: + code = row[0] + if not (isinstance(code, str) and REGION_CODE.match(code)): + continue + valid = int(row[valid_col] or 0) + for col, party in parties: + votes = int(row[col] or 0) + if not votes: + continue + # the share as published (next column), so the figures match the official result exactly + published = row[col + 1] if col + 1 < len(row) and head[col + 1] == "%" else None + percent = (round(float(published), 2) if isinstance(published, (int, float)) + else round(votes * 100 / valid, 2) if valid else None) + out.append({"code": code, "region": str(row[1]).strip(), "electorate": int(row[2] or 0), + "valid": valid, "party": party, "votes": votes, "percent": percent}) + return out + + +def split_name(text: str) -> tuple[str, str]: + """'Brunner, Dr. Magnus' -> ('Brunner', 'Dr. Magnus'); 'Babler, MSc, Andreas' -> ('Babler', 'Andreas').""" + family, _, rest = str(text).partition(",") + parts = [p.strip() for p in rest.split(",")] + return family.strip(), next((p for p in parts if strip_titles(p)), "") + + +def parse_preferences(layout: str, workbook: list[tuple[str, list[tuple]]]) -> list[dict[str, Any]]: + """Candidates with their preference votes. Returns rank, name, birth year (if published), party, list.""" + out: list[dict[str, Any]] = [] + for sheet, rows in workbook: + party, party_name, list_label = None, None, "Bundesliste" + total_col = None + if layout == "eu": + party, party_name, list_label = sheet.strip(), sheet.strip(), "Bundesliste" + for row in rows: + cells = [c for c in row if c is not None and c != ""] + if not cells: + continue + first = row[0] + if layout == "eu" and first == "LP": + total_col = next((i for i, c in enumerate(row) if c == "Gesamt"), None) + continue + if isinstance(first, str) and len(cells) == 1: + if (m := LIST_HEAD.match(first)): + region = re.sub(r"^\w+\s*-\s*", "", m.group(2)) # "1A - Burgenland Nord" -> "Burgenland Nord" + list_label = f"{m.group(1)}parteiliste {region}" + elif (m := PARTY_HEAD.search(first)) and layout != "eu": + party, party_name, list_label = m.group(1).strip(), first.strip(), "Bundesliste" + continue + rank = str(first).rstrip(".") if first is not None else "" + if not rank.isdigit() or party is None: + continue + if layout == "land": + family, given, year, votes = row[1], row[2], row[4], row[6] + elif layout == "eu": + family, given = split_name(row[1]) + year, votes = None, row[total_col] if total_col is not None else None + else: + family, given = split_name(row[1]) + year, votes = row[2], row[4] + if not family or not isinstance(votes, (int, float)): + continue + out.append({"rank": int(rank), "name": f"{given} {family}".strip(), "birth_year": int(year) + if isinstance(year, (int, float)) else None, "votes": int(votes), "party": party, + "party_name": party_name, "list": list_label}) + return out + + +@register +class ElectionResults(Adapter): + spec = SPEC + name = "bmi.elections" + description = "Final results of federal and EU elections and candidates' preference votes (BMI)" + schedule = "0 6 1 * *" # the files are final; monthly re-checks cost a conditional request each + queue = "bulk" + + def start(self, ctx: RunContext) -> Iterator[Request]: + wanted = set(ctx.params.get("elections") or BY_KEY) + for election in ELECTIONS: + if election.key not in wanted: + continue + base = f"{BMI}/{election.path}" + yield Request(url=f"{base}/{election.result}", kind="result", meta={"election": election.key}) + for layout, file in election.preferences: + yield Request(url=f"{base}/{file}", kind="preferences", meta={"election": election.key, "layout": layout}) + + def parse(self, ctx: RunContext, response: Response) -> Iterator[Claim]: + election = BY_KEY[response.meta["election"]] + workbook = sheets(response.body) + if response.request.kind == "result": + yield from self.result_claims(ctx, election, response.request.url, workbook[0][1]) + else: + yield from self.preference_claims(ctx, election, response.request.url, + parse_preferences(response.meta["layout"], workbook)) + + def result_claims(self, ctx: RunContext, election: Election, url: str, rows: list[tuple]) -> Iterator[Claim]: + for r in parse_results(rows): + pct = f"{de_pct(r['percent'])} %" if r["percent"] is not None else "?" + yield Claim( + claim_type="election_result", org=OrgRef(short_name=r["party"], kind="party", create=False), + counterpart_org=OrgRef(slug=election.body), occurred_at=election.held_on, + key=("election", election.key, r["code"], r["party"]), + summary_de=f"{r['party']}: {pct} ({de_int(r['votes'])} Stimmen) bei der {election.name_de} – {r['region']}", + summary_en=f"{r['party']}: {pct.replace(',', '.')} ({r['votes']:,} votes) in the {election.name_en} " + f"– {r['region']}", + data={"election": election.key, "party": r["party"], "region": r["region"], "region_code": r["code"], + "votes": r["votes"], "percent": r["percent"], "valid_votes": r["valid"], + "electorate": r["electorate"]}, + evidence=[Evidence(url=url, doc_type="dataset_row", title=f"BMI: {election.name_de}, endgültiges Ergebnis", + published_at=election.held_on, locator=f"{r['code']}:{r['party']}", + quote=f"{r['region']} | {r['party']} | {r['votes']} | {r['valid']} gültige")], + ) + + def preference_claims(self, ctx: RunContext, election: Election, url: str, + candidates: list[dict[str, Any]]) -> Iterator[Claim]: + for c in candidates: + hints = {"birth_year": c["birth_year"], "no_name_match": True} if c["birth_year"] else {} + yield Claim( + claim_type="election_result", person=PersonRef(name=c["name"], hints=hints), + counterpart_org=OrgRef(short_name=c["party"], kind="party", create=False), occurred_at=election.held_on, + key=("preference-votes", election.key, c["list"], c["party"], c["rank"], c["name"]), + summary_de=f"{de_int(c['votes'])} Vorzugsstimmen bei der {election.name_de} " + f"({c['list']} {c['party']}, Platz {c['rank']})", + summary_en=f"{c['votes']:,} preference votes in the {election.name_en} " + f"({c['party']}, {c['list']}, position {c['rank']})", + data={"election": election.key, "preference_votes": c["votes"], "rank": c["rank"], "list": c["list"], + "party": c["party"]}, + evidence=[Evidence(url=url, doc_type="dataset_row", title=f"BMI: {election.name_de}, Vorzugsstimmen", + published_at=election.held_on, locator=f"{c['list']}:{c['party']}:{c['rank']}", + quote=f"{c['list']} {c['party']}, Platz {c['rank']}: {c['votes']} Vorzugsstimmen")], + ) diff --git a/backend/tests/fixtures/bmi.elections/preferences_bund.expected.json b/backend/tests/fixtures/bmi.elections/preferences_bund.expected.json new file mode 100644 index 0000000..2438a51 --- /dev/null +++ b/backend/tests/fixtures/bmi.elections/preferences_bund.expected.json @@ -0,0 +1,262 @@ +[ + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "ÖVP", + "slug": null + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "list": "Bundesliste", + "party": "ÖVP", + "preference_votes": 60402, + "rank": 1 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "Bundesliste:ÖVP:1", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Bundesliste ÖVP, Platz 1: 60402 Vorzugsstimmen", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, Vorzugsstimmen", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_bundesbewerber_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "preference-votes", + "nrw2024", + "Bundesliste", + "ÖVP", + 1, + "Karl Nehammer" + ], + "occurred_at": "2024-09-29", + "org": null, + "person": { + "__type__": "PersonRef", + "hints": { + "birth_year": 1972, + "no_name_match": true + }, + "ids": {}, + "name": "Karl Nehammer" + }, + "review": false, + "sensitive": false, + "summary_de": "60.402 Vorzugsstimmen bei der Nationalratswahl 2024 (Bundesliste ÖVP, Platz 1)", + "summary_en": "60,402 preference votes in the 2024 National Council election (ÖVP, Bundesliste, position 1)", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "ÖVP", + "slug": null + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "list": "Bundesliste", + "party": "ÖVP", + "preference_votes": 7820, + "rank": 2 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "Bundesliste:ÖVP:2", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Bundesliste ÖVP, Platz 2: 7820 Vorzugsstimmen", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, Vorzugsstimmen", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_bundesbewerber_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "preference-votes", + "nrw2024", + "Bundesliste", + "ÖVP", + 2, + "Claudia Plakolm" + ], + "occurred_at": "2024-09-29", + "org": null, + "person": { + "__type__": "PersonRef", + "hints": { + "birth_year": 1994, + "no_name_match": true + }, + "ids": {}, + "name": "Claudia Plakolm" + }, + "review": false, + "sensitive": false, + "summary_de": "7.820 Vorzugsstimmen bei der Nationalratswahl 2024 (Bundesliste ÖVP, Platz 2)", + "summary_en": "7,820 preference votes in the 2024 National Council election (ÖVP, Bundesliste, position 2)", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "ÖVP", + "slug": null + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "list": "Bundesliste", + "party": "ÖVP", + "preference_votes": 2069, + "rank": 3 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "Bundesliste:ÖVP:3", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Bundesliste ÖVP, Platz 3: 2069 Vorzugsstimmen", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, Vorzugsstimmen", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_bundesbewerber_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "preference-votes", + "nrw2024", + "Bundesliste", + "ÖVP", + 3, + "Dr. Magnus Brunner" + ], + "occurred_at": "2024-09-29", + "org": null, + "person": { + "__type__": "PersonRef", + "hints": { + "birth_year": 1972, + "no_name_match": true + }, + "ids": {}, + "name": "Dr. Magnus Brunner" + }, + "review": false, + "sensitive": false, + "summary_de": "2.069 Vorzugsstimmen bei der Nationalratswahl 2024 (Bundesliste ÖVP, Platz 3)", + "summary_en": "2,069 preference votes in the 2024 National Council election (ÖVP, Bundesliste, position 3)", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "SPÖ", + "slug": null + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "list": "Bundesliste", + "party": "SPÖ", + "preference_votes": 46440, + "rank": 1 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "Bundesliste:SPÖ:1", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Bundesliste SPÖ, Platz 1: 46440 Vorzugsstimmen", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, Vorzugsstimmen", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_bundesbewerber_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "preference-votes", + "nrw2024", + "Bundesliste", + "SPÖ", + 1, + "Andreas Babler" + ], + "occurred_at": "2024-09-29", + "org": null, + "person": { + "__type__": "PersonRef", + "hints": { + "birth_year": 1973, + "no_name_match": true + }, + "ids": {}, + "name": "Andreas Babler" + }, + "review": false, + "sensitive": false, + "summary_de": "46.440 Vorzugsstimmen bei der Nationalratswahl 2024 (Bundesliste SPÖ, Platz 1)", + "summary_en": "46,440 preference votes in the 2024 National Council election (SPÖ, Bundesliste, position 1)", + "topics": [], + "valid_from": null, + "valid_to": null + } +] diff --git a/backend/tests/fixtures/bmi.elections/preferences_bund.request.json b/backend/tests/fixtures/bmi.elections/preferences_bund.request.json new file mode 100644 index 0000000..bbca639 --- /dev/null +++ b/backend/tests/fixtures/bmi.elections/preferences_bund.request.json @@ -0,0 +1,13 @@ +{ + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_bundesbewerber_16102024.xlsx", + "kind": "preferences", + "method": "GET", + "params": null, + "json": null, + "meta": { + "election": "nrw2024", + "layout": "bund" + }, + "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "body_file": "preferences_bund.xlsx" +} \ No newline at end of file diff --git a/backend/tests/fixtures/bmi.elections/preferences_bund.xlsx b/backend/tests/fixtures/bmi.elections/preferences_bund.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..ca8a4a5ffdc13d068a88cce8277f58442f4b8aff GIT binary patch literal 5664 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 literal 0 HcmV?d00001 diff --git a/backend/tests/fixtures/bmi.elections/preferences_land.expected.json b/backend/tests/fixtures/bmi.elections/preferences_land.expected.json new file mode 100644 index 0000000..13744bb --- /dev/null +++ b/backend/tests/fixtures/bmi.elections/preferences_land.expected.json @@ -0,0 +1,197 @@ +[ + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "ÖVP", + "slug": null + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "list": "Landesparteiliste Burgenland", + "party": "ÖVP", + "preference_votes": 3937, + "rank": 1 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "Landesparteiliste Burgenland:ÖVP:1", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Landesparteiliste Burgenland ÖVP, Platz 1: 3937 Vorzugsstimmen", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, Vorzugsstimmen", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_landes_undregionalbewerber_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "preference-votes", + "nrw2024", + "Landesparteiliste Burgenland", + "ÖVP", + 1, + "Christoph Zarits" + ], + "occurred_at": "2024-09-29", + "org": null, + "person": { + "__type__": "PersonRef", + "hints": { + "birth_year": 1980, + "no_name_match": true + }, + "ids": {}, + "name": "Christoph Zarits" + }, + "review": false, + "sensitive": false, + "summary_de": "3.937 Vorzugsstimmen bei der Nationalratswahl 2024 (Landesparteiliste Burgenland ÖVP, Platz 1)", + "summary_en": "3,937 preference votes in the 2024 National Council election (ÖVP, Landesparteiliste Burgenland, position 1)", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "ÖVP", + "slug": null + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "list": "Landesparteiliste Burgenland", + "party": "ÖVP", + "preference_votes": 2734, + "rank": 2 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "Landesparteiliste Burgenland:ÖVP:2", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Landesparteiliste Burgenland ÖVP, Platz 2: 2734 Vorzugsstimmen", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, Vorzugsstimmen", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_landes_undregionalbewerber_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "preference-votes", + "nrw2024", + "Landesparteiliste Burgenland", + "ÖVP", + 2, + "Nikolaus Berlakovich" + ], + "occurred_at": "2024-09-29", + "org": null, + "person": { + "__type__": "PersonRef", + "hints": { + "birth_year": 1961, + "no_name_match": true + }, + "ids": {}, + "name": "Nikolaus Berlakovich" + }, + "review": false, + "sensitive": false, + "summary_de": "2.734 Vorzugsstimmen bei der Nationalratswahl 2024 (Landesparteiliste Burgenland ÖVP, Platz 2)", + "summary_en": "2,734 preference votes in the 2024 National Council election (ÖVP, Landesparteiliste Burgenland, position 2)", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "ÖVP", + "slug": null + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "list": "Regionalparteiliste Burgenland Nord", + "party": "ÖVP", + "preference_votes": 7361, + "rank": 1 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "Regionalparteiliste Burgenland Nord:ÖVP:1", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Regionalparteiliste Burgenland Nord ÖVP, Platz 1: 7361 Vorzugsstimmen", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, Vorzugsstimmen", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_landes_undregionalbewerber_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "preference-votes", + "nrw2024", + "Regionalparteiliste Burgenland Nord", + "ÖVP", + 1, + "Christoph Zarits" + ], + "occurred_at": "2024-09-29", + "org": null, + "person": { + "__type__": "PersonRef", + "hints": { + "birth_year": 1980, + "no_name_match": true + }, + "ids": {}, + "name": "Christoph Zarits" + }, + "review": false, + "sensitive": false, + "summary_de": "7.361 Vorzugsstimmen bei der Nationalratswahl 2024 (Regionalparteiliste Burgenland Nord ÖVP, Platz 1)", + "summary_en": "7,361 preference votes in the 2024 National Council election (ÖVP, Regionalparteiliste Burgenland Nord, position 1)", + "topics": [], + "valid_from": null, + "valid_to": null + } +] diff --git a/backend/tests/fixtures/bmi.elections/preferences_land.request.json b/backend/tests/fixtures/bmi.elections/preferences_land.request.json new file mode 100644 index 0000000..28bf794 --- /dev/null +++ b/backend/tests/fixtures/bmi.elections/preferences_land.request.json @@ -0,0 +1,13 @@ +{ + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/nrw2024_aufstellung_der_vorzugsstimmen_der_landes_undregionalbewerber_16102024.xlsx", + "kind": "preferences", + "method": "GET", + "params": null, + "json": null, + "meta": { + "election": "nrw2024", + "layout": "land" + }, + "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "body_file": "preferences_land.xlsx" +} \ No newline at end of file diff --git a/backend/tests/fixtures/bmi.elections/preferences_land.xlsx b/backend/tests/fixtures/bmi.elections/preferences_land.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..752ccbc73a6374032390068fd29e11af03fdf5b6 GIT binary patch literal 5211 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 literal 0 HcmV?d00001 diff --git a/backend/tests/fixtures/bmi.elections/result_nrw2024.expected.json b/backend/tests/fixtures/bmi.elections/result_nrw2024.expected.json new file mode 100644 index 0000000..01c7698 --- /dev/null +++ b/backend/tests/fixtures/bmi.elections/result_nrw2024.expected.json @@ -0,0 +1,398 @@ +[ + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": true, + "ids": {}, + "kind": "other", + "name": null, + "short_name": null, + "slug": "nationalrat" + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "electorate": 6346059, + "party": "ÖVP", + "percent": 26.26, + "region": "Österreich", + "region_code": "G00000", + "valid_votes": 4882888, + "votes": 1282734 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "G00000:ÖVP", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Österreich | ÖVP | 1282734 | 4882888 gültige", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, endgültiges Ergebnis", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/endgueltiges_ergebnis_beschluss_bundeswahlbehoerde_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "election", + "nrw2024", + "G00000", + "ÖVP" + ], + "occurred_at": "2024-09-29", + "org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "ÖVP", + "slug": null + }, + "person": null, + "review": false, + "sensitive": false, + "summary_de": "ÖVP: 26,26 % (1.282.734 Stimmen) bei der Nationalratswahl 2024 – Österreich", + "summary_en": "ÖVP: 26.26 % (1,282,734 votes) in the 2024 National Council election – Österreich", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": true, + "ids": {}, + "kind": "other", + "name": null, + "short_name": null, + "slug": "nationalrat" + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "electorate": 6346059, + "party": "SPÖ", + "percent": 21.13, + "region": "Österreich", + "region_code": "G00000", + "valid_votes": 4882888, + "votes": 1032233 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "G00000:SPÖ", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Österreich | SPÖ | 1032233 | 4882888 gültige", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, endgültiges Ergebnis", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/endgueltiges_ergebnis_beschluss_bundeswahlbehoerde_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "election", + "nrw2024", + "G00000", + "SPÖ" + ], + "occurred_at": "2024-09-29", + "org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "SPÖ", + "slug": null + }, + "person": null, + "review": false, + "sensitive": false, + "summary_de": "SPÖ: 21,13 % (1.032.233 Stimmen) bei der Nationalratswahl 2024 – Österreich", + "summary_en": "SPÖ: 21.13 % (1,032,233 votes) in the 2024 National Council election – Österreich", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": true, + "ids": {}, + "kind": "other", + "name": null, + "short_name": null, + "slug": "nationalrat" + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "electorate": 6346059, + "party": "FPÖ", + "percent": 28.84, + "region": "Österreich", + "region_code": "G00000", + "valid_votes": 4882888, + "votes": 1408512 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "G00000:FPÖ", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Österreich | FPÖ | 1408512 | 4882888 gültige", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, endgültiges Ergebnis", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/endgueltiges_ergebnis_beschluss_bundeswahlbehoerde_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "election", + "nrw2024", + "G00000", + "FPÖ" + ], + "occurred_at": "2024-09-29", + "org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "FPÖ", + "slug": null + }, + "person": null, + "review": false, + "sensitive": false, + "summary_de": "FPÖ: 28,84 % (1.408.512 Stimmen) bei der Nationalratswahl 2024 – Österreich", + "summary_en": "FPÖ: 28.84 % (1,408,512 votes) in the 2024 National Council election – Österreich", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": true, + "ids": {}, + "kind": "other", + "name": null, + "short_name": null, + "slug": "nationalrat" + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "electorate": 233738, + "party": "ÖVP", + "percent": 28.55, + "region": "Burgenland", + "region_code": "G10000", + "valid_votes": 190099, + "votes": 54287 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "G10000:ÖVP", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Burgenland | ÖVP | 54287 | 190099 gültige", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, endgültiges Ergebnis", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/endgueltiges_ergebnis_beschluss_bundeswahlbehoerde_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "election", + "nrw2024", + "G10000", + "ÖVP" + ], + "occurred_at": "2024-09-29", + "org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "ÖVP", + "slug": null + }, + "person": null, + "review": false, + "sensitive": false, + "summary_de": "ÖVP: 28,55 % (54.287 Stimmen) bei der Nationalratswahl 2024 – Burgenland", + "summary_en": "ÖVP: 28.55 % (54,287 votes) in the 2024 National Council election – Burgenland", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": true, + "ids": {}, + "kind": "other", + "name": null, + "short_name": null, + "slug": "nationalrat" + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "electorate": 233738, + "party": "SPÖ", + "percent": 27.02, + "region": "Burgenland", + "region_code": "G10000", + "valid_votes": 190099, + "votes": 51374 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "G10000:SPÖ", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Burgenland | SPÖ | 51374 | 190099 gültige", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, endgültiges Ergebnis", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/endgueltiges_ergebnis_beschluss_bundeswahlbehoerde_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "election", + "nrw2024", + "G10000", + "SPÖ" + ], + "occurred_at": "2024-09-29", + "org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "SPÖ", + "slug": null + }, + "person": null, + "review": false, + "sensitive": false, + "summary_de": "SPÖ: 27,02 % (51.374 Stimmen) bei der Nationalratswahl 2024 – Burgenland", + "summary_en": "SPÖ: 27.02 % (51,374 votes) in the 2024 National Council election – Burgenland", + "topics": [], + "valid_from": null, + "valid_to": null + }, + { + "__type__": "Claim", + "claim_type": "election_result", + "counterpart_org": { + "__type__": "OrgRef", + "create": true, + "ids": {}, + "kind": "other", + "name": null, + "short_name": null, + "slug": "nationalrat" + }, + "counterpart_person": null, + "data": { + "election": "nrw2024", + "electorate": 233738, + "party": "FPÖ", + "percent": 28.78, + "region": "Burgenland", + "region_code": "G10000", + "valid_votes": 190099, + "votes": 54718 + }, + "evidence": [ + { + "__type__": "Evidence", + "confidence": 1.0, + "doc_type": "dataset_row", + "excerpt": null, + "full_text": null, + "locator": "G10000:FPÖ", + "meta": {}, + "published_at": "2024-09-29", + "quote": "Burgenland | FPÖ | 54718 | 190099 gültige", + "source": null, + "stance": "supports", + "title": "BMI: Nationalratswahl 2024, endgültiges Ergebnis", + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/endgueltiges_ergebnis_beschluss_bundeswahlbehoerde_16102024.xlsx" + } + ], + "facet": null, + "key": [ + "election", + "nrw2024", + "G10000", + "FPÖ" + ], + "occurred_at": "2024-09-29", + "org": { + "__type__": "OrgRef", + "create": false, + "ids": {}, + "kind": "party", + "name": null, + "short_name": "FPÖ", + "slug": null + }, + "person": null, + "review": false, + "sensitive": false, + "summary_de": "FPÖ: 28,78 % (54.718 Stimmen) bei der Nationalratswahl 2024 – Burgenland", + "summary_en": "FPÖ: 28.78 % (54,718 votes) in the 2024 National Council election – Burgenland", + "topics": [], + "valid_from": null, + "valid_to": null + } +] diff --git a/backend/tests/fixtures/bmi.elections/result_nrw2024.request.json b/backend/tests/fixtures/bmi.elections/result_nrw2024.request.json new file mode 100644 index 0000000..20a5ccd --- /dev/null +++ b/backend/tests/fixtures/bmi.elections/result_nrw2024.request.json @@ -0,0 +1,12 @@ +{ + "url": "https://www.bmi.gv.at/412/nationalratswahlen/nationalratswahl_2024/files/endgueltiges_ergebnis_beschluss_bundeswahlbehoerde_16102024.xlsx", + "kind": "result", + "method": "GET", + "params": null, + "json": null, + "meta": { + "election": "nrw2024" + }, + "content_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet", + "body_file": "result_nrw2024.xlsx" +} \ No newline at end of file diff --git a/backend/tests/fixtures/bmi.elections/result_nrw2024.xlsx b/backend/tests/fixtures/bmi.elections/result_nrw2024.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..aaea51aafa7ddde5c22d0dd72d0cce8fed82fa3b GIT binary patch literal 5311 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 = { wiki_edit: 'pencil', declaration: 'document', ownership: 'link', - affiliation: 'link' + affiliation: 'link', + election_result: 'vote' }; export async function TypeLabel({type}: {type: ClaimType}) { diff --git a/web/src/lib/api-types.ts b/web/src/lib/api-types.ts index bf60ca8..de91c70 100644 --- a/web/src/lib/api-types.ts +++ b/web/src/lib/api-types.ts @@ -22,7 +22,7 @@ export type CalendarEvent = { export type Claim = { id: number; - claim_type: 'role' | 'vote' | 'donation' | 'money_flow' | 'statement' | 'appearance' | 'meeting' | 'travel' | 'side_job' | 'legal_proceeding' | 'lobbying' | 'media_mention' | 'career' | 'parliamentary_question' | 'government_decision' | 'wiki_edit' | 'declaration' | 'ownership' | 'affiliation' | 'party_finance' | 'other'; + claim_type: 'role' | 'vote' | 'donation' | 'money_flow' | 'statement' | 'appearance' | 'meeting' | 'travel' | 'side_job' | 'legal_proceeding' | 'lobbying' | 'media_mention' | 'career' | 'parliamentary_question' | 'government_decision' | 'wiki_edit' | 'declaration' | 'ownership' | 'affiliation' | 'party_finance' | 'election_result' | 'other'; summary_de: string; summary_en: string | null; occurred_at: string | null;