Remove journey-client
This commit is contained in:
parent
bd0dd34b6e
commit
6b1539a3e7
5 changed files with 63 additions and 1278 deletions
1170
Journey.yaml
1170
Journey.yaml
File diff suppressed because it is too large
Load diff
|
|
@ -4,7 +4,6 @@ tasks:
|
|||
install:
|
||||
desc: Install dependencies, generate client, and download data
|
||||
cmds:
|
||||
- uv run generate_tfl_client.py
|
||||
- uv sync
|
||||
- cd frontend && npm install
|
||||
|
||||
|
|
|
|||
|
|
@ -1,49 +0,0 @@
|
|||
#!/usr/bin/env python3
|
||||
# /// script
|
||||
# requires-python = ">=3.12"
|
||||
# dependencies = ["openapi-python-client"]
|
||||
# ///
|
||||
"""Regenerate the TfL Journey API client from the OpenAPI specification."""
|
||||
|
||||
# Run it with:
|
||||
# uv run generate_tfl_client.py
|
||||
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
OPENAPI_SPEC = Path("Journey.yaml")
|
||||
OUTPUT_PATH = Path("tfl_journey_client")
|
||||
|
||||
|
||||
def main() -> None:
|
||||
if not OPENAPI_SPEC.exists():
|
||||
raise FileNotFoundError(f"OpenAPI spec not found: {OPENAPI_SPEC}")
|
||||
|
||||
# Skip if client already exists
|
||||
if OUTPUT_PATH.exists():
|
||||
print(f"TfL client already exists at {OUTPUT_PATH}, skipping")
|
||||
return
|
||||
|
||||
# Generate the client
|
||||
print(f"Generating client from {OPENAPI_SPEC}")
|
||||
result = subprocess.run(
|
||||
[
|
||||
"openapi-python-client",
|
||||
"generate",
|
||||
"--path",
|
||||
str(OPENAPI_SPEC),
|
||||
"--output-path",
|
||||
str(OUTPUT_PATH),
|
||||
],
|
||||
check=True,
|
||||
)
|
||||
|
||||
if result.returncode == 0:
|
||||
print(f"Client generated successfully at {OUTPUT_PATH}")
|
||||
else:
|
||||
print("Client generation failed")
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
|
@ -1,35 +1,22 @@
|
|||
import asyncio
|
||||
import os
|
||||
from typing import Literal
|
||||
import warnings
|
||||
from collections.abc import Callable
|
||||
from http import HTTPStatus
|
||||
|
||||
from httpx import Timeout
|
||||
from journey_client import Client
|
||||
from journey_client.api.journey import (
|
||||
journey_journey_results_by_path_from_path_to_query_via_query_national_search_query_date_qu as journey_api,
|
||||
)
|
||||
from journey_client.models import (
|
||||
JourneyJourneyResultsByPathFromPathToQueryViaQueryNationalSearchQueryDateQuTimeIs as TimeIs,
|
||||
)
|
||||
from journey_client.models import (
|
||||
JourneyJourneyResultsByPathFromPathToQueryViaQueryNationalSearchQueryDateQuJourneyPreference as JourneyPreference,
|
||||
)
|
||||
from journey_client.models import (
|
||||
JourneyJourneyResultsByPathFromPathToQueryViaQueryNationalSearchQueryDateQuCyclePreference as CyclePreference,
|
||||
)
|
||||
from journey_client.models import (
|
||||
JourneyJourneyResultsByPathFromPathToQueryViaQueryNationalSearchQueryDateQuBikeProficiency as BikeProficiency,
|
||||
)
|
||||
from journey_client.types import Unset
|
||||
import httpx
|
||||
|
||||
from .config import MAX_DELAY
|
||||
from .models import Destination, JourneyResult
|
||||
from .rate_limiter import RateLimiter
|
||||
|
||||
|
||||
BASE_URL = "https://api.tfl.gov.uk"
|
||||
|
||||
|
||||
async def fetch_journey_for_mode(
|
||||
client: Client,
|
||||
client: httpx.AsyncClient,
|
||||
rate_limiter: RateLimiter,
|
||||
from_location: str,
|
||||
to_location: str,
|
||||
|
|
@ -44,65 +31,77 @@ async def fetch_journey_for_mode(
|
|||
try:
|
||||
await rate_limiter.acquire()
|
||||
|
||||
cycle_preference = {
|
||||
"quick": CyclePreference.TAKEONTRANSPORT,
|
||||
"easy": CyclePreference.NONE,
|
||||
"cycle": CyclePreference.ALLTHEWAY,
|
||||
journey_preference = {
|
||||
"quick": "LeastTime",
|
||||
"easy": "LeastInterchange",
|
||||
"cycle": None,
|
||||
}[journey_type]
|
||||
|
||||
# options: public-bus,overground,train,tube,coach,dlr,cablecar,tram,river,walking,cycle
|
||||
cycle_preference = {
|
||||
"quick": None,
|
||||
"easy": None,
|
||||
"cycle": "AllTheWay",
|
||||
}[journey_type]
|
||||
|
||||
# curl -s "https://api.tfl.gov.uk/Journey/Meta/Modes" | jq '.[].modeName'
|
||||
mode = {
|
||||
"quick": [
|
||||
"public-bus",
|
||||
"bus",
|
||||
"overground",
|
||||
"train",
|
||||
"national-rail",
|
||||
"international-rail",
|
||||
"elizabeth-line",
|
||||
"tube",
|
||||
"coach",
|
||||
"dlr",
|
||||
"cablecar",
|
||||
"cable-car",
|
||||
"replacement-bus",
|
||||
"tram",
|
||||
"river",
|
||||
"river-bus",
|
||||
"walking",
|
||||
"cycle",
|
||||
],
|
||||
"easy": [
|
||||
"public-bus",
|
||||
"bus",
|
||||
"overground",
|
||||
"train",
|
||||
"national-rail",
|
||||
"international-rail",
|
||||
"elizabeth-line",
|
||||
"replacement-bus",
|
||||
"tube",
|
||||
"coach",
|
||||
"dlr",
|
||||
"cablecar",
|
||||
"cable-car",
|
||||
"tram",
|
||||
"river",
|
||||
"river-bus",
|
||||
],
|
||||
"cycle": ["cycle"],
|
||||
}[journey_type]
|
||||
|
||||
response = await journey_api.asyncio_detailed(
|
||||
from_=from_location,
|
||||
to=to_location,
|
||||
client=client,
|
||||
date=journey_date,
|
||||
time=journey_time,
|
||||
national_search=True,
|
||||
time_is=TimeIs.ARRIVING,
|
||||
journey_preference=JourneyPreference.LEASTINTERCHANGE
|
||||
if journey_type == "easy"
|
||||
else JourneyPreference.LEASTINTERCHANGE,
|
||||
cycle_preference=cycle_preference,
|
||||
bike_proficiency=BikeProficiency.FAST,
|
||||
walking_optimization=journey_type == "quick",
|
||||
mode=mode,
|
||||
)
|
||||
params: dict = {
|
||||
"date": journey_date,
|
||||
"time": journey_time,
|
||||
"nationalSearch": "true",
|
||||
"timeIs": "Arriving",
|
||||
"cyclePreference": cycle_preference,
|
||||
"bikeProficiency": "Fast",
|
||||
"walkingOptimization": str(journey_type == "quick").lower(),
|
||||
"mode": ",".join(mode),
|
||||
}
|
||||
if journey_preference:
|
||||
params["journeyPreference"] = journey_preference
|
||||
|
||||
if response.status_code == HTTPStatus.OK and response.parsed:
|
||||
journeys = response.parsed.journeys
|
||||
if not isinstance(journeys, Unset) and journeys:
|
||||
url = f"/Journey/JourneyResults/{from_location}/to/{to_location}"
|
||||
response = await client.get(url, params=params)
|
||||
|
||||
if response.status_code == HTTPStatus.OK:
|
||||
data = response.json()
|
||||
journeys = data.get("journeys", [])
|
||||
if journeys:
|
||||
durations = [
|
||||
j.duration
|
||||
j["duration"]
|
||||
for j in journeys
|
||||
if not isinstance(j.duration, Unset)
|
||||
if j.get("duration") is not None
|
||||
]
|
||||
if durations:
|
||||
return min(durations)
|
||||
|
|
@ -115,7 +114,7 @@ async def fetch_journey_for_mode(
|
|||
HTTPStatus.GATEWAY_TIMEOUT,
|
||||
):
|
||||
warnings.warn(
|
||||
f"HTTP {response.status_code.value} for {journey_type} from {from_location}, "
|
||||
f"HTTP {response.status_code} for {journey_type} from {from_location}, "
|
||||
f"retrying in {backoff:.1f}s (attempt {attempt + 1}/{retry_count})",
|
||||
stacklevel=2,
|
||||
)
|
||||
|
|
@ -141,7 +140,7 @@ async def fetch_journey_for_mode(
|
|||
|
||||
|
||||
async def fetch_all_modes(
|
||||
client: Client,
|
||||
client: httpx.AsyncClient,
|
||||
rate_limiter: RateLimiter,
|
||||
postcode: str,
|
||||
lat: float,
|
||||
|
|
@ -220,8 +219,15 @@ async def fetch_journey_times(
|
|||
to_location = dest.to_tfl_location()
|
||||
rate_limiter = RateLimiter()
|
||||
|
||||
client = Client(base_url="https://api.tfl.gov.uk").with_timeout(Timeout(30))
|
||||
async with client as client:
|
||||
# TFL API authentication via app_key query parameter
|
||||
tfl_token = os.environ.get("TFL_TOKEN")
|
||||
params = {"app_key": tfl_token} if tfl_token else {}
|
||||
|
||||
async with httpx.AsyncClient(
|
||||
base_url=BASE_URL,
|
||||
params=params,
|
||||
timeout=httpx.Timeout(30),
|
||||
) as client:
|
||||
tasks = [
|
||||
fetch_all_modes(
|
||||
client,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ dependencies = [
|
|||
"attrs>=22.2.0",
|
||||
"httpx>=0.28.1",
|
||||
"ipywidgets>=8.0.0",
|
||||
"journey-client",
|
||||
"jupyter>=1.0.0",
|
||||
"nest-asyncio>=1.6.0",
|
||||
"numpy>=1.26.0",
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue