Add full type annotations and black formatting across all modules
Annotated all functions with mypy --strict-compatible types (-> None, dict[str, Any], Generator types, etc.), added # type: ignore for untyped third-party libs (lxml), and reformatted with black. All 18 source files now pass mypy --strict with zero errors. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
453d6244ec
commit
13c4341f3a
14 changed files with 1802 additions and 974 deletions
|
|
@ -3,6 +3,7 @@ Combine GWR station→Paddington trains with Eurostar St Pancras→destination t
|
|||
"""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from typing import Any
|
||||
|
||||
import circle_line
|
||||
from tfl_fare import circle_line_fare
|
||||
|
|
@ -15,14 +16,16 @@ DATE_FMT = "%Y-%m-%d"
|
|||
TIME_FMT = "%H:%M"
|
||||
|
||||
PAD_WALK_TO_UNDERGROUND_MINUTES = 8 # GWR platform → Paddington (H&C Line) platform
|
||||
KX_WALK_TO_UNDERGROUND_MINUTES = 10 # St Pancras arrivals → King's Cross St Pancras Underground
|
||||
KX_WALK_TO_UNDERGROUND_MINUTES = (
|
||||
10 # St Pancras arrivals → King's Cross St Pancras Underground
|
||||
)
|
||||
|
||||
|
||||
def _parse_dt(date: str, time: str) -> datetime:
|
||||
return datetime.strptime(f"{date} {time}", f"{DATE_FMT} {TIME_FMT}")
|
||||
|
||||
|
||||
def _circle_line_services(arrive_paddington: datetime) -> list[dict]:
|
||||
def _circle_line_services(arrive_paddington: datetime) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Given GWR arrival at Paddington, return up to 2 upcoming Circle line services
|
||||
as [{'depart': 'HH:MM', 'arrive_kx': 'HH:MM'}, ...].
|
||||
|
|
@ -33,7 +36,9 @@ def _circle_line_services(arrive_paddington: datetime) -> list[dict]:
|
|||
earliest_board = arrive_paddington + timedelta(
|
||||
minutes=PAD_WALK_TO_UNDERGROUND_MINUTES
|
||||
)
|
||||
services = circle_line.upcoming_services(earliest_board, count=2, direction='pad_to_kx')
|
||||
services = circle_line.upcoming_services(
|
||||
earliest_board, count=2, direction="pad_to_kx"
|
||||
)
|
||||
return [
|
||||
{
|
||||
"depart": dep.strftime(TIME_FMT),
|
||||
|
|
@ -44,24 +49,32 @@ def _circle_line_services(arrive_paddington: datetime) -> list[dict]:
|
|||
]
|
||||
|
||||
|
||||
PAD_WALK_FROM_UNDERGROUND_MINUTES = 5 # Circle line platform → GWR platform at Paddington
|
||||
INBOUND_COMFORTABLE_MIN_CONN = 40 # threshold above which we apply the platform walk buffer
|
||||
PAD_WALK_FROM_UNDERGROUND_MINUTES = (
|
||||
5 # Circle line platform → GWR platform at Paddington
|
||||
)
|
||||
INBOUND_COMFORTABLE_MIN_CONN = (
|
||||
40 # threshold above which we apply the platform walk buffer
|
||||
)
|
||||
|
||||
|
||||
def _circle_line_services_to_paddington(
|
||||
arrive_st_pancras: datetime,
|
||||
dep_paddington: datetime | None = None,
|
||||
min_conn_minutes: int = INBOUND_MIN_CONNECTION_MINUTES,
|
||||
) -> list[dict]:
|
||||
) -> list[dict[str, Any]]:
|
||||
earliest_board = arrive_st_pancras + timedelta(
|
||||
minutes=KX_WALK_TO_UNDERGROUND_MINUTES
|
||||
)
|
||||
if min_conn_minutes >= INBOUND_COMFORTABLE_MIN_CONN and dep_paddington is not None:
|
||||
cutoff = dep_paddington - timedelta(minutes=PAD_WALK_FROM_UNDERGROUND_MINUTES)
|
||||
candidates = circle_line.upcoming_services(earliest_board, count=4, direction='kx_to_pad')
|
||||
candidates = circle_line.upcoming_services(
|
||||
earliest_board, count=4, direction="kx_to_pad"
|
||||
)
|
||||
services = [(dep, arr) for dep, arr in candidates if arr <= cutoff][:2]
|
||||
else:
|
||||
services = circle_line.upcoming_services(earliest_board, count=1, direction='kx_to_pad', preceding=1)
|
||||
services = circle_line.upcoming_services(
|
||||
earliest_board, count=1, direction="kx_to_pad", preceding=1
|
||||
)
|
||||
return [
|
||||
{
|
||||
"depart": dep.strftime(TIME_FMT),
|
||||
|
|
@ -82,8 +95,8 @@ def _fmt_duration(minutes: int) -> str:
|
|||
|
||||
|
||||
def _is_viable_connection(
|
||||
gwr: dict,
|
||||
eurostar: dict,
|
||||
gwr: dict[str, Any],
|
||||
eurostar: dict[str, Any],
|
||||
travel_date: str,
|
||||
min_connection_minutes: int,
|
||||
max_connection_minutes: int,
|
||||
|
|
@ -112,8 +125,8 @@ def _is_viable_connection(
|
|||
|
||||
|
||||
def _is_viable_inbound_connection(
|
||||
eurostar: dict,
|
||||
gwr: dict,
|
||||
eurostar: dict[str, Any],
|
||||
gwr: dict[str, Any],
|
||||
travel_date: str,
|
||||
min_connection_minutes: int,
|
||||
max_connection_minutes: int,
|
||||
|
|
@ -143,13 +156,13 @@ def _is_viable_inbound_connection(
|
|||
|
||||
|
||||
def combine_trips(
|
||||
gwr_trains: list[dict],
|
||||
eurostar_trains: list[dict],
|
||||
gwr_trains: list[dict[str, Any]],
|
||||
eurostar_trains: list[dict[str, Any]],
|
||||
travel_date: str,
|
||||
min_connection_minutes: int = MIN_CONNECTION_MINUTES,
|
||||
max_connection_minutes: int = MAX_CONNECTION_MINUTES,
|
||||
gwr_fares: dict | None = None,
|
||||
) -> list[dict]:
|
||||
gwr_fares: dict[str, Any] | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""
|
||||
Return a list of valid combined trips, sorted by Bristol departure time.
|
||||
|
||||
|
|
@ -217,13 +230,13 @@ def combine_trips(
|
|||
|
||||
|
||||
def combine_inbound_trips(
|
||||
eurostar_trains: list[dict],
|
||||
gwr_trains: list[dict],
|
||||
eurostar_trains: list[dict[str, Any]],
|
||||
gwr_trains: list[dict[str, Any]],
|
||||
travel_date: str,
|
||||
min_connection_minutes: int = INBOUND_MIN_CONNECTION_MINUTES,
|
||||
max_connection_minutes: int = INBOUND_MAX_CONNECTION_MINUTES,
|
||||
gwr_fares: dict | None = None,
|
||||
) -> list[dict]:
|
||||
gwr_fares: dict[str, Any] | None = None,
|
||||
) -> list[dict[str, Any]]:
|
||||
"""Return valid continent→UK combined trips."""
|
||||
trips = []
|
||||
|
||||
|
|
@ -243,12 +256,16 @@ def combine_inbound_trips(
|
|||
total_mins = int((arr_station - dep_dest).total_seconds() / 60) + 60
|
||||
eurostar_mins = int((arr_stp - dep_dest).total_seconds() / 60) + 60
|
||||
fare = (gwr_fares or {}).get(gwr["depart_paddington"])
|
||||
circle_svcs = _circle_line_services_to_paddington(arr_stp, dep_pad, min_connection_minutes)
|
||||
circle_svcs = _circle_line_services_to_paddington(
|
||||
arr_stp, dep_pad, min_connection_minutes
|
||||
)
|
||||
trips.append(
|
||||
{
|
||||
"direction": "inbound",
|
||||
"depart_destination": es["depart_destination"],
|
||||
"check_in_by": (dep_dest - timedelta(minutes=30)).strftime(TIME_FMT),
|
||||
"check_in_by": (dep_dest - timedelta(minutes=30)).strftime(
|
||||
TIME_FMT
|
||||
),
|
||||
"arrive_st_pancras": es["arrive_st_pancras"],
|
||||
"depart_paddington": gwr["depart_paddington"],
|
||||
"arrive_uk_station": gwr["arrive_destination"],
|
||||
|
|
@ -279,12 +296,12 @@ def combine_inbound_trips(
|
|||
|
||||
|
||||
def find_unreachable_morning_eurostars(
|
||||
gwr_trains: list[dict],
|
||||
eurostar_trains: list[dict],
|
||||
gwr_trains: list[dict[str, Any]],
|
||||
eurostar_trains: list[dict[str, Any]],
|
||||
travel_date: str,
|
||||
min_connection_minutes: int = MIN_CONNECTION_MINUTES,
|
||||
max_connection_minutes: int = MAX_CONNECTION_MINUTES,
|
||||
) -> list[dict]:
|
||||
) -> list[dict[str, Any]]:
|
||||
unreachable = []
|
||||
|
||||
for es in eurostar_trains:
|
||||
|
|
@ -311,12 +328,12 @@ def find_unreachable_morning_eurostars(
|
|||
|
||||
|
||||
def find_unreachable_inbound_eurostars(
|
||||
eurostar_trains: list[dict],
|
||||
gwr_trains: list[dict],
|
||||
eurostar_trains: list[dict[str, Any]],
|
||||
gwr_trains: list[dict[str, Any]],
|
||||
travel_date: str,
|
||||
min_connection_minutes: int = INBOUND_MIN_CONNECTION_MINUTES,
|
||||
max_connection_minutes: int = INBOUND_MAX_CONNECTION_MINUTES,
|
||||
) -> list[dict]:
|
||||
) -> list[dict[str, Any]]:
|
||||
unreachable = []
|
||||
|
||||
for es in eurostar_trains:
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue