Files
Trainpulse/trainpulse/realtime.py
T
Claude 2a4962c379 WIP: Deutschland als zweite Datenquelle (gtfs.de)
- mehrere Quellen (ch, de) mit je eigenem Fahrplan und Echtzeit-Feed
- OSM CH+DE, mit osmium auf Schienennetz gefiltert, für pfaedle
- Landesgrenze CH gegen doppelte grenzüberschreitende Züge
- Import robust gegen andere Spaltenreihenfolge (gtfs.de)
- Kacheln für CH+DE (Zoom 14)

Noch nicht vollständig getestet (pfaedle auf Deutschland läuft).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X48pE4bzjHej8w9HyZXCjb
2026-10-21 21:27:54 +02:00

44 lines
1.6 KiB
Python

"""GTFS-RT Trip Updates von opentransportdata.swiss holen und auf das Nötigste eindampfen."""
import httpx
from google.transit import gtfs_realtime_pb2 as pb
from . import config as C
CANCELED = pb.TripDescriptor.CANCELED
def parse(data: bytes, known_trips: dict):
"""-> (feed_version, {(trip_id, start_date): (ausgefallen, [(seq, ank, ank_absolut, abf, abf_absolut), ...])})
ank/abf sind Verspätung in s, oder Unix-Zeit wenn *_absolut; None = keine Angabe."""
feed = pb.FeedMessage()
feed.ParseFromString(data)
out = {}
for e in feed.entity:
if not e.HasField("trip_update"):
continue
tu = e.trip_update
if tu.trip.trip_id not in known_trips:
continue # nicht Bahn
upd = []
for s in tu.stop_time_update:
ev = []
for name in ("arrival", "departure"):
if s.HasField(name):
x = getattr(s, name)
ev += [x.time, True] if x.HasField("time") else [x.delay, False] if x.HasField("delay") else [None, False]
else:
ev += [None, False]
upd.append((s.stop_sequence, *ev))
out[(tu.trip.trip_id, tu.trip.start_date)] = (tu.trip.schedule_relationship == CANCELED, upd)
version = feed.header.feed_version if feed.header.HasField("feed_version") else None
return version, out
def fetch(url: str, key: str = "") -> bytes:
headers = {"User-Agent": C.USER_AGENT}
if key:
headers["Authorization"] = f"Bearer {key}"
r = httpx.get(url, follow_redirects=True, timeout=30, headers=headers)
r.raise_for_status()
return r.content