Show cheapest GWR fare per journey and flag unreachable morning Eurostars
Add cheapest_gwr_ticket() to trip_planner.py encoding the SSS/SVS/SDS walk-on single restrictions for Bristol Temple Meads → Paddington: on weekdays, Super Off-Peak (£45) is valid before 05:05 or from 09:58, Off-Peak (£63.60) from 08:26, and Anytime (£138.70) covers the gap. Weekends have no restrictions. The fare is included in each trip dict and displayed in a new GWR Fare column on the results page. Also wire up find_unreachable_morning_eurostars() into the results view so early Eurostar services unreachable from Bristol appear in the table, with tests covering both features. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
b88d23a270
commit
804fcedfad
5 changed files with 428 additions and 44 deletions
|
|
@ -1,5 +1,5 @@
|
|||
import pytest
|
||||
from trip_planner import combine_trips, _fmt_duration
|
||||
from trip_planner import combine_trips, find_unreachable_morning_eurostars, _fmt_duration, cheapest_gwr_ticket
|
||||
|
||||
DATE = '2026-03-30'
|
||||
|
||||
|
|
@ -129,3 +129,88 @@ def test_connection_duration_in_trip():
|
|||
# arrive Paddington 08:45, depart St Pancras 10:01 → 1h 16m
|
||||
trips = combine_trips([GWR_FAST], [ES_PARIS], DATE)
|
||||
assert trips[0]['connection_duration'] == '1h 16m'
|
||||
|
||||
|
||||
def test_unreachable_morning_eurostars_lists_only_unreachable_morning_services():
|
||||
gwr = [
|
||||
{'depart_bristol': '07:00', 'arrive_paddington': '08:45'},
|
||||
]
|
||||
eurostar = [
|
||||
{'depart_st_pancras': '09:30', 'arrive_destination': '12:00', 'destination': 'Paris Gare du Nord', 'train_number': 'ES 9001'},
|
||||
{'depart_st_pancras': '10:15', 'arrive_destination': '13:40', 'destination': 'Paris Gare du Nord', 'train_number': 'ES 9002'},
|
||||
{'depart_st_pancras': '12:30', 'arrive_destination': '15:55', 'destination': 'Paris Gare du Nord', 'train_number': 'ES 9003'},
|
||||
]
|
||||
|
||||
unreachable = find_unreachable_morning_eurostars(gwr, eurostar, DATE)
|
||||
|
||||
assert [service['depart_st_pancras'] for service in unreachable] == ['09:30']
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# cheapest_gwr_ticket — Bristol Temple Meads → Paddington
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
# 2026-03-30 is a Monday; 2026-03-28 is a Saturday
|
||||
|
||||
def test_cheapest_ticket_weekday_super_off_peak_morning():
|
||||
# 05:00 on Monday: dep ≤ 05:04 → Super Off-Peak
|
||||
t = cheapest_gwr_ticket('05:00', '2026-03-30')
|
||||
assert t['ticket'] == 'Super Off-Peak'
|
||||
assert t['price'] == 45.00
|
||||
|
||||
def test_cheapest_ticket_weekday_anytime_window():
|
||||
# 07:00 on Monday: 05:05–08:25 → Anytime only
|
||||
t = cheapest_gwr_ticket('07:00', '2026-03-30')
|
||||
assert t['ticket'] == 'Anytime'
|
||||
assert t['price'] == 138.70
|
||||
|
||||
def test_cheapest_ticket_weekday_off_peak():
|
||||
# 08:30 on Monday: dep ≥ 08:26 but < 09:58 → Off-Peak
|
||||
t = cheapest_gwr_ticket('08:30', '2026-03-30')
|
||||
assert t['ticket'] == 'Off-Peak'
|
||||
assert t['price'] == 63.60
|
||||
|
||||
def test_cheapest_ticket_weekday_super_off_peak_late():
|
||||
# 10:00 on Monday: dep ≥ 09:58 → Super Off-Peak
|
||||
t = cheapest_gwr_ticket('10:00', '2026-03-30')
|
||||
assert t['ticket'] == 'Super Off-Peak'
|
||||
assert t['price'] == 45.00
|
||||
|
||||
def test_cheapest_ticket_boundary_super_off_peak_cutoff():
|
||||
# 05:04 is last valid minute for early Super Off-Peak
|
||||
assert cheapest_gwr_ticket('05:04', '2026-03-30')['ticket'] == 'Super Off-Peak'
|
||||
# 05:05 falls into the Anytime window (off-peak starts at 08:26)
|
||||
assert cheapest_gwr_ticket('05:05', '2026-03-30')['ticket'] == 'Anytime'
|
||||
|
||||
def test_cheapest_ticket_boundary_off_peak_start():
|
||||
assert cheapest_gwr_ticket('08:25', '2026-03-30')['ticket'] == 'Anytime'
|
||||
assert cheapest_gwr_ticket('08:26', '2026-03-30')['ticket'] == 'Off-Peak'
|
||||
|
||||
def test_cheapest_ticket_boundary_super_off_peak_resumes():
|
||||
assert cheapest_gwr_ticket('09:57', '2026-03-30')['ticket'] == 'Off-Peak'
|
||||
assert cheapest_gwr_ticket('09:58', '2026-03-30')['ticket'] == 'Super Off-Peak'
|
||||
|
||||
def test_cheapest_ticket_weekend_always_super_off_peak():
|
||||
# Saturday — no restrictions
|
||||
t = cheapest_gwr_ticket('07:00', '2026-03-28')
|
||||
assert t['ticket'] == 'Super Off-Peak'
|
||||
assert t['price'] == 45.00
|
||||
|
||||
def test_combine_trips_includes_ticket_fields():
|
||||
trips = combine_trips([GWR_FAST], [ES_PARIS], DATE)
|
||||
assert len(trips) == 1
|
||||
t = trips[0]
|
||||
assert 'ticket_name' in t
|
||||
assert 'ticket_price' in t
|
||||
assert 'ticket_code' in t
|
||||
|
||||
|
||||
def test_unreachable_morning_eurostars_returns_empty_when_morning_service_is_reachable():
|
||||
gwr = [
|
||||
{'depart_bristol': '07:00', 'arrive_paddington': '08:45'},
|
||||
]
|
||||
eurostar = [
|
||||
{'depart_st_pancras': '10:15', 'arrive_destination': '13:40', 'destination': 'Paris Gare du Nord', 'train_number': 'ES 9002'},
|
||||
]
|
||||
|
||||
assert find_unreachable_morning_eurostars(gwr, eurostar, DATE) == []
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue