Features: - GWÖ-Matrix 2.0 Analyse für NRW-Landtagsanträge - Verbesserungsvorschläge im Redline-Format (Original/Vorschlag/Begründung) - Wahlprogramm- und Parteiprogrammtreue-Bewertung - Landtag-Suche via OPAL-API - Tag-Wolke mit Multi-Select Filter - Partei-Filter mit Durchschnittswerten - PDF-Report-Generierung - Security Headers (CSP, X-Frame-Options, etc.) - Persistente SQLite-DB via Docker Volumes Tech Stack: - FastAPI + Jinja2 - Qwen LLM via DashScope API - SQLite + aiosqlite - WeasyPrint für PDF - Docker Compose mit Traefik
168 lines
5.2 KiB
Python
168 lines
5.2 KiB
Python
"""Python types ported from TypeScript types.ts — GWÖ-Matrix 2.0 für Gemeinden."""
|
||
|
||
from __future__ import annotations
|
||
|
||
from enum import Enum
|
||
from typing import Optional
|
||
|
||
from pydantic import BaseModel, Field
|
||
|
||
|
||
# --- Enums ---
|
||
|
||
class Empfehlung(str, Enum):
|
||
ABLEHNEN = "Ablehnen"
|
||
UEBERARBEITEN = "Überarbeiten"
|
||
UNTERSTUETZEN_MIT = "Unterstützen mit Änderungen"
|
||
UNEINGESCHRAENKT = "Uneingeschränkt unterstützen"
|
||
|
||
|
||
class EmpfehlungSymbol(str, Enum):
|
||
X = "[X]"
|
||
BANG = "[!]"
|
||
PLUS = "[+]"
|
||
DPLUS = "[++]"
|
||
|
||
|
||
class Verbesserungspotenzial(str, Enum):
|
||
GERING = "gering"
|
||
MITTEL = "mittel"
|
||
HOCH = "hoch"
|
||
FUNDAMENTAL = "fundamental"
|
||
|
||
|
||
# --- Sub-models ---
|
||
|
||
class MatrixEntry(BaseModel):
|
||
field: str = Field(..., pattern=r"^[A-E][1-5]$")
|
||
label: str
|
||
aspect: str
|
||
rating: int = Field(..., ge=-5, le=5) # Neue Skala: -5 bis +5
|
||
symbol: Optional[str] = None
|
||
|
||
|
||
class Zitat(BaseModel):
|
||
text: str
|
||
quelle: str
|
||
url: Optional[str] = None
|
||
|
||
|
||
class ProgrammScore(BaseModel):
|
||
score: float = Field(..., ge=0, le=10)
|
||
begruendung: str = Field(..., alias="begründung")
|
||
zitate: list[Zitat] = Field(default_factory=list)
|
||
|
||
model_config = {"populate_by_name": True}
|
||
|
||
|
||
class FraktionScores(BaseModel):
|
||
fraktion: str
|
||
ist_antragsteller: Optional[bool] = Field(None, alias="istAntragsteller")
|
||
ist_regierung: Optional[bool] = Field(None, alias="istRegierung")
|
||
wahlprogramm: ProgrammScore
|
||
parteiprogramm: ProgrammScore
|
||
|
||
model_config = {"populate_by_name": True}
|
||
|
||
|
||
class Verbesserung(BaseModel):
|
||
original: str
|
||
vorschlag: str
|
||
begruendung: str
|
||
|
||
|
||
# --- Main Assessment ---
|
||
|
||
class Assessment(BaseModel):
|
||
drucksache: str
|
||
title: str
|
||
fraktionen: list[str]
|
||
datum: str
|
||
link: Optional[str] = None
|
||
|
||
gwoe_score: float = Field(..., ge=0, le=10, alias="gwoeScore")
|
||
gwoe_begruendung: str = Field(..., alias="gwoeBegründung")
|
||
gwoe_matrix: list[MatrixEntry] = Field(..., alias="gwoeMatrix")
|
||
gwoe_schwerpunkt: list[str] = Field(..., alias="gwoeSchwerpunkt")
|
||
|
||
wahlprogramm_scores: list[FraktionScores] = Field(..., alias="wahlprogrammScores")
|
||
|
||
verbesserungen: list[Verbesserung] = []
|
||
|
||
staerken: list[str] = Field(default_factory=list, alias="stärken")
|
||
schwaechen: list[str] = Field(default_factory=list, alias="schwächen")
|
||
empfehlung: Empfehlung
|
||
empfehlung_symbol: Optional[str] = Field(None, alias="empfehlungSymbol")
|
||
verbesserungspotenzial: Verbesserungspotenzial
|
||
|
||
themen: list[str] = []
|
||
antrag_zusammenfassung: Optional[str] = Field(None, alias="antragZusammenfassung")
|
||
antrag_kernpunkte: Optional[list[str]] = Field(None, alias="antragKernpunkte")
|
||
|
||
model_config = {"populate_by_name": True}
|
||
|
||
|
||
# --- Matrix constants ---
|
||
|
||
MATRIX_LABELS: dict[str, str] = {
|
||
"A1": "Grundrechtsschutz und Menschenwürde in der Lieferkette",
|
||
"A2": "Nutzen für die Gemeinde",
|
||
"A3": "Ökologische Verantwortung für die Lieferkette",
|
||
"A4": "Soziale Verantwortung für die Lieferkette",
|
||
"A5": "Öffentliche Rechenschaft und Mitsprache",
|
||
"B1": "Ethisches Finanzgebaren / Geld und Mensch",
|
||
"B2": "Gemeinnutz im Finanzgebaren",
|
||
"B3": "Ökologische Verantwortung der Finanzpolitik",
|
||
"B4": "Soziale Verantwortung der Finanzpolitik",
|
||
"B5": "Rechenschaft und Partizipation in der Finanzpolitik",
|
||
"C1": "Individuelle Rechts- und Gleichstellung",
|
||
"C2": "Gemeinsame Zielvereinbarung für das Gemeinwohl",
|
||
"C3": "Förderung ökologischen Verhaltens",
|
||
"C4": "Gerechte Verteilung von Arbeit",
|
||
"C5": "Transparente Kommunikation und demokratische Prozesse",
|
||
"D1": "Schutz des Individuums, Rechtsgleichheit",
|
||
"D2": "Gesamtwohl in der Gemeinde",
|
||
"D3": "Ökologische Gestaltung der öffentlichen Leistung",
|
||
"D4": "Soziale Gestaltung der öffentlichen Leistung",
|
||
"D5": "Transparente Kommunikation und demokratische Einbindung",
|
||
"E1": "Gestaltung der Bedingungen für ein menschenwürdiges Leben – zukünftige Generationen",
|
||
"E2": "Beitrag zum Gesamtwohl",
|
||
"E3": "Verantwortung für ökologische Auswirkungen",
|
||
"E4": "Beitrag zum sozialen Ausgleich",
|
||
"E5": "Transparente und demokratische Mitbestimmung",
|
||
}
|
||
|
||
ROW_LABELS: dict[str, str] = {
|
||
"A": "Ausgelagerte Betriebe, Lieferant:innen, Dienstleister:innen",
|
||
"B": "Finanzpartner:innen, Geldgeber:innen, Steuerzahler:innen",
|
||
"C": "Politische Führung, Verwaltung, Ehrenamtliche",
|
||
"D": "Bürger:innen und Wirtschaft",
|
||
"E": "Staat, Gesellschaft und Natur",
|
||
}
|
||
|
||
COL_LABELS = [
|
||
"Menschenwürde",
|
||
"Solidarität",
|
||
"Ökologische Nachhaltigkeit",
|
||
"Soziale Gerechtigkeit",
|
||
"Transparenz & Demokratie",
|
||
]
|
||
|
||
COL_STAATSPRINZIPIEN = [
|
||
"Rechtsstaatsprinzip",
|
||
"Gemeinnutz",
|
||
"Umwelt-Verantwortung",
|
||
"Sozialstaatsprinzip",
|
||
"Demokratie",
|
||
]
|
||
|
||
MATRIX_VERSION = "2.0"
|
||
MATRIX_TITLE = "Matrix 2.0 für Gemeinden"
|
||
|
||
EMPFEHLUNG_CONFIG: dict[str, dict] = {
|
||
"Ablehnen": {"symbol": "[X]", "color": "#d00000", "css_class": "empf-ablehnen"},
|
||
"Überarbeiten": {"symbol": "[!]", "color": "#F7941D", "css_class": "empf-ueberarbeiten"},
|
||
"Unterstützen mit Änderungen": {"symbol": "[+]", "color": "#009da5", "css_class": "empf-unterstuetzen"},
|
||
"Uneingeschränkt unterstützen": {"symbol": "[++]", "color": "#889e33", "css_class": "empf-voll"},
|
||
}
|