Add per-direction NR ticket and Eurostar class selectors for return journeys

Return pages now show separate NR/Eurostar fare class buttons for outbound
and inbound, so you can compare walk-on vs advance for each leg independently.
URL uses nr_class_out/nr_class_in/es_class_out/es_class_in params for returns;
single-direction pages keep the existing nr_class/es_class params.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Edward Betts 2026-05-21 20:17:57 +01:00
parent 8d998aad71
commit 298ce14812
2 changed files with 145 additions and 53 deletions

53
app.py
View file

@ -357,6 +357,17 @@ def _results(station_crs, slug, travel_date, journey_type, return_date):
if es_class not in VALID_ES_CLASSES:
es_class = DEFAULT_ES_CLASS
if journey_type == "return":
def _p(raw, default, valid):
return raw if raw in valid else default
nr_class_out = _p(request.args.get("nr_class_out"), DEFAULT_NR_CLASS, VALID_NR_CLASSES)
nr_class_in = _p(request.args.get("nr_class_in"), DEFAULT_NR_CLASS, VALID_NR_CLASSES)
es_class_out = _p(request.args.get("es_class_out"), DEFAULT_ES_CLASS, VALID_ES_CLASSES)
es_class_in = _p(request.args.get("es_class_in"), DEFAULT_ES_CLASS, VALID_ES_CLASSES)
else:
nr_class_out = nr_class_in = nr_class
es_class_out = es_class_in = es_class
if (
request.args.get("render") != "full"
and not (
@ -613,6 +624,15 @@ def _results(station_crs, slug, travel_date, journey_type, return_date):
else:
sections = [build_section("main", journey_type, travel_date)]
nr_classes = {}
es_classes = {}
section_directions = {}
for section in sections:
direction = section["direction"]
section_directions[section["id"]] = direction
nr_classes[section["id"]] = nr_class_out if direction == "outbound" else nr_class_in
es_classes[section["id"]] = es_class_out if direction == "outbound" else es_class_in
no_prices_note = None
all_es_prices = [
row.get("eurostar_price")
@ -646,14 +666,26 @@ def _results(station_crs, slug, travel_date, journey_type, return_date):
url_max = None if max_connection == default_max else max_connection
url_nr = None if nr_class == DEFAULT_NR_CLASS else nr_class
url_es = None if es_class == DEFAULT_ES_CLASS else es_class
common_url_args = {
"journey_type": journey_type,
"return_date": return_date,
"min_connection": url_min,
"max_connection": url_max,
"nr_class": url_nr,
"es_class": url_es,
}
if journey_type == "return":
common_url_args = {
"journey_type": journey_type,
"return_date": return_date,
"min_connection": url_min,
"max_connection": url_max,
"nr_class_out": None if nr_class_out == DEFAULT_NR_CLASS else nr_class_out,
"nr_class_in": None if nr_class_in == DEFAULT_NR_CLASS else nr_class_in,
"es_class_out": None if es_class_out == DEFAULT_ES_CLASS else es_class_out,
"es_class_in": None if es_class_in == DEFAULT_ES_CLASS else es_class_in,
}
else:
common_url_args = {
"journey_type": journey_type,
"return_date": return_date,
"min_connection": url_min,
"max_connection": url_max,
"nr_class": url_nr,
"es_class": url_es,
}
prev_results_url = _results_url(
station_crs,
slug,
@ -813,6 +845,11 @@ def _results(station_crs, slug, travel_date, journey_type, return_date):
es_class=es_class,
url_nr_class=url_nr,
url_es_class=url_es,
nr_classes=nr_classes,
es_classes=es_classes,
nr_classes_json=json.dumps(nr_classes),
es_classes_json=json.dumps(es_classes),
section_directions_json=json.dumps(section_directions),
trip_fares_json=json.dumps(trip_fares),
advance_fares_json=json.dumps(advance_fares),
walkon_api_urls_json=json.dumps(walkon_api_urls),