Show Eurostar seat availability and no-prices notice

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>
This commit is contained in:
Edward Betts 2026-04-04 14:12:54 +01:00
parent cd37f0619b
commit 05eec29b7d
4 changed files with 38 additions and 17 deletions

View file

@ -162,12 +162,13 @@ def _generate_cid() -> str:
return 'SRCH-' + ''.join(random.choices(chars, k=22))
def fetch_prices(destination: str, travel_date: str) -> dict[str, int | None]:
def fetch_prices(destination: str, travel_date: str) -> dict[str, dict]:
"""
Return Eurostar Standard prices for every departure on travel_date.
Return Eurostar Standard price and seat availability for every departure on travel_date.
Result: {depart_st_pancras: price_gbp_int_or_None}
None means the class is sold out or unavailable for that departure.
Result: {depart_st_pancras: {'price': int_or_None, 'seats': int_or_None}}
price is None when unavailable/not yet on sale; seats is the number of
Standard seats currently available for sale.
"""
dest_id = DESTINATION_STATION_IDS[destination]
headers = {
@ -196,16 +197,18 @@ def fetch_prices(destination: str, travel_date: str) -> dict[str, int | None]:
resp = requests.post(_GATEWAY_URL, json=payload, headers=headers, timeout=20)
resp.raise_for_status()
data = resp.json()
prices: dict[str, int | None] = {}
prices: dict[str, dict] = {}
journeys = data['data']['journeySearch']['outbound']['journeys']
for journey in journeys:
dep = journey['timing']['departureTime']
price = None
seats = None
for fare in journey['fares']:
if fare['classOfService']['code'] == 'STANDARD':
p = fare.get('prices')
if p and p.get('displayPrice'):
price = int(p['displayPrice'])
seats = fare.get('seats')
break
prices[dep] = price
prices[dep] = {'price': price, 'seats': seats}
return prices