Use navitia IDFM API directly

This commit is contained in:
nemunaire 2023-07-15 20:13:59 +02:00
parent b77f1cd0d0
commit a7f428ace1
1 changed files with 111 additions and 50 deletions

View File

@ -9,6 +9,28 @@ import urllib.request
from PIL import Image, ImageDraw, ImageFont
def whenStr(date, now):
if date < now + timedelta(days=1):
return date.hours() + "h" + date.minutes()
elif date < now + timedelta(days=7):
weekday = date.weekday()
if weekday == 0:
return "lundi"
elif weekday == 1:
return "mardi"
elif weekday == 2:
return "mercredi"
elif weekday == 3:
return "jeudi"
elif weekday == 4:
return "vendredi"
elif weekday == 5:
return "samedi"
else:
return "dimanche"
else:
return date.strftime("%d %b")
class IDFMAPI:
fnt_R_path = "./fonts/Parisine-Regular.ttf"
@ -65,24 +87,13 @@ class IDFMAPI:
self.cache_timeout = config.cache_timeout
self.max_cache_timeout = config.max_cache_timeout
def fromIVtoPRIM(src, line):
def fromHTMLDisruption(src):
cleanr = re.compile('<.*?>')
cleanrA = re.compile('<a.*?>.*?</a>')
return {
"InfoChannelRef": {
"value": "Perturbation" if src["severity"] >= 2 else ("Travaux" if src["severity"] == 1 else "Message"),
},
"Content": {
"Message": [{
"MessageType": "SHORT_MESSAGE",
"MessageText": {
"id": src["id"],
"title": src["title"].replace("Métro " + line + " : ", '').replace("Ligne " + line + " : ", '').replace("Tramway " + line + " : ", ''),
"value": re.sub(cleanr, '', re.sub(cleanrA, '', src["message"].replace('&nbsp;', ' ').replace('&#8217;', "'").replace('&#224;', 'à').replace('&#233;', 'é').replace('&#232;', 'è').replace('<br>', ' ').replace('</p>', ' ').replace('Information Ile de France Mobilités :', ''))).strip(),
}
}],
}
}
period = re.compile('Période :[^.]+. ')
period2 = re.compile('Dates? :[^.]+. ')
more = re.compile(" Plus d'informations sur [^.]+.")
return re.sub(cleanr, '', re.sub(cleanrA, '', re.sub(period, '', re.sub(period2, '', re.sub(more, '', src.replace('&nbsp;', ' ').replace('&#160;', ' ').replace('&#8217;', "'").replace('&#224;', 'à').replace('&#233;', 'é').replace('&#232;', 'è').replace('&#234;', 'ê').replace('&#251;', 'û').replace('&#206;', 'Î').replace('<br>', ' ').replace('</p>', ' ').replace('Information Ile de France Mobilités :', '').replace("Les horaires du calculateur d'itinéraire tiennent compte des travaux. ", '').replace("à la demande de la Préfecture de Police et d'Île-de-France Mobilités, et ", '')))))).strip()
def get_schedules(self, mode, line, station, way="A+R"):
if mode == "M":
@ -128,16 +139,6 @@ class IDFMAPI:
return [m for m in res["result"]["schedules"] if "message" in m and m["message"] != "Train sans arrêt"]
def get_weather(self):
ret = {}
for mode in IDFMAPI.lines:
ret[mode] = {}
for line in IDFMAPI.lines[mode]:
ret[mode][line] = self.get_line_weather(mode, line)
return ret
def get_line_weather(self, mode, line):
cache_file = self._cached_file % ("ratp-disruptions")
# Read the mod time
statinfo = None
@ -148,7 +149,8 @@ class IDFMAPI:
if statinfo is None or datetime.fromtimestamp(statinfo.st_mtime, tz=timezone.utc) + timedelta(minutes=self.cache_timeout) < datetime.now(tz=timezone.utc):
# Do the request and save it
req = urllib.request.Request("https://api-iv.iledefrance-mobilites.fr/disruptions")
req = urllib.request.Request(self.baseurl + "/navitia/line_reports/coverage/fr-idf/line_reports?count=2000")
req.headers["apikey"] = self.apikey
try:
with urllib.request.urlopen(req) as f:
with open(cache_file, 'wb') as fd:
@ -169,11 +171,29 @@ class IDFMAPI:
with open(cache_file) as f:
res = json.load(f)
for l in res["currentIT"]:
if "id" in l:
if l["id"] == "line:IDFM:" + IDFMAPI.lines[mode][line]:
for d in l["disruptions"]:
yield IDFMAPI.fromIVtoPRIM(d, line)
disruptions = {}
for d in res["disruptions"]:
disruptions[d["id"]] = d
ret = {}
for mode in IDFMAPI.lines:
ret[mode] = {}
for line in IDFMAPI.lines[mode]:
ret[mode][line] = self.get_line_weather(res, disruptions, mode, line)
return ret
def get_line_weather(self, res, disruptions, mode, line):
for l in res["line_reports"]:
if "line" not in l:
continue
if "id" in l["line"]:
if l["line"]["id"] == "line:IDFM:" + IDFMAPI.lines[mode][line]:
for link in l["line"]["links"]:
if disruptions[link["id"]]["status"] != "past":
yield disruptions[link["id"]]
return None
@ -230,26 +250,56 @@ class RATPWeatherModule:
return image
return icon
for info in weather[mode][line]:
if "InfoChannelRef" not in info or (info["InfoChannelRef"]["value"] != "Perturbation" and info["InfoChannelRef"]["value"] != "Travaux"):
for disruption in weather[mode][line]:
if "messages" not in disruption:
continue
if "Message" not in info["Content"]:
if disruption["status"] != "active" and "application_periods" not in disruption:
continue
for msg in info["Content"]["Message"]:
if "MessageType" not in msg or msg["MessageType"] != "SHORT_MESSAGE":
continue
title = ""
subtitle = ""
content = ""
if "id" in msg["MessageText"]:
if msg["MessageText"]["id"] in id_seens:
if "application_periods" in disruption:
now = datetime.now()
nextweek = now + timedelta(days=1)
application_periods = []
for ap in disruption["application_periods"]:
ap["begin"] = datetime.fromisoformat(ap["begin"])
ap["end"] = datetime.fromisoformat(ap["end"])
if ap["end"] < now:
continue
id_seens.append(msg["MessageText"]["id"])
yield {
"title": msg["MessageText"]["title"] if "title" in msg["MessageText"] else "",
"description": msg["MessageText"]["value"].replace("", ""),
"icon": alert_icon(mode, line),
}
elif ap["begin"] > nextweek:
continue
elif len(application_periods) > 0 and application_periods[0]["begin"] + timedelta(hours=16) < ap["begin"]:
continue
else:
application_periods.append(ap)
if len(application_periods) == 0:
continue
elif len(application_periods) == 1:
if application_periods[0]["begin"] > now:
subtitle = "De " + whenStr(application_periods[0]["begin"], now) + " à " + whenStr(application_periods[0]["end"], now)
elif application_periods[0]["end"] > now:
subtitle = "Fin " + whenStr(application_periods[0]["end"], now)
for msg in disruption["messages"]:
if msg["channel"]["name"] == "titre":
title = msg["text"]
elif msg["channel"]["name"] == "moteur":
content = IDFMAPI.fromHTMLDisruption(msg["text"])
elif len(content) == "":
content = IDFMAPI.fromHTMLDisruption(msg["text"])
yield {
"title": title,
"subtitle": subtitle,
"description": content,
"icon": alert_icon(mode, line),
}
def draw_module(self, config, width, height, line_height=19):
image = Image.new('RGB', (width, height), '#fff')
@ -275,11 +325,22 @@ class RATPWeatherModule:
align_y += line_height + 6
states = []
for info in weather[mode][line]:
if "InfoChannelRef" in info:
states.append(info["InfoChannelRef"]["value"])
for disruption in weather[mode][line]:
if disruption["status"] != "active":
continue
if "severity" in disruption:
if disruption["cause"] == "travaux" and "line" not in [x["pt_object"]["embedded_type"] for x in disruption["impacted_objects"]]:
states.append(disruption["severity"]["effect"] + " " + disruption["cause"])
else:
states.append(disruption["severity"]["effect"])
fill = "black" if "Perturbation" in states else ("gray" if "Travaux" in states else "darkgray")
fill = "darkgray"
if "NO_SERVICE" in states:
fill = "black"
elif "REDUCED_SERVICE" in states or "UNKNOWN_EFFECT" in states or "OTHER_EFFECT" in states:
fill = "teal"
elif len(states) > 0:
fill = "gray"
icon = IDFMAPI.get_line_icon(mode, line, line_height, fill=fill)
image.paste(icon, (align_x, align_y), icon)