Add Circle Line timetable info.

This commit is contained in:
Edward Betts 2026-04-04 12:58:19 +01:00
parent cdee44ea3f
commit 60674fe663
2 changed files with 32 additions and 0 deletions

View file

@ -150,6 +150,7 @@
</td> </td>
<td class="col-transfer" style="padding:0.6rem 0.8rem;color:#4a5568;white-space:nowrap"> <td class="col-transfer" style="padding:0.6rem 0.8rem;color:#4a5568;white-space:nowrap">
{{ row.connection_duration }}{% if row.connection_minutes < 80 %} <span title="Tight connection">⚠️</span>{% endif %} {{ row.connection_duration }}{% if row.connection_minutes < 80 %} <span title="Tight connection">⚠️</span>{% endif %}
<br><span style="font-size:0.75rem;color:#718096">Circle {{ row.circle_line_depart }} → STP {{ row.circle_arrive_checkin }}</span>
</td> </td>
<td style="padding:0.6rem 0.8rem;font-weight:600"> <td style="padding:0.6rem 0.8rem;font-weight:600">
{{ row.depart_st_pancras }} {{ row.depart_st_pancras }}

View file

@ -9,6 +9,12 @@ MAX_GWR_MINUTES = 110
DATE_FMT = '%Y-%m-%d' DATE_FMT = '%Y-%m-%d'
TIME_FMT = '%H:%M' TIME_FMT = '%H:%M'
# Circle Line: Paddington (H&C) → Kings Cross St Pancras
CIRCLE_LINE_MINUTES_PAST = [8, 18, 28, 38, 48, 58] # departures past each hour
CIRCLE_LINE_JOURNEY_MINUTES = 11
PAD_WALK_TO_UNDERGROUND_MINUTES = 8 # GWR platform → Paddington Underground
KX_WALK_TO_CHECKIN_MINUTES = 10 # Kings Cross St Pancras platform → St Pancras check-in
# Bristol Temple Meads → London Paddington walk-on single fares. # Bristol Temple Meads → London Paddington walk-on single fares.
# Weekday restrictions (MonFri only): # Weekday restrictions (MonFri only):
@ -45,6 +51,28 @@ def _parse_dt(date: str, time: str) -> datetime:
return datetime.strptime(f"{date} {time}", f"{DATE_FMT} {TIME_FMT}") return datetime.strptime(f"{date} {time}", f"{DATE_FMT} {TIME_FMT}")
def _next_circle_line(arrive_paddington: datetime) -> tuple[datetime, datetime]:
"""
Given GWR arrival at Paddington, return (circle_line_depart, arrive_checkin).
Walk 10 min to Paddington Underground, catch next Circle Line at :08/:18/:28/:38/:48/:58,
11 min journey to Kings Cross St Pancras, 10 min walk to St Pancras check-in.
"""
earliest_board = arrive_paddington + timedelta(minutes=PAD_WALK_TO_UNDERGROUND_MINUTES)
minute = earliest_board.minute
depart_minute = next((m for m in CIRCLE_LINE_MINUTES_PAST if m >= minute), None)
if depart_minute is None:
circle_depart = (earliest_board + timedelta(hours=1)).replace(
minute=CIRCLE_LINE_MINUTES_PAST[0], second=0, microsecond=0
)
else:
circle_depart = earliest_board.replace(minute=depart_minute, second=0, microsecond=0)
arrive_checkin = circle_depart + timedelta(
minutes=CIRCLE_LINE_JOURNEY_MINUTES + KX_WALK_TO_CHECKIN_MINUTES
)
return circle_depart, arrive_checkin
def _fmt_duration(minutes: int) -> str: def _fmt_duration(minutes: int) -> str:
h, m = divmod(minutes, 60) h, m = divmod(minutes, 60)
if h and m: if h and m:
@ -122,6 +150,7 @@ def combine_trips(
total_mins = int((arr_dest - dep_bri).total_seconds() / 60) total_mins = int((arr_dest - dep_bri).total_seconds() / 60)
ticket = cheapest_gwr_ticket(gwr['depart_bristol'], travel_date) ticket = cheapest_gwr_ticket(gwr['depart_bristol'], travel_date)
circle_depart, arrive_checkin = _next_circle_line(arr_pad)
trips.append({ trips.append({
'depart_bristol': gwr['depart_bristol'], 'depart_bristol': gwr['depart_bristol'],
'arrive_paddington': gwr['arrive_paddington'], 'arrive_paddington': gwr['arrive_paddington'],
@ -129,6 +158,8 @@ def combine_trips(
'gwr_duration': _fmt_duration(int((arr_pad - dep_bri).total_seconds() / 60)), 'gwr_duration': _fmt_duration(int((arr_pad - dep_bri).total_seconds() / 60)),
'connection_minutes': int((dep_stp - arr_pad).total_seconds() / 60), 'connection_minutes': int((dep_stp - arr_pad).total_seconds() / 60),
'connection_duration': _fmt_duration(int((dep_stp - arr_pad).total_seconds() / 60)), 'connection_duration': _fmt_duration(int((dep_stp - arr_pad).total_seconds() / 60)),
'circle_line_depart': circle_depart.strftime(TIME_FMT),
'circle_arrive_checkin': arrive_checkin.strftime(TIME_FMT),
'depart_st_pancras': es['depart_st_pancras'], 'depart_st_pancras': es['depart_st_pancras'],
'arrive_destination': es['arrive_destination'], 'arrive_destination': es['arrive_destination'],
'train_number': es.get('train_number', ''), 'train_number': es.get('train_number', ''),