From 03d2a53961a1f27b6e9c3a68c92f2a3314fd6411 Mon Sep 17 00:00:00 2001 From: Edward Betts Date: Thu, 21 May 2026 18:40:11 +0100 Subject: [PATCH] Stream GWR walk-on fares client-side instead of blocking page render Walk-on fares are now always fetched in the browser via WALKON_API_URLS rather than synchronously on the server. This means the page renders immediately with timetable and Eurostar prices, and NR fares fill in shortly after without delaying the initial load. Co-Authored-By: Claude Sonnet 4.6 --- app.py | 17 +---------------- templates/results.html | 13 ++++++++++++- tests/test_app.py | 7 +++---- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/app.py b/app.py index ed9ed86..6d3c4c2 100644 --- a/app.py +++ b/app.py @@ -497,22 +497,7 @@ def _results(station_crs, slug, travel_date, journey_type, return_date): ) fare_direction = "to_paddington" if direction == "outbound" else "from_paddington" - gwr_fares = get_cached(gwr_cache_key, ttl=30 * 24 * 3600) - if gwr_fares is not None: - from_cache_parts.append(gwr_cache_key) - elif nr_provisional or es_provisional: - gwr_fares = {} - else: - gwr_fares = cached_fetch( - gwr_cache_key, - 30 * 24 * 3600, - ( - (lambda: gwr_fares_scraper.fetch(station_crs, section_date)) - if fare_direction == "to_paddington" - else (lambda: gwr_fares_scraper.fetch(station_crs, section_date, direction=fare_direction)) - ), - "GWR fares", - ) + gwr_fares = {} cached_advance = get_cached(advance_cache_key, ttl=24 * 3600) if direction == "outbound": diff --git a/templates/results.html b/templates/results.html index 2b39f46..e98d6f4 100644 --- a/templates/results.html +++ b/templates/results.html @@ -351,9 +351,21 @@ }); } + function loadWalkonFares() { + for (var sectionId in WALKON_API_URLS) { + (function(id, url) { + fetch(url).then(function(r) { return r.json(); }).then(function(fares) { + mergeWalkonFares(id, fares); + updateDisplay(); + }); + })(sectionId, WALKON_API_URLS[sectionId]); + } + } + function initialiseResultsPage() { if (currentNrClass === 'advance_std' || currentNrClass === 'advance_1st') loadMissingAdvanceFares(); updateDisplay(); + loadWalkonFares(); startTimetableRefresh(); } @@ -500,7 +512,6 @@ {% set row_class = '' %} {% endif %} {% if row.row_type == 'trip' %} {% if section.direction == 'inbound' %} diff --git a/tests/test_app.py b/tests/test_app.py index 13cdae4..74d2bfa 100644 --- a/tests/test_app.py +++ b/tests/test_app.py @@ -374,11 +374,10 @@ def test_results_shows_eurostar_price_and_total(monkeypatch): html = resp.get_data(as_text=True) assert resp.status_code == 200 - assert '£59' in html # Eurostar Standard price - assert '£138.70' in html # Walk-on price shown in NR cell - # Total (£197.70) is computed client-side; verify data attributes carry the right values - assert 'data-walkon="138.7"' in html + assert '£59' in html # Eurostar Standard price in initial render + assert '£138.70' not in html # Walk-on price is streamed, not server-rendered assert 'data-es-std="59"' in html + assert '/api/walkon_fares/BRI/' in html # client will fetch walk-on fares def test_results_uses_unique_row_keys_for_same_eurostar(monkeypatch):