tcl-filtrage/main.py

132 lines
4 KiB
Python
Raw Normal View History

2021-11-14 11:52:44 +01:00
import collections
2021-11-09 13:56:42 +01:00
import re
2021-12-04 15:57:16 +01:00
from datetime import datetime
2021-11-14 12:32:34 +01:00
from typing import Dict, List, Optional, Tuple
2021-11-09 12:33:52 +01:00
import httpx
2021-11-09 12:33:18 +01:00
from fastapi import FastAPI, HTTPException
2021-11-12 15:58:03 +01:00
from fastapi.middleware.cors import CORSMiddleware
2021-11-09 12:50:21 +01:00
from fastapi.params import Header, Path
2021-11-09 12:33:18 +01:00
from pydantic import BaseModel
app = FastAPI()
2021-11-12 15:58:03 +01:00
origins = ["http://localhost:3000", "https://display.augendre.info"]
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
2021-11-09 12:33:52 +01:00
Delai = tuple[str, int]
2021-11-14 11:52:44 +01:00
class Stop(BaseModel):
id: int
name: str
2021-11-09 12:33:18 +01:00
class Passage(BaseModel):
ligne: str
2021-11-09 13:44:40 +01:00
delais: List[str]
2021-11-14 11:52:44 +01:00
destination: Stop
2021-11-09 12:33:18 +01:00
2021-11-09 12:33:52 +01:00
2021-11-09 12:33:18 +01:00
class Passages(BaseModel):
passages: List[Passage]
2021-11-14 11:52:44 +01:00
stop: Stop
2021-11-09 12:33:18 +01:00
2021-11-09 12:33:52 +01:00
2021-12-09 08:03:54 +01:00
@app.get("/")
async def root():
return {"status": "ok"}
2021-11-09 12:33:18 +01:00
@app.get("/stop/{stop_id}", response_model=Passages)
2021-11-09 12:50:21 +01:00
async def stop(
stop_id: int = Path(
None,
description="Stop id to monitor. Can be obtained using https://data.grandlyon.com/jeux-de-donnees/points-arret-reseau-transports-commun-lyonnais/donnees",
),
authorization: Optional[str] = Header(
None,
alias="Authorization",
description="Basic auth for remote API (data grand lyon)",
),
):
2021-11-14 11:52:44 +01:00
monitored_stop_id = stop_id
2021-11-09 12:33:18 +01:00
if authorization is None:
raise HTTPException(status_code=401, detail="Not authenticated")
2021-11-09 12:33:52 +01:00
headers = {"Authorization": authorization}
2021-11-09 12:33:18 +01:00
async with httpx.AsyncClient(headers=headers) as client:
2021-11-14 11:52:44 +01:00
passages_res = client.get(
2021-11-09 12:33:52 +01:00
"https://download.data.grandlyon.com/ws/rdata/tcl_sytral.tclpassagearret/all.json?maxfeatures=-1"
)
2021-11-14 11:52:44 +01:00
infos_res = client.get(
"https://download.data.grandlyon.com/ws/rdata/tcl_sytral.tclarret/all.json?maxfeatures=-1"
)
passages_res = await passages_res
infos_res = await infos_res
if passages_res.status_code != 200:
raise HTTPException(
status_code=passages_res.status_code,
2021-11-14 12:29:58 +01:00
detail="HTTP error during call to remote passages API",
2021-11-14 11:52:44 +01:00
)
if infos_res.status_code != 200:
2021-11-09 12:33:52 +01:00
raise HTTPException(
2021-11-14 11:52:44 +01:00
status_code=infos_res.status_code,
2021-11-14 12:29:58 +01:00
detail="HTTP error during call to remote info API",
2021-11-09 12:33:52 +01:00
)
2021-11-14 11:52:44 +01:00
stop_ids = {monitored_stop_id}
passages: Dict[Tuple[str, int], list[Delai]] = collections.defaultdict(list)
2021-11-14 11:52:44 +01:00
for passage in passages_res.json().get("values"):
if passage.get("id") == monitored_stop_id and passage.get("type") == "E":
2021-11-09 13:56:42 +01:00
ligne = passage.get("ligne")
ligne = re.sub(
"[A-Z]$", "", ligne
) # Remove letter suffix to group by commercial line name
2021-11-14 11:52:44 +01:00
destination = passage.get("idtarretdestination")
2021-12-04 15:57:16 +01:00
heure_passage = passage.get("heurepassage")
delai = get_delai(heure_passage)
passages[(ligne, destination)].append(delai)
2021-11-14 11:52:44 +01:00
stop_ids.add(destination)
2021-11-14 12:29:58 +01:00
if not passages:
raise HTTPException(status_code=404, detail="Stop not found")
2021-11-14 11:52:44 +01:00
stop_infos: Dict[int, Stop] = {}
for info in infos_res.json().get("values"):
stop_id = info.get("id")
if stop_id in stop_ids:
stop_infos[stop_id] = Stop(id=stop_id, name=info.get("nom"))
if len(stop_infos) == len(stop_ids):
break
2021-11-09 13:44:40 +01:00
passages_list = []
2021-11-14 11:52:44 +01:00
for key, delais in passages.items():
delais = list(map(lambda x: x[0], sorted(delais, key=lambda x: x[1])))
2021-11-14 11:52:44 +01:00
passages_list.append(
Passage(ligne=key[0], delais=delais, destination=stop_infos.get(key[1]))
)
passages_list.sort(key=lambda x: x.ligne)
2021-11-14 11:52:44 +01:00
return Passages(passages=passages_list, stop=stop_infos.get(monitored_stop_id))
2021-12-04 15:57:16 +01:00
def get_delai(heure_passage: str) -> Delai:
2021-12-04 15:57:16 +01:00
dt = datetime.strptime(heure_passage, "%Y-%m-%d %H:%M:%S")
now = datetime.now()
if now > dt:
return ("Passé", -2)
2021-12-04 15:57:16 +01:00
delai = dt - now
minutes = delai.seconds // 60
if minutes <= 0:
return ("Proche", -1)
return (f"{minutes} min", minutes)