# urlaub.perfekt-ohne-en.de — 3-Ebenen-Zugsuche (2026-08-09) Referenz für mehrstufige, DB-gestützte Navigations-Web-App. Gelernt beim Bau einer 3-Ebenen-Zugziele-Suche (Radevormwald → 100k → 50k/25k → 25k/10k). ## Architektur (endgültig) - Flask + SQLite (`/opt/urlaub/urlaub.db`), Routen: `/`, `/2/`, `/3/`, `/pdf/`, `/dbauskunft//` - Templates: `ebene.html` (Kategorien als `selectattr`), `pdf.html`, `leer.html` - systemd `urlaub.service` + nginx reverse_proxy auf `127.0.0.1:8040` - PDF on-demand via `wkhtmltopdf` (ein `/tmp/urlaub-html/*.html` je Stadt) ## DB-Deep-Link — DEAD ENDS (wichtig, verifiziert 08.08) Nutzer-Wunsch: ein Button, der bahn.de mit vorbefüllter Suche öffnet. **Alle Deep-Link-Formate sind kaputt/nicht zuverlässig:** - `reiseauskunft.bahn.de` → **toter DNS** (`DNS_PROBE_FAILED_NXDOMAIN`). Sub-Agent "bestätigte" dieses Format aus altem Wissen — **musste live getestet werden**. - `reiseauskunft.de/bin/query.exe/dn?...` → toter Pfad ("Seite existiert nicht mehr") - `www.bahn.de/buchung/fahrplan/suche?von=..&nach=..&..` → SPA übernimmt Query-Parameter NICHT - `#query=start:X;destination:Y` → fragil, übernimmt nicht zuverlässig **Lösung:** ehrliche Zwischenseite — zeigt Start/Ziel (readonly), Button + Fallback-Link auf `https://www.bahn.de/buchung/fahrplan/suche` (verifiziert HTTP 200). Nutzer trägt Start/Ziel manuell ein (paar Sekunden). Kein fabrizierter zuverlässiger Deep-Link. ## Daten-Pitfalls - **Sortier-Filter-Bug:** `sorted(nach Distanz)[:N]` → nur nächste Ziele, alle fernen 5-7h/>7h-Kategorien "verschwinden". Fix: je Kategorie (unter5h/5-7h/über7h) die nächsten N. - **Kategorie aus berechneter Zeit** ableiten (`h=d/70`, kategorisiere auf h), nicht aus Distanz-Bandbreite — sonst Label widerspricht Zeit. - **Platzhalter-Einwohner (alle 150000)** waren als "keine echten kleinen Städte" erkennbar. Fix: echte Werte aus Referenzliste. - Schwellen-Umbau im Nutzer-Konsens: E2 = 25k, E3 = 10k (nicht 50k/25k). Anzahl Städte ~ ≥100k:80, ≥50k:~180-200, ≥25k:~400-450, ≥10k:~900-1100. Diese "mehr Städte bei tieferer Schwelle"-Erwartung des Nutzers war korrekt. ## Abdeckung/Deep-Links - Jede Ebene-Ziel sollte klickbar zur nächsten Ebene sein; bei finaler Ebene (z.B. 3) dürfen Links nicht auf nicht-existente `/4/` zeigen (wäre 404-Dangling) — Endpunkt als `(final)` markieren. - Umlaute in URLs: slug erzeugt `osnabrueck` via ae/oe/ue/ss-Transkription; Route muss MEHRERE Kandidaten durchprobieren (`wuerzburg`, `würzburg`, `wurzburg`) inkl. umgekehrte Richtung (`koln` → `Köln`). Wir praxis: `LOWER(replace(nach,' ',''))`-Vergleich. ## Mengen (zur Orientierung) - Ebene1: 33 Ziele (100k+). Ebene2 (25k): ~1946. Ebene3 (10k): ~10222. (Nach Kategorien-Balance.)