From 8506cfc83f62abdf72f8098cf5a749ae1c75d8a9 Mon Sep 17 00:00:00 2001 From: Stefan Koelle Date: Sat, 1 Aug 2026 21:21:03 +0200 Subject: [PATCH] docs --- AGENTS.md | 81 ++++++++++++++++++++++++++++++++++ SPEC.md | 127 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 208 insertions(+) create mode 100644 AGENTS.md create mode 100644 SPEC.md diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..3ca684c --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,81 @@ +# AGENTS.md — Richtlinien für KI-Agenten + +## Projekt-Kontext + +MVG Departures Monitor: FastAPI-App für Münchner U-/S-Bahn-Abfahrtsanzeige. Mobile-optimiert, Docker-fähig, Config via YAML. + +## Wichtige Dateien + +| Datei | Zweck | +|---|---| +| `app/main.py` | FastAPI-App, Endpunkte, Caching, MVG-API-Aufrufe | +| `app/config.py` | Config-Loader: YAML → Dataclasses | +| `app/templates/index.html` | Jinja2-HTML-Template (Mobile-UI) | +| `config.yaml` | Stationskonfiguration (User-Input) | +| `requirements.txt` | Python-Dependencies | +| `Dockerfile` | Container-Build | +| `.github/workflows/build.yml` | CI/CD | + +## Code-Conventions + +- **Sprache**: Englisch im Code, Deutsch in UI/Comments (nur wo nötig) +- **Typisierung**: Python Dataclasses für Config, `typing.List`, `typing.Dict` +- **Caching**: In-memory Dicts, kein externes Tool (Redis etc.) +- **Logging**: `logging.getLogger("mvg-departures")` +- **Naming**: snake_case (Python), camelCase (HTML/CSS-Klassen) +- **Keine Comments**: Nur wenn explizit angefordert + +## Entwicklung + +### Starten + +```bash +pip install -r requirements.txt +uvicorn app.main:app --reload +``` + +### Testen + +Kein Test-Framework vorhanden. Bei Änderungen: +1. Manuell testen: `curl http://localhost:8000/api/departures` +2. HTML-Visualcheck: Browser `http://localhost:8000/` +3. Docker-Build prüfen: `docker build -t mvg-departures .` + +### Linting + +Kein Linter konfiguriert. Bei Bedarf: `ruff check app/` oder `black app/` + +## Architektur-Entscheidungen + +1. **Ein-Datei-App**: `main.py` enthält alle Endpunkte und Logik → bewusst simpel gehalten +2. **Keine DB**: Alles In-Memory → Neustart = Cache-Verlust (akzeptabel) +3. **Exclude-Filter**: Whitelist-Logik (alles anzeigen AUSSER exclude_destinations) +4. **Template-Rendering**: Server-seitig via Jinja2, kein Frontend-Framework +5. **Config-Pfad**: `CONFIG_PATH` Env-Var oder `config.yaml` als Fallback + +## Typische Änderungen + +### Neue Station hinzufügen +→ Nur `config.yaml` editieren, kein Code nötig + +### Neuen Transporttyp hinzufügen +→ `TYPE_MAP` und `ICON_MAP` in `main.py` erweitern, CSS-Klasse `.icon-X` in `index.html` hinzufügen + +### API-Endpoint ändern +→ Nur `app/main.py`, Funktionen `api_departures()` oder `index()` + +### UI anpassen +→ Nur `app/templates/index.html` (inline CSS) + +## Sicherheit + +- Keine Secrets im Code +- Config.yaml kann sensible Stationsnamen enthalten → Volume-Mount, nicht ins Image +- GHCR-Token nur in CI, nie im Repo + +## Deployment-Hinweise + +- Immer `TZ=Europe/Berlin` setzen (MVG-API liefert Münchner Zeit) +- Config.yaml als Read-Only-Volume mounten +- Healthcheck auf `/healthz` verwenden +- Watchtower kompatibel: Image-Tag `latest` + SHA diff --git a/SPEC.md b/SPEC.md new file mode 100644 index 0000000..636317e --- /dev/null +++ b/SPEC.md @@ -0,0 +1,127 @@ +# SPEC.md — MVG Departures Monitor + +## 1. Überblick + +Kompakter Abfahrtsmonitor für Münchner U-Bahn- und S-Bahn-Abfahrten. Primär für mobile Nutzung optimiert. Baut auf der öffentlichen MVG-API auf. + +## 2. Architektur + +``` +mvg-departures/ +├── app/ +│ ├── __init__.py +│ ├── main.py # FastAPI-App, Endpunkte, Caching +│ ├── config.py # Config-Loader (YAML → Dataclasses) +│ └── templates/ +│ └── index.html # Jinja2-Template (mobile HTML-Ansicht) +├── config.yaml # Stationskonfiguration +├── requirements.txt # Python-Dependencies +├── Dockerfile # Multi-Stage Build (python:3.12-slim) +├── docker-compose.yml # Deployment-Beispiel +└── .github/workflows/ + └── build.yml # CI/CD: Docker-Build + GHCR-Push +``` + +## 3. Technologie-Stack + +| Komponente | Technologie | +|---|---| +| Runtime | Python 3.12 | +| Web-Framework | FastAPI | +| Templating | Jinja2 | +| API-Client | `mvg` (PyPI) | +| Config | YAML (`pyyaml`) | +| Server | Uvicorn | +| Container | Docker (python:3.12-slim) | +| CI/CD | GitHub Actions → GHCR | + +## 4. Datenfluss + +``` +config.yaml + ↓ +load_config() → AppConfig (Dataclass) + ↓ +Für jede Station: + resolve_station_id(name) → station_id (mit Memory-Cache) + ↓ +MvgApi(station_id).departures(limit, transport_types) + ↓ +Filter: exclude_destinations (exakter Stringvergleich) + ↓ +Berechnung: delay_min, format time_str + ↓ +In-memory Cache (cache_seconds) + ↓ +Response: JSON oder HTML-Template +``` + +## 5. Endpunkte + +| Route | Methode | Beschreibung | +|---|---|---| +| `/` | GET | Mobile HTML-Ansicht, Auto-Refresh alle 60s | +| `/api/departures` | GET | JSON-Liste aller gefilterten Abfahrten | +| `/healthz` | GET | Healthcheck (`{"status": "ok"}`) | + +## 6. Konfiguration (`config.yaml`) + +```yaml +refresh_seconds: 60 # Meta-Refresh-Intervall (HTML) +cache_seconds: 20 # API-Cache pro Station +departures_limit: 10 # Max. Abfahrten pro Station + +stations: + - name: "Station Name, München" + type: "UBAHN" # UBAHN | SBAHN | TRAM | BUS + exclude_destinations: # Exakter Stringvergleich + - "Zielstation" +``` + +### Filter-Logik + +- `exclude_destinations` filtert per exaktem `destination`-Stringvergleich +- Alles, was NICHT in der Liste steht, wird angezeigt +- Neue/unbekannte Ziele erscheinen automatisch → keine lautlos verschwundenen Linienänderungen + +## 7. Caching + +- **Station-ID-Cache**: Globales Dict `_station_id_cache`, lifetime=Session +- **Departures-Cache**: Pro Station+Typ, TTL=`cache_seconds` (Standard: 20s) +- Kein Persistence-Layer → Cache geht bei Neustart verloren + +## 8. UI + +- Dunkles Theme (`#111417` Background) +- Farbliche Unterscheidung: U-Bahn (blau `#005ca9`), S-Bahn (grün `#00933b`), Tram (rot `#e2001a`), Bus (grau `#55545a`) +- Anzeige: Linie, Ziel, Abfahrtszeit, Verspätung (gelb/rot), Entfall, Störungsmeldungen +- Responsive, für Smartphone-First optimiert + +## 9. Deployment + +- **Lokal**: `uvicorn app.main:app --reload` +- **Docker**: `docker run -p 8000:8000 -v $(pwd)/config.yaml:/app/config.yaml mvg-departures` +- **Production**: Docker-Image von GHCR, Config via Volume-Mount, TZ=Europe/Berlin +- **Healthcheck**: Uvicorn-Endpoint `/healthz` (30s Interval) + +## 10. CI/CD + +- Trigger: Push auf `main` +- Build: Docker-Image → `ghcr.io//:latest` + Short-SHA-Tag +- Cleanup: Behält nur letzte 4 Image-Versionen +- Voraussetzung: Repo-Actions Permissions auf "Read and write" + +## 11. MVG-API + +- Basis: `https://www.mvg.de/api/bgw-pt/v3/` +- Station-Lookup: `/locations?query=` +- Abfahrten: `/departures?globalId=&limit=&transportTypes=` +- Auth: Keine (öffentlich) + +## 12. Einschränkungen + +- Kein WebSocket/Live-Updates → nur periodischer Refresh +- Keine persistenten Daten → Cache geht bei Restart verloren +- Kein Test-Suite vorhanden +- Kein Rate-Limiting auf Client-Seite +- Ein `config.yaml` pro Deployment (kein Multi-Tenancy)