- Eurostar scraper now fetches both Standard and Plus (PLUS class code)
prices/seats in a single API call; each service dict gains plus_price
and plus_seats fields
- GWR fares scraper gains fetch_advance() which makes two sets of
paginated calls (standard advance + first-class advance) and returns
cheapest per departure; shared _run_pages() generator reduces
duplication in fetch()
- New /api/advance_fares/<station_crs>/<travel_date> endpoint returns
advance fares as JSON, cached for 24 hours
- Results page gains NR ticket selector (Walk-on / Std Advance / 1st
Advance) and Eurostar selector (Standard / Plus); total column is
JS-computed from the selected combination with cheapest/priciest
highlighting
- Load advance prices button fetches the API lazily; if advance fares
are already cached they are embedded in the page and applied on load
so the button is hidden automatically
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
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>
fetch_prices now returns {'price': ..., 'seats': ...} per departure.
Seat count (labelled "N at this price") is shown below the fare — it
reflects price-band depth rather than total remaining seats. A yellow
notice is shown when the API returns journeys but all prices are null
(tickets not yet on sale).
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Replace the Eurostar timetable link with the search URL
(eurostar.com/search/uk-en?adult=1&origin=…&destination=…&outbound=…)
so the footer links directly to the page that shows prices for the
specific date and destination.
Add a Bristol Temple Meads → Paddington departures link on RTT alongside
the existing Paddington arrivals link.
Also update "morning service unavailable" badge and tests to reflect the
removal of the morning-only cutoff filter from find_unreachable_morning_eurostars.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Fetches prices via the site-api.eurostar.com GraphQL gateway
(NewBookingSearch operation, discovered with Playwright). Adds
fetch_prices() to scraper/eurostar.py using requests, caches results,
annotates each trip with eurostar_price and total_price, and shows an
ES Std column plus total cost (duration + price) in the results table.
The Transfer column is hidden on small screens for mobile usability.
Closes#4
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>