Partijgedrag is a web application that provides insight into the voting behavior of political parties in the Dutch parliament. It was originally created by Elwin Oost, later rebuilt in TypeScript, and has now become a final implementation that runs as a single Go binary.
cmd/partijgedrag/: The CLI entry point withmigrate,ingest,sync,status,maintenance,inspect, andservesubcommands.internal/: Ingestion pipelines (Tweede Kamer OData), analysis queries, motion categorization, and the server-rendered web UI.deploy/systemd/: Unit files for running the server and a recurring sync on a plain Linux host.docker-compose.yml: The PostgreSQL database for local development.
Home, moties, stemwijzer, en de analysepagina's
Recente stemmingen, met de totalen over de hele dataset.
Alle moties, filterbaar op onderwerp en kabinetsperiode.
De stemuitslag van een motie per partij, met zetels en de besluiten eromheen.
De stemwijzer begint bij een profiel: periode, onderwerpen, en partijen.
Daarna zijn de stellingen echte moties, met de letterlijke verzoekt-tekst.
De uitslag krijgt een vast adres en is deelbaar.
Hoe vaak partijen hetzelfde stemmen.
Eén partij uitgelicht, per onderwerp.
Stemgedrag van de coalitie tegenover de oppositie.
- Go (see
go.modfor the version) - Podman or Docker, with compose
- just and lefthook for the development workflow
just install # git hooks + Go dependencies
just dev # start the database, apply migrations, serve web + APIThe server runs on http://localhost:3001. Configuration is read from environment variables; see .env.example for the defaults.
The database starts empty. Fetch parties, motions, and votes from the Tweede Kamer open data API, and categorize motions, with:
go run ./cmd/partijgedrag sync tweedekamerThe first full sync takes a while; rerunning it is incremental. See go run ./cmd/partijgedrag for all commands, including ingestion status and data-quality tooling.
The GitHub CI workflow builds a single container image; running it with serve (the default command) is all a server needs. On startup the server applies pending migrations and starts a built-in sync scheduler, so the data stays fresh without an external cron:
SYNC_INTERVAL(default1h): how oftenserveruns a fullsync tweedekamer. The first run starts one minute after boot. Set to0to disable, e.g. when scheduling sync externally instead (deploy/systemd/has a timer unit for that setup).SYNC_MOTION_VOTE_LIMIT(default250) andSYNC_MOTION_DOCUMENT_LIMIT(default500): how many motions get votes/documents backfilled per run. Pipeline advisory locks make concurrent syncs safe: an overlapping run fails fast.SYNC_MOTION_VOTE_RESYNC_GRACE(default720h): re-polls votes for motions with no terminating decision, and for decided ones until this long after that decision. A motion is normally ingested before it is voted on, so without this it keeps the zero votes it had on first sight. The window also covers late amendments; avergissingis usually filed about a week after the vote. Set to0to sync a motion's votes only once.SYNC_MOTION_DOCUMENT_RESYNC_GRACE(default2160h): retries motions that still have no bullet points, until this long after they were proposed. A published document never changes, so a successful extraction is never fetched again. Past the window a motion counts as permanently without a document, which bounds the retry set. Set to0to disable.
This project uses open data provided by the Tweede Kamer der Staten-Generaal (Dutch House of Representatives). For more information about the data sources and API documentation, visit https://opendata.tweedekamer.nl.