2022-08-14 13:19:51 +00:00
from datetime import datetime , timedelta , timezone
2022-08-21 02:03:33 +00:00
import hashlib
2022-08-14 13:19:51 +00:00
import json
2023-01-16 12:48:41 +00:00
import logging
2022-08-14 13:19:51 +00:00
import os
2023-08-29 11:04:31 +00:00
import os . path
2023-01-19 11:04:08 +00:00
import re
2022-08-14 13:19:51 +00:00
import urllib . parse
import urllib . request
from PIL import Image , ImageDraw , ImageFont
2023-07-15 18:13:59 +00:00
def whenStr ( date , now ) :
if date < now + timedelta ( days = 1 ) :
2023-09-06 08:34:49 +00:00
if date . minute < = 9 :
return f " { date . hour } h0 { date . minute } "
else :
return f " { date . hour } h { date . minute } "
2023-07-15 18:13:59 +00:00
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 " )
2022-08-14 16:24:46 +00:00
class IDFMAPI :
2022-08-14 13:19:51 +00:00
2023-08-29 11:04:31 +00:00
fnt_R_path = " Parisine-Regular.ttf "
fnt_RB_path = " Parisine-Bold.ttf "
2022-08-14 13:19:51 +00:00
2022-08-14 16:24:46 +00:00
lines = {
" metros " : {
" 1 " : " C01371 " ,
" 2 " : " C01372 " ,
" 3 " : " C01373 " ,
" 4 " : " C01374 " ,
" 5 " : " C01375 " ,
" 6 " : " C01376 " ,
" 7 " : " C01377 " ,
" 8 " : " C01378 " ,
" 9 " : " C01379 " ,
" 10 " : " C01380 " ,
" 11 " : " C01381 " ,
" 12 " : " C01382 " ,
" 13 " : " C01383 " ,
" 14 " : " C01384 " ,
" 3B " : " C01386 " ,
" 7B " : " C01387 " ,
} ,
" buses " : {
" 57 " : " C01094 " ,
" 125 " : " C01154 " ,
#"131": "C01159",
" 184 " : " C01205 " ,
} ,
" rers " : {
" A " : " C01742 " ,
" B " : " C01743 " ,
" C " : " C01727 " ,
" D " : " C01728 " ,
" E " : " C01729 "
} ,
" tramways " : {
" T2 " : " C01390 " ,
" T3A " : " C01391 " ,
" T3B " : " C01679 " ,
" T7 " : " C01774 " ,
" T9 " : " C02317 " ,
} ,
}
2022-12-30 18:54:21 +00:00
def __init__ ( self , config , apikey = None ) :
2023-08-29 11:04:31 +00:00
self . fnt_R_path = os . path . join ( config . fonts_dir , IDFMAPI . fnt_R_path )
self . fnt_RB_path = os . path . join ( config . fonts_dir , IDFMAPI . fnt_RB_path )
2024-04-02 09:29:22 +00:00
self . baseurl = " https://prim.iledefrance-mobilites.fr/marketplace/disruptions_bulk "
2022-08-14 16:24:46 +00:00
self . apikey = apikey or os . environ [ " TOKEN_IDFM " ]
2022-08-14 13:19:51 +00:00
self . _cached_file = " .ratp- %s .cache "
2022-12-30 18:54:21 +00:00
self . cache_timeout = config . cache_timeout
self . max_cache_timeout = config . max_cache_timeout
2022-08-14 13:19:51 +00:00
2023-07-15 18:13:59 +00:00
def fromHTMLDisruption ( src ) :
2023-01-19 11:04:08 +00:00
cleanr = re . compile ( ' <.*?> ' )
cleanrA = re . compile ( ' <a.*?>.*?</a> ' )
2023-07-15 18:13:59 +00:00
period = re . compile ( ' Période :[^.]+. ' )
period2 = re . compile ( ' Dates? :[^.]+. ' )
more = re . compile ( " Plus d ' informations sur [^.]+. " )
2023-12-09 16:40:39 +00:00
return re . sub ( cleanr , ' ' , re . sub ( cleanrA , ' ' , re . sub ( period , ' ' , re . sub ( period2 , ' ' , re . sub ( more , ' ' , src . replace ( ' ' , ' ' ) . replace ( '   ' , ' ' ) . replace ( ' ’ ' , " ' " ) . replace ( ' à ' , ' à ' ) . replace ( ' é ' , ' é ' ) . replace ( ' è ' , ' è ' ) . replace ( ' ê ' , ' ê ' ) . replace ( ' û ' , ' û ' ) . replace ( ' Î ' , ' Î ' ) . 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 " , ' ' ) ) ) ) ) ) . replace ( " Pour plus d ' informations,. " , ' ' ) . replace ( " Pour plus d ' informations, . " , ' ' ) . strip ( )
2022-12-03 11:39:58 +00:00
2022-08-21 02:03:33 +00:00
def get_schedules ( self , mode , line , station , way = " A+R " ) :
if mode == " M " :
mode = " metros "
elif mode == " R " :
mode = " rers "
elif mode == " T " :
mode = " tramways "
2023-01-19 11:57:25 +00:00
line = line [ 1 : ]
2022-08-21 02:03:33 +00:00
elif mode == " B " :
mode = " buses "
elif mode == " N " :
mode = " noctiliens "
cache_file = self . _cached_file % ( " schedule- " + mode + " - " + line + " - " + hashlib . md5 ( ( mode + line + station + way ) . encode ( ) ) . hexdigest ( ) )
2023-01-16 01:27:23 +00:00
req = urllib . request . Request ( " https://ratp.p0m.fr/api/schedules/ %s / %s / %s / %s " % ( mode , line , station , way ) )
2022-08-21 02:03:33 +00:00
try :
with urllib . request . urlopen ( req ) as f :
with open ( cache_file , ' wb ' ) as fd :
fd . write ( f . read ( ) )
2023-01-16 12:48:41 +00:00
except ConnectionResetError as e :
logging . exception ( e )
except urllib . error . URLError as e :
logging . exception ( e )
except urllib . error . HTTPError as e :
logging . exception ( e )
2022-08-21 02:03:33 +00:00
with open ( cache_file ) as f :
res = json . load ( f )
# Convert time to hours
2023-01-16 01:27:23 +00:00
now = datetime . fromisoformat ( res [ " _metadata " ] [ " date " ] if len ( res [ " _metadata " ] [ " date " ] ) < = 25 else res [ " _metadata " ] [ " date " ] [ 0 : 19 ] + res [ " _metadata " ] [ " date " ] [ len ( res [ " _metadata " ] [ " date " ] ) - 6 : ] )
2022-08-21 02:03:33 +00:00
2022-08-22 07:15:12 +00:00
for i in range ( len ( res [ " result " ] [ " schedules " ] ) ) :
if " message " in res [ " result " ] [ " schedules " ] [ i ] :
2023-01-19 11:57:53 +00:00
if res [ " result " ] [ " schedules " ] [ i ] [ " message " ] == " Train a l ' approche " or res [ " result " ] [ " schedules " ] [ i ] [ " message " ] == " Train à l ' approche " or res [ " result " ] [ " schedules " ] [ i ] [ " message " ] == " Train à quai " or res [ " result " ] [ " schedules " ] [ i ] [ " message " ] == " Train a quai " or res [ " result " ] [ " schedules " ] [ i ] [ " message " ] == " A l ' approche " or res [ " result " ] [ " schedules " ] [ i ] [ " message " ] == " A l ' arret " or res [ " result " ] [ " schedules " ] [ i ] [ " message " ] == " A l ' arrêt " or res [ " result " ] [ " schedules " ] [ i ] [ " message " ] == " A quai " :
2022-08-22 07:15:12 +00:00
res [ " result " ] [ " schedules " ] [ i ] [ " message " ] = now . strftime ( " % H: % M " )
elif res [ " result " ] [ " schedules " ] [ i ] [ " message " ] . endswith ( " mn " ) :
res [ " result " ] [ " schedules " ] [ i ] [ " message " ] = ( now + timedelta ( minutes = int ( res [ " result " ] [ " schedules " ] [ i ] [ " message " ] . split ( " " ) [ 0 ] ) ) ) . strftime ( " % H: % M " )
2023-01-19 11:57:53 +00:00
res [ " result " ] [ " schedules " ] [ i ] [ " message " ] = res [ " result " ] [ " schedules " ] [ i ] [ " message " ] . replace ( " Retardé " , " + " ) . replace ( " Train retardé " , " ++ " ) . replace ( " Retardé " , " ++ " )
2022-08-21 02:03:33 +00:00
2022-08-22 07:15:12 +00:00
return [ m for m in res [ " result " ] [ " schedules " ] if " message " in m and m [ " message " ] != " Train sans arrêt " ]
2022-08-21 02:03:33 +00:00
2022-08-14 13:19:51 +00:00
def get_weather ( self ) :
2022-12-03 11:39:58 +00:00
cache_file = self . _cached_file % ( " ratp-disruptions " )
2022-08-14 13:19:51 +00:00
# Read the mod time
statinfo = None
try :
2022-08-14 16:24:46 +00:00
statinfo = os . stat ( cache_file )
2022-08-14 13:19:51 +00:00
except :
pass
2022-12-30 18:54:21 +00:00
if statinfo is None or datetime . fromtimestamp ( statinfo . st_mtime , tz = timezone . utc ) + timedelta ( minutes = self . cache_timeout ) < datetime . now ( tz = timezone . utc ) :
2022-08-14 13:19:51 +00:00
# Do the request and save it
2024-04-02 09:29:22 +00:00
req = urllib . request . Request ( self . baseurl + " /disruptions/v2 " )
2023-07-15 18:13:59 +00:00
req . headers [ " apikey " ] = self . apikey
2022-08-14 18:27:54 +00:00
try :
with urllib . request . urlopen ( req ) as f :
with open ( cache_file , ' wb ' ) as fd :
fd . write ( f . read ( ) )
except ConnectionResetError :
pass
2022-08-14 13:19:51 +00:00
2022-12-30 18:54:21 +00:00
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 " )
2022-08-14 13:19:51 +00:00
# Retrieve cached data
res = { }
2022-08-14 16:24:46 +00:00
with open ( cache_file ) as f :
2022-08-14 13:19:51 +00:00
res = json . load ( f )
2023-07-15 18:13:59 +00:00
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 ) :
2024-04-02 09:29:22 +00:00
for l in res [ " lines " ] :
if l [ " id " ] == " line:IDFM: " + IDFMAPI . lines [ mode ] [ line ] :
for impactedObject in l [ " impactedObjects " ] :
for disruptionId in impactedObject [ " disruptionIds " ] :
status = " past "
for ap in disruptions [ disruptionId ] [ " applicationPeriods " ] :
end = datetime . strptime ( ap [ " end " ] , " % Y % m %d T % H % M % S " )
if end < datetime . now ( ) :
continue
begin = datetime . strptime ( ap [ " begin " ] , " % Y % m %d T % H % M % S " )
if begin > datetime . now ( ) :
status = " future "
continue
2023-07-15 18:13:59 +00:00
2024-04-02 09:29:22 +00:00
status = " active "
break
disruptions [ disruptionId ] [ " status " ] = status
yield disruptions [ disruptionId ]
2022-12-03 11:39:58 +00:00
return None
2022-08-14 16:24:46 +00:00
2024-05-09 10:43:59 +00:00
def get_line_icon ( config , mode , line , size , fill = " gray " , state = " " ) :
if mode == " M " or mode == " metros " :
icon_mode = " metro "
line = line . replace ( " B " , " bis " )
elif mode == " T " or mode == " tramways " :
icon_mode = " tram "
line = line . replace ( " T " , " " ) . lower ( )
elif mode == " R " or mode == " rers " :
icon_mode = " RER "
2024-05-13 05:13:11 +00:00
img_line = None
2024-05-13 05:37:17 +00:00
if mode == " rers " and fill != " white " :
img = Image . new ( ' RGBA ' , ( size * 2 , int ( size * 2 * 1.25 ) ) , ' #fff0 ' )
draw = ImageDraw . Draw ( img )
2024-05-09 10:43:59 +00:00
2024-05-13 05:37:17 +00:00
fnt_icon = ImageFont . truetype ( os . path . join ( config . fonts_dir , IDFMAPI . fnt_RB_path ) , size * 2 - 9 )
draw . rounded_rectangle ( ( 0 , 0 , size * 2 - 1 , size * 2 - 1 ) , 7 , outline = fill , width = 2 )
draw . text ( ( size , size ) , line , fill = fill , anchor = " mm " , font = fnt_icon )
elif mode == " buses " :
2024-05-13 05:13:11 +00:00
width = int ( size * 1.38 )
img = Image . new ( ' RGBA ' , ( width , width ) , ' #fff0 ' )
draw = ImageDraw . Draw ( img )
fnt_icon = ImageFont . truetype ( os . path . join ( config . fonts_dir , IDFMAPI . fnt_RB_path ) , size - 3 )
2024-05-13 05:37:17 +00:00
draw . rectangle ( ( 0 , 0 , width , size ) , fill )
2024-05-13 05:13:11 +00:00
draw . text ( ( int ( width / 2 ) , int ( size / 2 ) ) , line , fill = " white " , anchor = " mm " , font = fnt_icon )
2024-05-13 05:37:17 +00:00
elif fill == " white " :
img = Image . open ( os . path . join ( config . icons_dir , " ratp " , " lignes " , icon_mode + " _ " + line + " _NB " + state + " _RVB.png " ) )
return img . resize ( ( int ( size ) , int ( img . height * size / img . width ) ) )
else :
img_line = Image . open ( os . path . join ( config . icons_dir , " ratp " , " lignes " , icon_mode + " _ " + line + " _NB_RVB.png " ) )
img = Image . new ( img_line . mode , ( img_line . width , int ( img_line . height * 1.4 ) ) )
if mode != " rers " :
img . paste ( Image . new ( " RGB " , ( img_line . width , img_line . height ) , fill ) , ( 0 , 0 ) , img_line )
else :
img . paste ( img_line , ( 0 , 0 ) )
2024-05-13 05:13:11 +00:00
if state != " " :
if state . endswith ( " petit " ) :
coeff = 2.5
elif state . endswith ( " moyen " ) :
coeff = 2
else :
coeff = 1.5
img_perturb = Image . open ( os . path . join ( config . icons_dir , " ratp " , " Perturbation_travaux_NB_RVB.png " if state . startswith ( " _travaux " ) else " Perturbation_trafic_NB_RVB.png " ) )
img_perturb = img_perturb . resize ( ( int ( img . width / coeff ) , int ( img_perturb . height * img . width / ( coeff * img_perturb . width ) ) ) )
if img_line is not None :
img . paste ( img_perturb , ( img . width - img_perturb . width , img_line . height - int ( img_perturb . height / coeff ) ) )
else :
img . paste ( img_perturb , ( img . width - img_perturb . width , img . height - img_perturb . height ) )
return img . resize ( ( int ( size ) , int ( img . height * size / img . width ) ) )
2022-08-14 16:24:46 +00:00
2022-08-14 13:19:51 +00:00
class RATPWeatherModule :
2023-01-19 09:51:59 +00:00
def __init__ ( self , major_lines = [ " M7 " , " M5 " , " M14 " , " RB " , " T3A " ] ) :
self . major_lines = major_lines
2022-08-14 16:24:46 +00:00
2022-12-30 18:54:21 +00:00
def gen_alerts ( self , config ) :
2022-08-14 16:24:46 +00:00
alerts = [ ]
2022-12-30 18:54:21 +00:00
weather = IDFMAPI ( config ) . get_weather ( )
2023-01-19 11:04:08 +00:00
id_seens = [ ]
2022-08-14 16:24:46 +00:00
for mode in weather :
for line in weather [ mode ] :
if mode [ 0 ] . upper ( ) + line not in self . major_lines :
continue
2024-05-09 10:43:59 +00:00
def alert_icon ( mode , line , state ) :
2022-08-14 16:24:46 +00:00
def icon ( size = 64 ) :
2022-08-14 16:41:51 +00:00
image = Image . new ( ' RGB ' , ( size , size ) , ' #000 ' )
2022-08-14 16:24:46 +00:00
2024-05-09 10:43:59 +00:00
line_icon = IDFMAPI . get_line_icon ( config , mode , line , int ( size / 2 ) , fill = " white " , state = state )
if state == " " :
white = Image . new ( ' RGB ' , ( line_icon . width , line_icon . height ) , ' #fff ' )
image . paste ( white , ( int ( size / 4 ) , 0 ) , line_icon )
else :
image . paste ( line_icon , ( int ( size / 4 ) , 0 ) , line_icon )
2022-08-14 16:24:46 +00:00
return image
return icon
2023-07-15 18:13:59 +00:00
for disruption in weather [ mode ] [ line ] :
2024-04-02 09:29:22 +00:00
if " message " not in disruption :
continue
if disruption [ " id " ] in id_seens :
2022-08-14 16:24:46 +00:00
continue
2024-04-02 09:29:22 +00:00
if disruption [ " status " ] != " active " and " applicationPeriods " not in disruption :
2022-08-17 08:22:48 +00:00
continue
2023-07-15 18:13:59 +00:00
subtitle = " "
2023-08-30 14:01:00 +00:00
oneline = False
2024-05-09 10:43:59 +00:00
state_type = " "
state_importance = " "
2023-07-15 18:13:59 +00:00
2024-04-02 09:29:22 +00:00
if " applicationPeriods " in disruption :
2023-07-15 18:13:59 +00:00
now = datetime . now ( )
2023-08-30 14:01:00 +00:00
yesterday = now + timedelta ( days = - 1 )
2023-07-15 18:13:59 +00:00
nextweek = now + timedelta ( days = 1 )
application_periods = [ ]
2024-04-02 09:29:22 +00:00
for ap in disruption [ " applicationPeriods " ] :
begin = datetime . strptime ( ap [ " begin " ] , " % Y % m %d T % H % M % S " )
end = datetime . strptime ( ap [ " end " ] , " % Y % m %d T % H % M % S " )
2022-08-14 16:24:46 +00:00
2024-04-02 09:29:22 +00:00
if end < now :
2023-01-19 11:04:08 +00:00
continue
2024-04-02 09:29:22 +00:00
elif begin > nextweek :
2023-07-15 18:13:59 +00:00
continue
2024-04-02 09:29:22 +00:00
elif len ( application_periods ) > 0 and application_periods [ 0 ] [ " begin " ] + timedelta ( hours = 16 ) < begin :
2023-07-15 18:13:59 +00:00
continue
else :
2024-04-02 09:29:22 +00:00
application_periods . append ( { " begin " : begin , " end " : end } )
2023-07-15 18:13:59 +00:00
2024-04-02 09:29:22 +00:00
if begin < yesterday :
2023-08-30 14:01:00 +00:00
oneline = True
2024-04-02 09:29:22 +00:00
if " cause " in disruption and disruption [ " cause " ] . lower ( ) == " travaux " and begin > now :
2023-09-06 09:06:07 +00:00
oneline = True
2024-05-09 10:43:59 +00:00
state_type = " travaux "
elif " cause " in disruption and disruption [ " cause " ] . lower ( ) == " travaux " :
state_type = " travaux "
else :
state_type = " trafic "
if " severity " in disruption :
if disruption [ " severity " ] == " NO_SERVICE " or disruption [ " severity " ] == " BLOQUANTE " :
state_importance = " grand "
elif disruption [ " severity " ] == " REDUCED_SERVICE " or disruption [ " severity " ] == " UNKNOWN_EFFECT " or disruption [ " severity " ] == " OTHER_EFFECT " :
state_importance = " moyen "
else :
state_importance = " petit "
2023-09-06 09:06:07 +00:00
2023-07-15 18:13:59 +00:00
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 )
2024-04-02 09:29:22 +00:00
id_seens . append ( disruption [ " id " ] )
2023-07-15 18:13:59 +00:00
yield {
2024-04-02 09:29:22 +00:00
" title " : disruption [ " title " ] ,
2023-07-15 18:13:59 +00:00
" subtitle " : subtitle ,
2024-04-02 09:29:22 +00:00
" description " : IDFMAPI . fromHTMLDisruption ( disruption [ " message " ] ) ,
2024-05-09 10:43:59 +00:00
" icon " : alert_icon ( mode , line , ( " _ " + state_type + " _ " + state_importance ) if state_type != " " else " " ) ,
2023-08-30 14:01:00 +00:00
" oneline " : oneline ,
2023-07-15 18:13:59 +00:00
}
2022-08-14 16:24:46 +00:00
2024-05-09 10:43:59 +00:00
def draw_module ( self , config , width , height , line_height = 22 ) :
2022-08-14 13:19:51 +00:00
image = Image . new ( ' RGB ' , ( width , height ) , ' #fff ' )
draw = ImageDraw . Draw ( image )
2022-12-30 18:54:21 +00:00
weather = IDFMAPI ( config ) . get_weather ( )
2022-08-14 13:19:51 +00:00
align_x = 0
align_y = 0
2022-08-14 16:24:46 +00:00
for mode in [ " metros " , " rers " , " tramways " , " buses " ] :
if mode != " rers " and mode != " buses " :
2022-08-14 13:19:51 +00:00
align_x = 0
for line in weather [ mode ] :
if align_x + line_height > = width :
2024-05-09 10:43:59 +00:00
align_x = 0
align_y + = int ( 1.5 * line_height )
2022-08-14 13:19:51 +00:00
2022-08-14 16:24:46 +00:00
states = [ ]
2023-07-15 18:13:59 +00:00
for disruption in weather [ mode ] [ line ] :
2024-04-02 09:29:22 +00:00
status = " past "
for ap in disruption [ " applicationPeriods " ] :
end = datetime . strptime ( ap [ " end " ] , " % Y % m %d T % H % M % S " )
if end < datetime . now ( ) :
continue
begin = datetime . strptime ( ap [ " begin " ] , " % Y % m %d T % H % M % S " )
if begin > datetime . now ( ) :
status = " future "
continue
status = " active "
break
if status != " active " :
2023-07-15 18:13:59 +00:00
continue
if " severity " in disruption :
2024-04-02 09:29:22 +00:00
if disruption [ " cause " ] == " TRAVAUX " :
states . append ( disruption [ " severity " ] + " " + disruption [ " cause " ] )
2023-07-15 18:13:59 +00:00
else :
2024-04-02 09:29:22 +00:00
states . append ( disruption [ " severity " ] )
2023-07-15 18:13:59 +00:00
2024-05-09 10:43:59 +00:00
state = " "
2023-07-15 18:13:59 +00:00
fill = " darkgray "
if " NO_SERVICE " in states :
fill = " black "
2024-05-09 10:43:59 +00:00
state = " _trafic_grand "
2023-07-15 18:13:59 +00:00
elif " REDUCED_SERVICE " in states or " UNKNOWN_EFFECT " in states or " OTHER_EFFECT " in states :
fill = " teal "
2024-05-09 10:43:59 +00:00
state = " _trafic_moyen "
elif " BLOQUANTE TRAVAUX " in states :
fill = " teal "
state = " _travaux_moyen "
2023-07-15 18:13:59 +00:00
elif len ( states ) > 0 :
fill = " gray "
2024-05-09 10:43:59 +00:00
state = " _trafic_petit "
2022-08-14 13:19:51 +00:00
2024-05-09 10:43:59 +00:00
icon = IDFMAPI . get_line_icon ( config , mode , line , line_height , fill = fill , state = state )
2022-08-14 16:24:46 +00:00
image . paste ( icon , ( align_x , align_y ) , icon )
2022-08-14 13:19:51 +00:00
2022-08-14 16:24:46 +00:00
align_x + = icon . width + 5
2022-08-14 13:19:51 +00:00
2022-08-14 16:24:46 +00:00
if mode != " metros " and mode != " tramways " :
2024-05-09 10:43:59 +00:00
align_y + = int ( 1.5 * line_height )
2022-08-14 13:19:51 +00:00
else :
2024-05-09 10:43:59 +00:00
align_x + = int ( 1.5 * line_height )
2022-08-14 13:19:51 +00:00
return image
2022-08-21 02:03:33 +00:00
class RATPNextStopModule :
def draw_module ( self , config , stops , width , height , line_height = 17 ) :
image = Image . new ( ' RGB ' , ( width , height ) , ' #fff ' )
draw = ImageDraw . Draw ( image )
2023-01-16 01:28:10 +00:00
alerts = [ ]
2023-08-29 11:04:31 +00:00
fnt_R = ImageFont . truetype ( os . path . join ( config . fonts_dir , IDFMAPI . fnt_R_path ) , line_height )
fnt_B = ImageFont . truetype ( os . path . join ( config . fonts_dir , IDFMAPI . fnt_RB_path ) , line_height )
2022-08-21 02:03:33 +00:00
align = 0
2023-09-06 08:21:34 +00:00
mint = datetime . now ( ) + timedelta ( minutes = 10 )
2022-12-30 18:54:21 +00:00
api = IDFMAPI ( config )
2022-08-21 02:03:33 +00:00
for stop in stops :
2023-01-16 01:27:23 +00:00
tmp = stop . split ( " / " , 2 )
2022-08-21 02:03:33 +00:00
mode = tmp [ 0 ] [ 0 ]
line = tmp [ 0 ] [ 1 : ]
2023-01-16 01:28:10 +00:00
try :
if 1 < len ( tmp ) < 4 :
prep = { }
for s in api . get_schedules ( mode , line , * tmp [ 1 : ] ) :
2023-09-06 08:21:34 +00:00
# Skip too close items
try :
t = datetime . strptime ( datetime . now ( ) . strftime ( " % Y- % m- %d T " ) + s [ " message " ] [ 0 : 5 ] , " % Y- % m- %d T % H: % M " )
if t < mint :
continue
except :
pass
2023-01-16 01:28:10 +00:00
if s [ " destination " ] not in prep :
prep [ s [ " destination " ] ] = [ ]
prep [ s [ " destination " ] ] . append ( s )
2023-08-29 11:04:31 +00:00
icon = IDFMAPI . get_line_icon ( config , mode , line , int ( line_height * ( 1.5 if len ( prep . keys ( ) ) > 1 else 1 ) ) )
2023-01-16 01:28:10 +00:00
image . paste ( icon , ( 0 , align ) , icon )
max_dest = 64
for dest , msgs in prep . items ( ) :
if len ( msgs ) == 0 :
continue
2022-08-22 07:15:12 +00:00
2023-01-16 01:28:10 +00:00
align_x = line_height * 2
2022-08-21 02:03:33 +00:00
2023-12-09 16:40:39 +00:00
sz = fnt_B . getlength ( dest )
2023-01-16 01:28:10 +00:00
while sz > max_dest :
dest = dest [ : - 1 ]
2023-12-09 16:40:39 +00:00
sz = fnt_B . getlength ( dest )
2022-08-21 02:03:33 +00:00
draw . text (
( align_x , align ) ,
2023-01-16 01:28:10 +00:00
dest ,
font = fnt_B , anchor = " lt " , fill = " black "
2022-08-21 02:03:33 +00:00
)
2023-01-16 01:28:10 +00:00
align_x + = max_dest + int ( line_height / 2.5 )
2023-01-16 12:48:59 +00:00
for msg in [ ] + msgs :
2023-01-16 01:28:10 +00:00
draw . text (
( align_x , align ) ,
msg [ " message " ] ,
font = fnt_R , anchor = " lt " , fill = " black "
)
2023-12-09 16:40:39 +00:00
align_x + = fnt_R . getlength ( msg [ " message " ] ) + int ( line_height / 2.5 )
2023-01-16 01:28:10 +00:00
align + = line_height
align + = int ( line_height * 0.33 )
except Exception as e :
alerts . append ( {
" title " : " Impossible de récupérer les horaires du " + mode + line + " pour " + tmp [ 1 ] ,
" description " : type ( e ) . __name__ + " : " + ( e . message if hasattr ( e , ' message ' ) else str ( e ) ) ,
2024-05-09 08:53:28 +00:00
" icon " : " weather/wi-earthquake.png " ,
2023-01-16 01:28:10 +00:00
} )
2022-08-21 02:03:33 +00:00
2023-01-16 12:38:51 +00:00
align - = int ( line_height * 0.33 )
2022-08-21 02:03:33 +00:00
image = image . crop ( ( 0 , 0 , width , min ( align , height ) ) )
2023-01-16 01:28:10 +00:00
return image , alerts