Consolidate to single GraphQL call; show indirect trains; fix price formatting

Replace two-step Eurostar fetch (HTML timetable + GraphQL prices) with a
single GraphQL call that returns timing, train numbers, prices, and seats.
Support indirect services (e.g. Amsterdam) by joining multi-leg train numbers
with ' + ' and keeping the earliest arrival per departure time.
Fix half-pound prices by casting displayPrice to float instead of int.
Wrap each train number segment in white-space:nowrap so 'ES 9132 + ER 9363'
never breaks mid-segment.
Format Eurostar prices with two decimal places.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Edward Betts 2026-04-04 14:46:22 +01:00
parent 05eec29b7d
commit c22a3ea0fc
5 changed files with 182 additions and 320 deletions

30
app.py
View file

@ -9,7 +9,6 @@ from cache import get_cached, set_cached
import scraper.eurostar as eurostar_scraper
import scraper.realtime_trains as rtt_scraper
from trip_planner import combine_trips, find_unreachable_morning_eurostars
from scraper.eurostar import fetch_prices as fetch_eurostar_prices
RTT_PADDINGTON_URL = (
"https://www.realtimetrains.co.uk/search/detailed/"
@ -103,12 +102,10 @@ def results(slug, travel_date):
rtt_cache_key = f"rtt_{travel_date}"
es_cache_key = f"eurostar_{travel_date}_{destination}"
prices_cache_key = f"eurostar_prices_{travel_date}_{destination}"
cached_rtt = get_cached(rtt_cache_key)
cached_es = get_cached(es_cache_key)
cached_prices = get_cached(prices_cache_key, ttl=24 * 3600)
from_cache = bool(cached_rtt and cached_es and cached_prices)
cached_es = get_cached(es_cache_key, ttl=24 * 3600)
from_cache = bool(cached_rtt and cached_es)
error = None
@ -123,26 +120,21 @@ def results(slug, travel_date):
error = f"Could not fetch GWR trains: {e}"
if cached_es:
eurostar_trains = cached_es
eurostar_services = cached_es
else:
try:
eurostar_trains = eurostar_scraper.fetch(destination, travel_date, user_agent)
set_cached(es_cache_key, eurostar_trains)
eurostar_services = eurostar_scraper.fetch(destination, travel_date)
set_cached(es_cache_key, eurostar_services)
except Exception as e:
eurostar_trains = []
eurostar_services = []
msg = f"Could not fetch Eurostar times: {e}"
error = f"{error}; {msg}" if error else msg
if cached_prices:
eurostar_prices = cached_prices
else:
try:
eurostar_prices = fetch_eurostar_prices(destination, travel_date)
set_cached(prices_cache_key, eurostar_prices)
except Exception as e:
eurostar_prices = {}
msg = f"Could not fetch Eurostar prices: {e}"
error = f"{error}; {msg}" if error else msg
eurostar_trains = eurostar_services
eurostar_prices = {
s['depart_st_pancras']: {'price': s.get('price'), 'seats': s.get('seats')}
for s in eurostar_services
}
trips = combine_trips(gwr_trains, eurostar_trains, travel_date, min_connection, max_connection)