gwoe-antragspruefer/app/models.py
Dotty Dotter 63de3ca20d Initial commit: GWÖ-Antragsprüfer v1.0
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
2026-03-28 22:30:24 +01:00

168 lines
5.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""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"},
}