mirror of
https://github.com/skoelle/mvg-departures.git
synced 2026-09-17 18:40:23 +00:00
docs
This commit is contained in:
@@ -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
|
||||
@@ -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/<owner>/<repo>: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=<name>`
|
||||
- Abfahrten: `/departures?globalId=<id>&limit=<n>&transportTypes=<type>`
|
||||
- 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)
|
||||
Reference in New Issue
Block a user