Add return and inbound journey support
This commit is contained in:
parent
6ba71447ef
commit
9691632f65
12 changed files with 1687 additions and 486 deletions
|
|
@ -32,7 +32,8 @@ def _headers() -> dict:
|
|||
|
||||
|
||||
def _request_body(
|
||||
station_crs: str,
|
||||
from_code: str,
|
||||
to_code: str,
|
||||
travel_date: str,
|
||||
conversation_token: str | None,
|
||||
later: bool,
|
||||
|
|
@ -44,8 +45,8 @@ def _request_body(
|
|||
"IsPreviousReturn": False,
|
||||
"campaignCode": "",
|
||||
"validationCode": "",
|
||||
"locfrom": f"GB{station_crs}",
|
||||
"locto": _PAD_CODE,
|
||||
"locfrom": from_code,
|
||||
"locto": to_code,
|
||||
"datetimedepart": f"{travel_date}T00:00:00",
|
||||
"outwarddepartafter": True,
|
||||
"datetimereturn": None,
|
||||
|
|
@ -67,7 +68,22 @@ def _request_body(
|
|||
}
|
||||
|
||||
|
||||
def _run_pages(station_crs: str, travel_date: str, first_class: bool = False):
|
||||
def _station_code(station_crs: str) -> str:
|
||||
return f"GB{station_crs}"
|
||||
|
||||
|
||||
def _od_codes(station_crs: str, direction: str) -> tuple[str, str]:
|
||||
if direction == "from_paddington":
|
||||
return _PAD_CODE, _station_code(station_crs)
|
||||
return _station_code(station_crs), _PAD_CODE
|
||||
|
||||
|
||||
def _run_pages(
|
||||
station_crs: str,
|
||||
travel_date: str,
|
||||
first_class: bool = False,
|
||||
direction: str = "to_paddington",
|
||||
):
|
||||
"""
|
||||
Iterate all pages of GWR journey search results.
|
||||
|
||||
|
|
@ -78,8 +94,9 @@ def _run_pages(station_crs: str, travel_date: str, first_class: bool = False):
|
|||
with httpx.Client(headers=_headers(), timeout=30) as client:
|
||||
conversation_token = None
|
||||
later = False
|
||||
from_code, to_code = _od_codes(station_crs, direction)
|
||||
for _ in range(_MAX_PAGES):
|
||||
body = _request_body(station_crs, travel_date, conversation_token, later)
|
||||
body = _request_body(from_code, to_code, travel_date, conversation_token, later)
|
||||
if first_class:
|
||||
body["firstclass"] = True
|
||||
body["standardclass"] = False
|
||||
|
|
@ -99,7 +116,12 @@ def _run_pages(station_crs: str, travel_date: str, first_class: bool = False):
|
|||
later = True
|
||||
|
||||
|
||||
def _run_pages_batched(station_crs: str, travel_date: str, first_class: bool = False):
|
||||
def _run_pages_batched(
|
||||
station_crs: str,
|
||||
travel_date: str,
|
||||
first_class: bool = False,
|
||||
direction: str = "to_paddington",
|
||||
):
|
||||
"""
|
||||
Like _run_pages but yields one list of (dep_time, fares_list) per API page call,
|
||||
allowing callers to stream results a page at a time.
|
||||
|
|
@ -108,8 +130,9 @@ def _run_pages_batched(station_crs: str, travel_date: str, first_class: bool = F
|
|||
with httpx.Client(headers=_headers(), timeout=30) as client:
|
||||
conversation_token = None
|
||||
later = False
|
||||
from_code, to_code = _od_codes(station_crs, direction)
|
||||
for _ in range(_MAX_PAGES):
|
||||
body = _request_body(station_crs, travel_date, conversation_token, later)
|
||||
body = _request_body(from_code, to_code, travel_date, conversation_token, later)
|
||||
if first_class:
|
||||
body["firstclass"] = True
|
||||
body["standardclass"] = False
|
||||
|
|
@ -132,16 +155,18 @@ def _run_pages_batched(station_crs: str, travel_date: str, first_class: bool = F
|
|||
later = True
|
||||
|
||||
|
||||
def fetch(station_crs: str, travel_date: str) -> dict[str, dict]:
|
||||
def fetch(
|
||||
station_crs: str, travel_date: str, direction: str = "to_paddington"
|
||||
) -> dict[str, dict]:
|
||||
"""
|
||||
Fetch GWR walk-on single fares from station_crs to London Paddington on travel_date.
|
||||
Fetch GWR walk-on single fares for the selected Paddington direction.
|
||||
|
||||
Returns {departure_time: {'ticket': name, 'price': float, 'code': code}}
|
||||
where price is in £ and only the cheapest available standard-class walk-on
|
||||
ticket per departure (with restrictions already applied by GWR) is kept.
|
||||
"""
|
||||
result: dict[str, dict] = {}
|
||||
for dep_time, fares in _run_pages(station_crs, travel_date):
|
||||
for dep_time, fares in _run_pages(station_crs, travel_date, direction=direction):
|
||||
cheapest = None
|
||||
for fare in fares:
|
||||
code = fare.get("ticketTypeCode")
|
||||
|
|
@ -166,7 +191,9 @@ def fetch(station_crs: str, travel_date: str) -> dict[str, dict]:
|
|||
return result
|
||||
|
||||
|
||||
def fetch_advance(station_crs: str, travel_date: str) -> dict[str, dict]:
|
||||
def fetch_advance(
|
||||
station_crs: str, travel_date: str, direction: str = "to_paddington"
|
||||
) -> dict[str, dict]:
|
||||
"""
|
||||
Fetch advance fares: cheapest standard advance and first-class advance per departure.
|
||||
|
||||
|
|
@ -175,7 +202,9 @@ def fetch_advance(station_crs: str, travel_date: str) -> dict[str, dict]:
|
|||
where each sub-dict has keys 'ticket', 'price', 'code'.
|
||||
"""
|
||||
std_advance: dict[str, dict] = {}
|
||||
for dep_time, fares in _run_pages(station_crs, travel_date, first_class=False):
|
||||
for dep_time, fares in _run_pages(
|
||||
station_crs, travel_date, first_class=False, direction=direction
|
||||
):
|
||||
cheapest = None
|
||||
for fare in fares:
|
||||
code = fare.get("ticketTypeCode")
|
||||
|
|
@ -199,7 +228,9 @@ def fetch_advance(station_crs: str, travel_date: str) -> dict[str, dict]:
|
|||
}
|
||||
|
||||
first_advance: dict[str, dict] = {}
|
||||
for dep_time, fares in _run_pages(station_crs, travel_date, first_class=True):
|
||||
for dep_time, fares in _run_pages(
|
||||
station_crs, travel_date, first_class=True, direction=direction
|
||||
):
|
||||
cheapest = None
|
||||
for fare in fares:
|
||||
price_pence = fare.get("fare", 0)
|
||||
|
|
@ -227,7 +258,9 @@ def fetch_advance(station_crs: str, travel_date: str) -> dict[str, dict]:
|
|||
}
|
||||
|
||||
|
||||
def fetch_advance_streaming(station_crs: str, travel_date: str):
|
||||
def fetch_advance_streaming(
|
||||
station_crs: str, travel_date: str, direction: str = "to_paddington"
|
||||
):
|
||||
"""
|
||||
Generator yielding partial advance fare dicts one GWR API page at a time.
|
||||
|
||||
|
|
@ -236,7 +269,9 @@ def fetch_advance_streaming(station_crs: str, travel_date: str):
|
|||
yielded immediately so callers can stream prices to clients as they arrive.
|
||||
"""
|
||||
# Pass 1: standard class advance fares
|
||||
for batch in _run_pages_batched(station_crs, travel_date, first_class=False):
|
||||
for batch in _run_pages_batched(
|
||||
station_crs, travel_date, first_class=False, direction=direction
|
||||
):
|
||||
page: dict[str, dict] = {}
|
||||
for dep_time, fares in batch:
|
||||
cheapest = None
|
||||
|
|
@ -267,7 +302,9 @@ def fetch_advance_streaming(station_crs: str, travel_date: str):
|
|||
yield page
|
||||
|
||||
# Pass 2: first class advance fares
|
||||
for batch in _run_pages_batched(station_crs, travel_date, first_class=True):
|
||||
for batch in _run_pages_batched(
|
||||
station_crs, travel_date, first_class=True, direction=direction
|
||||
):
|
||||
page = {}
|
||||
for dep_time, fares in batch:
|
||||
cheapest = None
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue