Schweiz: alten Fahrplan ohne Trams nicht mehr laden, nur weil die Echtzeit noch auf ihn verweist

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01X48pE4bzjHej8w9HyZXCjb
This commit is contained in:
Claude
2026-10-22 13:43:07 +02:00
parent e948a69c32
commit be0de5d619
3 changed files with 47 additions and 5 deletions
+31 -1
View File
@@ -222,8 +222,38 @@ def test_all():
assert shows("ch", both, 8.54, 47.378) and not shows("de", both, 8.54, 47.378) assert shows("ch", both, 8.54, 47.378) and not shows("de", both, 8.54, 47.378)
assert shows("de", both, 11.40, 47.26) and shows("ch", both, 11.40, 47.26) # Ausland: beide assert shows("de", both, 11.40, 47.26) and shows("ch", both, 11.40, 47.26) # Ausland: beide
assert shows("de", both, 2.355, 48.881) # Frankreich ohne eigenen Fahrplan -> DE zeigt assert shows("de", both, 2.355, 48.881) # Frankreich ohne eigenen Fahrplan -> DE zeigt
print("alle Tests ok")
def test_feed_choice():
"""Echtzeit verweist auf alten Fahrplan ohne Trams -> trotzdem den neuen (mit Trams) laden."""
from trainpulse import server
with tempfile.TemporaryDirectory() as d:
for v, rtype in (("20261017", "102"), ("20261021", "900")):
p = Path(d) / "ch" / v
p.mkdir(parents=True)
(p / "meta.json").write_text("{}")
(p / "routes.txt").write_text(f"route_id,route_type\nR,{rtype}\n")
loaded = []
class Stub:
def __init__(self, path):
loaded.append(path.name)
self.version, self.meta, self.trip_ids = path.name, {}, []
def covers(self, day):
return True
old = server.C.FEEDS_DIR, server.Feed, server.compute_positions, dict(server.S.sources)
try:
server.C.FEEDS_DIR, server.Feed, server.compute_positions = Path(d), Stub, lambda: None
server.S.sources = {"ch": server.Source("ch")}
server.S.sources["ch"].rt_version = "20261017"
server.watch_feeds()
finally:
server.C.FEEDS_DIR, server.Feed, server.compute_positions, server.S.sources = old
assert loaded == ["20261021"], loaded
if __name__ == "__main__": if __name__ == "__main__":
test_all() test_all()
test_feed_choice()
print("alle Tests ok")
+1
View File
@@ -332,6 +332,7 @@ def prepare(source: str, state: dict, zip_path: Path | None = None, force: bool
if shaped: if shaped:
pack_shapes(shaped) pack_shapes(shaped)
meta["source_file"] = name meta["source_file"] = name
meta["format"] = FORMAT
meta["prepared_at"] = int(time.time()) meta["prepared_at"] = int(time.time())
publish(source, shaped or work / "rail", meta) publish(source, shaped or work / "rail", meta)
shutil.rmtree(work, ignore_errors=True) shutil.rmtree(work, ignore_errors=True)
+15 -4
View File
@@ -16,7 +16,7 @@ from fastapi.staticfiles import StaticFiles
from . import config as C from . import config as C
from . import border, realtime from . import border, realtime
from .feed import TZ, Feed from .feed import TZ, Feed, read, route_mode
log = logging.getLogger("trainpulse") log = logging.getLogger("trainpulse")
STEP, STEPS = 2, 6 # Gleisverlauf für die nächsten 12 s (Abruf alle 5 s, Berechnung alle 3 s -> Reserve) STEP, STEPS = 2, 6 # Gleisverlauf für die nächsten 12 s (Abruf alle 5 s, Berechnung alle 3 s -> Reserve)
@@ -68,6 +68,13 @@ def loop(every: float, fn, *args):
threading.Thread(target=run, daemon=True, name=fn.__name__).start() threading.Thread(target=run, daemon=True, name=fn.__name__).start()
def feed_format(path: Path, meta: dict) -> int:
"""Aufbereitungs-Format; ältere Ordner ohne Angabe: 2, wenn Trams/U-Bahnen drin sind."""
if "format" in meta:
return meta["format"]
return 2 if any(route_mode(r[h["route_type"]]) != "rail" for h, r in read(path / "routes.txt")) else 1
def watch_feeds(): def watch_feeds():
"""Lädt je Quelle den passenden Fahrplan: den, auf den sich die Echtzeitdaten beziehen (Schweiz: """Lädt je Quelle den passenden Fahrplan: den, auf den sich die Echtzeitdaten beziehen (Schweiz:
die schalten erst um 15 Uhr auf einen neuen Fahrplan um), sonst den neusten.""" die schalten erst um 15 Uhr auf einen neuen Fahrplan um), sonst den neusten."""
@@ -77,11 +84,15 @@ def watch_feeds():
found[src.name] = sorted(m.parent.name for m in (C.FEEDS_DIR / src.name).glob("*/meta.json")) found[src.name] = sorted(m.parent.name for m in (C.FEEDS_DIR / src.name).glob("*/meta.json"))
src.on_disk = bool(found[src.name]) src.on_disk = bool(found[src.name])
for src in S.sources.values(): for src in S.sources.values():
ready = found[src.name] if not found[src.name]:
if not ready:
continue continue
metas = {v: json.loads((C.FEEDS_DIR / src.name / v / "meta.json").read_text()) for v in found[src.name]}
# nur Versionen im neusten Aufbereitungs-Format (sonst z. B. alter Fahrplan ohne Trams, nur weil
# die Echtzeit sich noch auf ihn bezieht)
fmts = {v: feed_format(C.FEEDS_DIR / src.name / v, m) for v, m in metas.items()}
ready = [v for v in found[src.name] if fmts[v] == max(fmts.values())]
want = src.rt_version if src.rt_version in ready else ready[-1] want = src.rt_version if src.rt_version in ready else ready[-1]
meta = json.loads((C.FEEDS_DIR / src.name / want / "meta.json").read_text()) meta = metas[want]
if (src.feed and src.feed.version == want and src.feed.covers(today + dt.timedelta(days=1)) if (src.feed and src.feed.version == want and src.feed.covers(today + dt.timedelta(days=1))
and src.feed.meta.get("prepared_at") == meta.get("prepared_at")): # gleiche Version neu aufbereitet? and src.feed.meta.get("prepared_at") == meta.get("prepared_at")): # gleiche Version neu aufbereitet?
continue continue