Design concept and design system in docs/design/: principles (evidence
first, neutral, fair to the people shown, plain language, time as a
dimension, accessible and private), information architecture, core
patterns and a concept for every page; tokens with checked contrast,
component catalogue, content and accessibility rules.
web:
- neutral ink-indigo accent instead of flag red; party colours only as
small markers next to the name; light/dark theme with a switcher
- stylesheets split into tokens, base, layout, components, print
- new header (search, filter count, language, theme, mobile menu) and
footer; breadcrumbs, page headers, tabs with counts, filter bars that
collapse on phones, pagination, notices and empty states
- claim cards with source meter, legal stage, innocence notice inside
the card and a permalink; claim pages with citation (/angaben/{id})
- profiles with role timeline and key figures; party and organisation
dashboards; vote list with position chips; calendar agenda on phones;
tables that stack into cards; loading and error pages; favicon
backend:
- GET /api/v1/claims/{id} and MCP tool get_claim for permalinks
- parties list: evaluate person_status once instead of per party
(30 s -> 1 s on the sample database, identical result)
- parliament calendar: HTML-decode titles ("84. Sitzung")
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
100 lines
6.4 KiB
Markdown
100 lines
6.4 KiB
Markdown
# Politrack 🇦🇹
|
||
|
||
**What Austrian politicians do, with every claim linked to its sources.**
|
||
|
||
Politrack collects publicly available information about Austrian politicians and parties, from the EU and
|
||
federal level down to municipalities: mandates and functions, how factions and individuals voted, party
|
||
donations and finances, public contracts, government advertising, side jobs, lobby meetings, speeches,
|
||
questions and public posts, media reports, trips and meetings, and legal proceedings. Every fact is a
|
||
**claim** with one or more pieces of **evidence** (links), so readers can verify it and decide which
|
||
sources they trust.
|
||
|
||
* 🇩🇪 German UI by default (`/`), English at `/en`
|
||
* Users choose **which sources they trust** and **which topics they care about**
|
||
* Pages per **person**, **party** and **organisation**, a **money** explorer, **calendar** and **timeline**
|
||
* **Votes**: faction votes for every bill, **individual roll-call votes** with a dissent flag, and
|
||
"say vs. vote" (did the person speak on the item?)
|
||
* **Follow the money**: donations, party funding, loans and holdings, public contracts, government ads,
|
||
political ads – and the organisations a politician is connected to
|
||
* **Corroboration**: the same fact from independent sources becomes one claim with several sources
|
||
* **Former politicians**: what they are doing now
|
||
* Legal matters always carry the procedural stage and *"Es gilt die Unschuldsvermutung"*; automatically
|
||
detected ones need human review first
|
||
* **Agent-ready**: MCP server, typed REST/OpenAPI, read-only SQL role, pgvector chunks for RAG
|
||
* Runs as Docker containers on a homelab (Unraid); workers scale horizontally
|
||
* **Legal by construction**: robots.txt, rate limits, TDM reservations and storage rules are enforced in
|
||
the framework, not left to each scraper
|
||
|
||
## Documentation
|
||
|
||
| Doc | Contents |
|
||
|---|---|
|
||
| [docs/architecture/01-design-from-scratch.md](docs/architecture/01-design-from-scratch.md) | The architecture: quality goals, data model, adapter framework, serving, testing, guard-rails |
|
||
| [docs/architecture/02-comparison-and-decisions.md](docs/architecture/02-comparison-and-decisions.md) | Why it is built this way (decision records) |
|
||
| [docs/design/README.md](docs/design/README.md) | Design concept: principles, information architecture, core patterns, every page |
|
||
| [docs/design/design-system.md](docs/design/design-system.md) | Design system: tokens, components, content and accessibility rules |
|
||
| [docs/data-model.md](docs/data-model.md) | Tables, claim types, identifier schemes, views |
|
||
| [docs/sources.md](docs/sources.md) | Every source with access method, licence/ToS assessment, and what we deliberately don't collect (incl. Telegram/WhatsApp/Signal) |
|
||
| [docs/legal.md](docs/legal.md) | Copyright/TDM, GDPR, MedienG §7b (presumption of innocence), defamation |
|
||
| [docs/deployment-unraid.md](docs/deployment-unraid.md) | Step-by-step Unraid setup, backups, exposure |
|
||
| [docs/investigative-data.md](docs/investigative-data.md) | Data points for investigative work and the correlations they enable |
|
||
| [docs/market-research.md](docs/market-research.md) | Existing services and the gaps Politrack fills |
|
||
| [docs/roadmap.md](docs/roadmap.md) | What's done and what comes next |
|
||
| [CONTRIBUTING.md](CONTRIBUTING.md) | How to contribute: adapters, enrichers, API, frontend, tests |
|
||
|
||
## Quick start
|
||
|
||
```bash
|
||
cp .env.example .env # set passwords and a real contact address for the crawler
|
||
docker compose up -d --build # db, migrate, api, web, mcp, workers
|
||
docker compose exec api politrack sources list # all adapters/enrichers and their schedules
|
||
docker compose exec api politrack run parlament.persons # members, mandates, side jobs, speeches
|
||
docker compose exec api politrack run wikidata.politicians # state/local offices, parties, careers, social handles
|
||
docker compose exec api politrack run parlament.items # votes, roll calls, immunity, questions, hearings
|
||
docker compose exec api politrack run rechnungshof.donations # party donations
|
||
docker compose exec api politrack enrich # linking, events, legal detection, corroboration
|
||
docker compose exec api politrack review # approve/reject automatically detected legal matters
|
||
docker compose exec api politrack status # recent runs with stats and errors
|
||
```
|
||
|
||
The workers then keep everything fresh on their own: each adapter declares its cron schedule and queue.
|
||
|
||
* Web: http://localhost:3000
|
||
* API docs: http://localhost:8000/api/docs
|
||
* MCP (streamable HTTP): http://localhost:8001/mcp
|
||
* SQL (read-only): `postgresql://politrack_reader:<POLITRACK_READER_PASSWORD>@localhost:5433/politrack`
|
||
|
||
## Repository layout
|
||
|
||
```
|
||
backend/ Python package `politrack` (one image for API, workers, MCP)
|
||
migrations/ SQL schema (commented for agents) + seed (chambers, parties, topics)
|
||
config/ feeds.yaml, social_accounts.yaml, ip_ranges.yaml, meta_pages.yaml
|
||
politrack/
|
||
core/ the framework: SourceSpec, Adapter/Enricher, Request/Response, Fetcher
|
||
(policy + archive), records, Resolver, Loader, registry, runner
|
||
sources/ one module per publisher (parlament/, wikidata, rechnungshof, bundesregierung,
|
||
procurement, rtr, eu, wikipedia, political_ads, justiz, news, gdelt, social)
|
||
enrich/ linker, events, legal, topics, maintenance, corroborate, embed, curated
|
||
queries/ read queries shared by API and MCP
|
||
api/ FastAPI app + Pydantic response models
|
||
mcp_server.py MCP tools for agents
|
||
worker.py Procrastinate tasks generated from the registry
|
||
tests/ unit tests + golden fixtures
|
||
web/ Next.js 15 + next-intl frontend (types generated from the API)
|
||
docs/
|
||
docker-compose.yml
|
||
```
|
||
|
||
## Contributing
|
||
|
||
Adding a source is one module with a `SourceSpec`, `start()` and a pure `parse()` – see
|
||
[docs/contributing/adapters.md](docs/contributing/adapters.md) and [CONTRIBUTING.md](CONTRIBUTING.md).
|
||
|
||
## Data licence & attribution
|
||
|
||
Data from the Austrian Parliament: © Parlamentsdirektion, CC BY 4.0. Wikidata: CC0. Rechnungshof,
|
||
Bundeskanzleramt and WKStA publications are official works (§7 UrhG). Open government data from
|
||
data.gv.at and RTR under their respective licences (mostly CC BY 4.0). Media content is linked, not
|
||
reproduced.
|