diff --git a/app.py b/app.py index 8a81ded..debc5a1 100644 --- a/app.py +++ b/app.py @@ -226,8 +226,12 @@ def results(station_crs, slug, travel_date): trip["eurostar_plus_price"] = es.get("plus_price") trip["eurostar_plus_seats"] = es.get("plus_seats") gwr_p = trip.get("ticket_price") + circle_svcs = trip.get("circle_services") + circle_fare = circle_svcs[0]["fare"] if circle_svcs else 0 trip["total_price"] = ( - gwr_p + es_price if (gwr_p is not None and es_price is not None) else None + gwr_p + es_price + circle_fare + if (gwr_p is not None and es_price is not None) + else None ) # If the API returned journeys but every price is None, tickets aren't on sale yet diff --git a/templates/results.html b/templates/results.html index 2645e3d..4c37e33 100644 --- a/templates/results.html +++ b/templates/results.html @@ -181,10 +181,10 @@ {{ row.connection_duration }}{% if row.connection_minutes < 80 %} ⚠️{% endif %} {% if row.circle_services %} {% set c = row.circle_services[0] %} -
Circle {{ c.depart }} → KX {{ c.arrive_kx }} +
Circle {{ c.depart }} → KX {{ c.arrive_kx }} · £{{ "%.2f"|format(c.fare) }} {% if row.circle_services | length > 1 %} {% set c2 = row.circle_services[1] %} -
next {{ c2.depart }} → KX {{ c2.arrive_kx }} +
next {{ c2.depart }} → KX {{ c2.arrive_kx }} · £{{ "%.2f"|format(c2.fare) }} {% endif %} {% endif %} diff --git a/tfl_fare.py b/tfl_fare.py new file mode 100644 index 0000000..0d3108e --- /dev/null +++ b/tfl_fare.py @@ -0,0 +1,30 @@ +"""TfL single fare calculations for journeys within Zone 1.""" + +from datetime import datetime, time + +import holidays + +CIRCLE_LINE_PEAK = 3.10 +CIRCLE_LINE_OFF_PEAK = 3.00 + +_ENGLAND_HOLIDAYS = holidays.country_holidays("GB", subdiv="ENG") + +_AM_PEAK_START = time(6, 30) +_AM_PEAK_END = time(9, 30) +_PM_PEAK_START = time(16, 0) +_PM_PEAK_END = time(19, 0) + + +def circle_line_fare(depart_dt: datetime) -> float: + """Return the TfL Circle line single fare for a given departure datetime. + + Peak (£3.10): Monday–Friday (excluding public holidays), + 06:30–09:30 and 16:00–19:00. + Off-peak (£3.00): all other times, weekends, and public holidays. + """ + if depart_dt.date() in _ENGLAND_HOLIDAYS or depart_dt.weekday() >= 5: + return CIRCLE_LINE_OFF_PEAK + t = depart_dt.time() + if _AM_PEAK_START <= t < _AM_PEAK_END or _PM_PEAK_START <= t < _PM_PEAK_END: + return CIRCLE_LINE_PEAK + return CIRCLE_LINE_OFF_PEAK diff --git a/trip_planner.py b/trip_planner.py index ba7948d..324a703 100644 --- a/trip_planner.py +++ b/trip_planner.py @@ -5,6 +5,7 @@ Combine GWR station→Paddington trains with Eurostar St Pancras→destination t from datetime import datetime, timedelta import circle_line +from tfl_fare import circle_line_fare MIN_CONNECTION_MINUTES = 50 MAX_CONNECTION_MINUTES = 110 @@ -31,7 +32,11 @@ def _circle_line_services(arrive_paddington: datetime) -> list[dict]: ) services = circle_line.upcoming_services(earliest_board, count=2) return [ - {"depart": dep.strftime(TIME_FMT), "arrive_kx": arr.strftime(TIME_FMT)} + { + "depart": dep.strftime(TIME_FMT), + "arrive_kx": arr.strftime(TIME_FMT), + "fare": circle_line_fare(dep), + } for dep, arr in services ]