epaper/modules/sncf.py

146 lines
5.8 KiB
Python

from datetime import datetime, timedelta, timezone
import base64
import json
import logging
import os
import os.path
import urllib.error
import urllib.parse
import urllib.request
import re
from PIL import Image, ImageDraw, ImageFont, ImageOps
class SNCFAPI:
CLEANR = re.compile('<.*?>')
def __init__(self, config):
self.baseurl = "https://www.sncf.com/api/iv"
self.auth = base64.b64encode(b"admin:$2y$10$QvxWSS4f5DIFSkAmuBoV9OJG3M2bhec9d3F2.YnULBMtpzKAq2KS.").decode()
self._config = config
self._cached_file = ".sncf-%d-%s.cache"
self.cache_timeout = config.cache_timeout
self.max_cache_timeout = config.max_cache_timeout
def get_icon(size):
height = int(size * 0.531)
img = Image.open(os.path.join(self._config.icons_dir, "sncf.png")).resize((size, height))
r,g,b,a = img.split()
rgb_image = Image.merge('RGB', (r,g,b))
inverted_image = ImageOps.invert(rgb_image)
r2,g2,b2 = inverted_image.split()
return Image.merge('RGBA', (r2,g2,b2,a))
def get_train_status(self, numero, date):
cache_file = self._cached_file % (int(numero), date.strftime("%Y-%m-%d"))
# Read the mod time
statinfo = None
try:
statinfo = os.stat(cache_file)
except:
pass
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(self.baseurl + "/1.0/infoVoy/rechercherListeCirculations?numero=%d&dateCirculation=%s&codeZoneArret&typeHoraire=TEMPS_REEL" % (int(numero), date.strftime("%Y-%m-%d")),
headers={
'Authorization': "Basic " + self.auth,
'Accept': "application/json",
'Accept-Language': "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3",
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
})
try:
with urllib.request.urlopen(req) as f:
with open(cache_file, 'wb') as fd:
fd.write(f.read())
except ConnectionResetError:
pass
except urllib.error.URLError:
pass
try:
statinfo = os.stat(cache_file)
except:
pass
if statinfo is None or datetime.fromtimestamp(statinfo.st_mtime, tz=timezone.utc) + timedelta(minutes=self.max_cache_timeout) < datetime.now(tz=timezone.utc):
print(self.baseurl + "/1.0/infoVoy/rechercherListeCirculations?numero=%d&dateCirculation=%s&codeZoneArret&typeHoraire=TEMPS_REEL" % (int(numero), date.strftime("%Y-%m-%d")))
logging.exception(Exception("File too old to predict SNCF issue"))
return None
# Retrieve cached data
res = {}
with open(cache_file) as f:
res = json.load(f)
try:
return res["reponseRechercherListeCirculations"]["reponse"]["listeResultats"]["resultat"][0]["donnees"]["listeCirculations"]["circulation"][0]
except:
return None
def get_weather(self, region):
cache_file = self._cached_file % (0, region)
# Read the mod time
statinfo = None
try:
statinfo = os.stat(cache_file)
except:
pass
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(self.baseurl + "/edito/bandeaux?region=%s" % (region),
headers={
'Authorization': "Basic " + self.auth,
'Accept': "application/json",
'Accept-Language': "fr,fr-FR;q=0.8,en-US;q=0.5,en;q=0.3",
'User-Agent': "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/108.0.0.0 Safari/537.36",
})
try:
with urllib.request.urlopen(req, timeout=3) as f:
with open(cache_file, 'wb') as fd:
fd.write(f.read())
except ConnectionResetError as e:
logging.exception(e)
except urllib.error.URLError as e:
logging.exception(e)
except TimeoutError as e:
logging.exception(e)
try:
statinfo = os.stat(cache_file)
except:
pass
if statinfo is None or datetime.fromtimestamp(statinfo.st_mtime, tz=timezone.utc) + timedelta(minutes=self.max_cache_timeout) < datetime.now(tz=timezone.utc):
raise Exception("File too old")
# Retrieve cached data
res = {}
with open(cache_file) as f:
res = json.load(f)
return res
class SNCFWeatherModule:
def __init__(self):
pass
def gen_alerts(self, config, region):
alerts = []
weather = SNCFAPI(config).get_weather(region)
for alert in weather:
if alert["type"] != "perturbation":
continue
yield {
"description": re.sub(SNCFAPI.CLEANR, '\n', alert["content"]).replace('Restez informés sur le fil', '').replace('Twitter @train_nomad', '').strip(),
"icon": SNCFAPI.get_icon,
}