You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2.9 KiB
2.9 KiB
System Overview
Project: Stemwijzer (Dutch Political Voting Compass)
Purpose: A web application that maps the Dutch Tweede Kamer (House of Representatives) based on real parliamentary votes, helping citizens discover which political party aligns best with their views.
Architecture Summary
Data Flow
TweedeKamer OData API
↓
API Client (api_client.py)
↓
DuckDB Database (database.py)
↓
Pipeline Processing (pipeline/)
├── fetch_mp_metadata # MP party + tenure
├── extract_mp_votes # voting_results → mp_votes
├── svd_pipeline # SVD on vote matrix + Procrustes
├── text_pipeline # AI embeddings via OpenRouter
└── fusion # Combine SVD + text vectors
↓
Streamlit Web App (app.py, pages/)
├── Home.py # Landing page
├── 1_Stemwijzer.py # Voting quiz
└── 2_Explorer.py # Political compass explorer
Key Components
| Component | Purpose | File(s) |
|---|---|---|
| Database | Motion storage, MP votes, embeddings | database.py |
| API Client | TweedeKamer OData API integration | api_client.py |
| AI Provider | OpenRouter API for embeddings/summaries | ai_provider.py |
| Pipeline | Orchestrated data processing | pipeline/run_pipeline.py |
| Analysis | SVD, clustering, trajectory computation | analysis/*.py |
| Similarity | Motion similarity search | similarity/*.py |
| Web App | Streamlit UI | app.py, pages/*.py |
Data Models
Core Entities:
Motion: Parliamentary motion with voting resultsMP/MPMetadata: Member of Parliament with party/tenureMPVote: Individual vote record (Voor/Tegen/Onthouden/Geen stem/Afwezig)Party: Political partyUserSession/UserVote: Voting session trackingSVDVector: Dimensionality-reduced vote vectorsFusedEmbedding: Combined SVD + text embeddingSimilarityCache: Pre-computed motion similarities
Technical Decisions
- DuckDB over SQLite: Chosen for OLAP performance with complex analytical queries
- ibis ORM: Database-agnostic query building (currently using DuckDB backend)
- SVD + Procrustes: Aligns voting vectors across time windows
- UMAP for visualization: Non-linear dimensionality reduction for compass display
- OpenRouter API: Abstraction layer for AI embeddings (currently using Qwen)
- Module-level singletons:
db = MotionDatabase()pattern for shared state
Key Conventions
- DuckDB connections: Short-lived per method, always close
- Error handling: Catch
Exception, return safe fallbacks (False/[]/None) - Logging: Use
logging.getLogger(__name__)- avoid print() - Type hints: Required on public functions with typing module imports
- Config: Dataclass
Configinconfig.py, accessed asfrom config import config