2016-12-16 10:05:54 +00:00
import collections , re , time
2017-10-29 12:00:08 +00:00
from datetime import datetime , date
2017-03-09 11:57:35 +00:00
from collections import Counter
2018-09-24 14:13:27 +00:00
from src import Utils
2017-03-09 11:57:35 +00:00
2016-10-06 13:27:35 +00:00
from suds . client import Client
2017-03-09 11:57:35 +00:00
from suds import WebFault
2016-10-06 13:27:35 +00:00
2016-12-16 10:05:54 +00:00
# Note that this module requires the open *Staff Version* of the Darwin API
# You can register for an API key here: http://openldbsv.nationalrail.co.uk/
# We use this instead of the 'regular' version because it offers a *lot* more
# information.
URL = ' https://lite.realtime.nationalrail.co.uk/OpenLDBSVWS/wsdl.aspx?ver=2016-02-16 '
2016-10-06 13:27:35 +00:00
class Module ( object ) :
_name = " NR "
2017-12-10 15:20:48 +00:00
PASSENGER_ACTIVITIES = [ " U " , " P " , " R " ]
2017-04-06 09:39:59 +00:00
COLOURS = [ Utils . COLOR_LIGHTBLUE , Utils . COLOR_GREEN , Utils . COLOR_RED , Utils . COLOR_CYAN , Utils . COLOR_LIGHTGREY , Utils . COLOR_ORANGE ]
2017-12-10 15:20:48 +00:00
2018-09-02 18:54:45 +00:00
def __init__ ( self , bot , events , exports ) :
2016-10-06 13:27:35 +00:00
self . bot = bot
2017-02-14 11:10:18 +00:00
self . _client = None
2018-09-19 12:25:12 +00:00
events . on ( " received.command.nrtrains "
2017-03-09 13:33:24 +00:00
) . hook ( self . trains , min_args = 1 ,
help = " Get train/bus services for a station (Powered by NRE) " ,
2016-10-06 13:27:35 +00:00
usage = " <crs_id> " )
2018-09-19 12:25:12 +00:00
events . on ( " received.command.nrservice "
2016-11-02 01:28:47 +00:00
) . hook ( self . service , min_args = 1 ,
2016-12-20 08:54:56 +00:00
help = " Get train service information for a UID, headcode or RID (Powered by NRE) " ,
2016-11-02 01:28:47 +00:00
usage = " <service_id> " )
2018-09-19 12:25:12 +00:00
events . on ( " received.command.nrhead "
2016-12-16 10:05:54 +00:00
) . hook ( self . head , min_args = 1 ,
2016-12-20 08:54:56 +00:00
help = " Get information for a given headcode/UID/RID (Powered by NRE) " ,
2016-12-16 10:05:54 +00:00
usage = " <headcode> " )
2018-09-19 12:25:12 +00:00
events . on ( " received.command.nrcode "
2017-02-14 11:10:18 +00:00
) . hook ( self . service_code , min_args = 1 ,
help = " Get the text for a given delay/cancellation code (Powered by NRE) " ,
usage = " <code> " )
2018-09-19 12:25:12 +00:00
events . on ( " telegram.command.nrtrains " ) . hook ( self . trains )
events . on ( " telegram.command.nrcode " ) . hook ( self . service_code )
events . on ( " telegram.command.nrhead " ) . hook ( self . head )
events . on ( " telegram.command.nrservice " ) . hook ( self . service )
2017-02-14 11:10:18 +00:00
@property
def client ( self ) :
if self . _client : return self . _client
try :
token = self . bot . config [ " nre-api-key " ]
client = Client ( URL )
header_token = client . factory . create ( ' ns2:AccessToken ' )
header_token . TokenValue = token
client . set_options ( soapheaders = header_token )
self . _client = client
2018-09-19 12:25:12 +00:00
except Exception as e :
2017-02-14 11:10:18 +00:00
pass
return self . _client
2016-12-21 15:50:45 +00:00
def filter ( self , args , defaults ) :
args = re . findall ( r " [^ \ s,]+ " , args )
params = { }
for arg in args :
if " : " in arg :
params [ arg . split ( " : " , 1 ) [ 0 ] ] = arg . split ( " : " , 1 ) [ 1 ]
elif " = " in arg :
params [ arg . split ( " = " , 1 ) [ 0 ] ] = arg . split ( " = " , 1 ) [ 1 ]
else :
2017-03-08 09:35:23 +00:00
params [ arg . replace ( " ! " , " " ) ] = ' ! ' not in arg
2016-12-21 15:50:45 +00:00
ret = { k : v [ 0 ] for k , v in defaults . items ( ) }
ret [ " default " ] = True
2017-01-16 16:22:09 +00:00
ret [ " errors " ] = [ ]
2016-12-21 15:50:45 +00:00
for k , v in params . items ( ) :
if not k in defaults . keys ( ) :
2017-01-16 16:22:09 +00:00
ret [ " errors " ] . append ( ( k , " Invalid parameter " ) )
continue
2016-12-21 15:50:45 +00:00
if not defaults [ k ] [ 1 ] ( v ) :
2017-01-16 16:22:09 +00:00
ret [ " errors " ] . append ( ( v , ' Invalid value for " %s " ' % k ) )
continue
2016-12-21 15:50:45 +00:00
ret [ " default " ] = False
ret [ k ] = v if len ( defaults [ k ] ) == 2 else defaults [ k ] [ 2 ] ( v )
2017-01-16 16:22:09 +00:00
ret [ " errors_summary " ] = " , " . join ( [ ' " %s " : %s ' % ( a [ 0 ] , a [ 1 ] ) for a in ret [ " errors " ] ] )
2016-12-21 15:50:45 +00:00
return ret
2017-03-09 17:01:56 +00:00
def process ( self , service ) :
2017-09-02 11:54:20 +00:00
ut_now = datetime . now ( ) . timestamp ( )
2017-06-05 13:25:05 +00:00
nonetime = { " orig " : None , " datetime " : None , " ut " : 0 ,
2017-12-10 15:20:48 +00:00
" short " : ' ' , " prefix " : ' ' , " on_time " : False ,
2017-11-01 12:03:36 +00:00
" estimate " : False , " status " : 4 , " schedule " : False }
2017-03-09 17:01:56 +00:00
times = { }
a_types = [ " eta " , " ata " , " sta " ]
d_types = [ " etd " , " atd " , " std " ]
for a in a_types + d_types :
2017-05-30 21:11:27 +00:00
if a in service and service [ a ] :
2017-03-09 17:01:56 +00:00
times [ a ] = { " orig " : service [ a ] }
2017-05-30 21:11:27 +00:00
if len ( service [ a ] ) > 5 :
times [ a ] [ " datetime " ] = datetime . strptime ( service [ a ] , " % Y- % m- %d T % H: % M: % S " )
else :
times [ a ] [ " datetime " ] = datetime . strptime (
datetime . now ( ) . date ( ) . isoformat ( ) + " T " + service [ a ] [ : 4 ] ,
" % Y- % m- %d T % H % M "
)
2017-03-09 17:01:56 +00:00
times [ a ] [ " ut " ] = times [ a ] [ " datetime " ] . timestamp ( )
else :
2017-06-05 13:25:05 +00:00
times [ a ] = nonetime
2017-03-09 17:01:56 +00:00
for k , a in times . items ( ) :
if not a [ " orig " ] : continue
2017-05-30 21:11:27 +00:00
a [ " short " ] = a [ " datetime " ] . strftime ( " % H % M " ) if len ( a [ " orig " ] ) > 5 else a [ " orig " ]
2017-09-02 11:54:20 +00:00
a [ " shortest " ] = " %02d " % a [ " datetime " ] . minute if - 300 < a [ " ut " ] - ut_now < 1800 else a [ " short " ]
2017-03-09 17:01:56 +00:00
a [ " prefix " ] = k [ 2 ] + ( " s " if k [ 0 ] == " s " else " " )
2017-03-30 15:38:56 +00:00
a [ " estimate " ] = k [ 0 ] == " e "
2017-11-01 12:03:36 +00:00
a [ " schedule " ] = k [ 0 ] == " s "
2017-03-09 17:01:56 +00:00
a [ " on_time " ] = a [ " ut " ] - times [ " s " + k [ 1 : ] ] [ " ut " ] < 300
a [ " status " ] = 1 if a [ " on_time " ] else 2
2017-04-06 09:39:59 +00:00
if " a " + k [ 1 : ] in service : a [ " status " ] = { " d " : 0 , " a " : 3 } [ k [ 2 ] ]
2017-04-04 09:20:39 +00:00
if k [ 0 ] == " s " : a [ " status " ] = 4
2017-03-09 17:01:56 +00:00
2017-11-01 12:03:36 +00:00
arr , dep = [ times [ a ] for a in a_types if times [ a ] [ " ut " ] ] , [ times [ a ] for a in d_types if times [ a ] [ " ut " ] ]
times [ " arrival " ] = ( arr + dep + [ nonetime ] ) [ 0 ]
times [ " departure " ] = ( dep + arr + [ nonetime ] ) [ 0 ]
times [ " a " ] , times [ " d " ] = ( arr + [ nonetime ] ) [ 0 ] , ( dep + [ nonetime ] ) [ 0 ]
2017-03-09 17:01:56 +00:00
times [ " both " ] = times [ " departure " ]
2017-03-21 14:05:27 +00:00
times [ " max_sched " ] = { " ut " : max ( times [ " sta " ] [ " ut " ] , times [ " std " ] [ " ut " ] ) }
2017-03-09 17:01:56 +00:00
return times
2017-12-10 15:20:48 +00:00
def activities ( self , string ) : return [ a + b . strip ( ) for a , b in list ( zip ( * [ iter ( string ) ] * 2 ) ) if ( a + b ) . strip ( ) ]
def reduced_activities ( self , string ) : return [ a for a in self . activities ( string ) if a in self . PASSENGER_ACTIVITIES ]
2017-03-09 13:33:24 +00:00
def trains ( self , event ) :
2017-02-14 11:10:18 +00:00
client = self . client
2017-04-06 09:39:59 +00:00
colours = self . COLOURS
2016-12-20 08:54:56 +00:00
2017-08-11 09:03:05 +00:00
eagle_key = self . bot . config [ " eagle-api-key " ]
eagle_url = self . bot . config [ " eagle-api-url " ]
schedule = { }
2016-12-16 10:05:54 +00:00
location_code = event [ " args_split " ] [ 0 ] . upper ( )
2016-12-21 15:50:45 +00:00
filter = self . filter ( ' ' . join ( event [ " args_split " ] [ 1 : ] ) if len ( event [ " args_split " ] ) > 1 else " " , {
2017-03-09 12:16:41 +00:00
" dest " : ( ' ' , lambda x : x . isalpha ( ) and len ( x ) == 3 ) ,
2016-12-21 15:50:45 +00:00
" origin " : ( ' ' , lambda x : x . isalpha ( ) and len ( x ) == 3 ) ,
" inter " : ( ' ' , lambda x : x . isalpha ( ) and len ( x ) == 3 , lambda x : x . upper ( ) ) ,
2017-03-09 12:16:41 +00:00
" toc " : ( ' ' , lambda x : x . isalpha ( ) and len ( x ) == 2 ) ,
2016-12-21 15:50:45 +00:00
" dedup " : ( False , lambda x : type ( x ) == type ( True ) ) ,
2017-03-09 12:16:41 +00:00
" plat " : ( ' ' , lambda x : len ( x ) < = 3 ) ,
2017-03-09 17:01:56 +00:00
" type " : ( " departure " , lambda x : x in [ " departure " , " arrival " , " both " ] ) ,
2017-01-16 16:22:09 +00:00
" terminating " : ( False , lambda x : type ( x ) == type ( True ) ) ,
2017-09-03 05:20:41 +00:00
" period " : ( 120 , lambda x : x . isdigit ( ) and 1 < = int ( x ) < = 480 , lambda x : int ( x ) ) ,
2017-08-11 09:03:05 +00:00
" nonpassenger " : ( False , lambda x : type ( x ) == type ( True ) ) ,
2017-08-27 13:41:11 +00:00
" time " : ( " " , lambda x : len ( x ) == 4 and x . isdigit ( ) ) ,
2017-10-29 12:00:08 +00:00
" date " : ( " " , lambda x : len ( x ) == 10 ) ,
2017-08-11 09:03:05 +00:00
" tops " : ( None , lambda x : len ( x ) < 4 and x . isdigit ( ) ) ,
2017-09-02 09:11:26 +00:00
" power " : ( None , lambda x : x . upper ( ) in [ " EMU " , " DMU " , " HST " , " D " , " E " ] , lambda x : x . upper ( ) ) ,
2017-09-03 05:20:41 +00:00
" crs " : ( False , lambda x : type ( x ) == type ( True ) ) ,
" st " : ( False , lambda x : type ( x ) == type ( True ) )
2016-12-21 15:50:45 +00:00
} )
2017-01-16 16:22:09 +00:00
if filter [ " errors " ] :
2017-03-09 11:57:35 +00:00
return event [ " stderr " ] . write ( " Filter: " + filter [ " errors_summary " ] )
2017-01-16 16:22:09 +00:00
2017-03-09 17:01:56 +00:00
if filter [ " inter " ] and filter [ " type " ] != " departure " :
2017-03-09 11:57:35 +00:00
return event [ " stderr " ] . write ( " Filtering by intermediate stations is only supported for departures. " )
2016-10-06 13:27:35 +00:00
2016-12-21 15:50:45 +00:00
nr_filterlist = client . factory . create ( " filterList " )
if filter [ " inter " ] : nr_filterlist . crs . append ( filter [ " inter " ] )
2017-08-27 13:41:11 +00:00
now = datetime . now ( )
if filter [ " time " ] :
now = now . replace ( hour = int ( filter [ " time " ] [ : 2 ] ) )
now = now . replace ( minute = int ( filter [ " time " ] [ 2 : ] ) )
2017-10-29 12:00:08 +00:00
if filter [ " date " ] :
newdate = datetime . strptime ( filter [ " date " ] , " % Y- % m- %d " ) . date ( )
now = now . replace ( day = newdate . day , month = newdate . month , year = newdate . year )
2017-08-27 13:41:11 +00:00
2016-12-21 15:50:45 +00:00
method = client . service . GetArrivalDepartureBoardByCRS if len ( location_code ) == 3 else client . service . GetArrivalDepartureBoardByTIPLOC
2017-03-09 11:57:35 +00:00
try :
2017-08-27 13:41:11 +00:00
query = method ( 100 , location_code , now . isoformat ( ) . split ( " . " ) [ 0 ] , filter [ " period " ] ,
2017-08-11 09:03:05 +00:00
nr_filterlist , " to " , ' ' , " PBS " , filter [ " nonpassenger " ] )
2017-03-09 11:57:35 +00:00
except WebFault as detail :
if str ( detail ) == " Server raised fault: ' Invalid crs code supplied ' " :
return event [ " stderr " ] . write ( " Invalid CRS code. " )
else :
return event [ " stderr " ] . write ( " An error occurred. " )
2017-03-08 09:35:23 +00:00
nrcc_severe = len ( [ a for a in query [ " nrccMessages " ] [ 0 ] if a [ " severity " ] == " Major " ] ) if " nrccMessages " in query else 0
2017-09-05 19:50:20 +00:00
if event . get ( " external " ) :
station_summary = " %s ( %s ) - %s ( %s ): \n " % ( query [ " locationName " ] , query [ " crs " ] , query [ " stationManager " ] ,
query [ " stationManagerCode " ] )
else :
station_summary = " %s ( %s , %s %s ) " % ( query [ " locationName " ] , query [ " crs " ] , query [ " stationManagerCode " ] ,
2018-09-21 10:38:55 +00:00
" , %s %s severe messages %s " % ( Utils . color ( nrcc_severe , Utils . COLOR_RED ) if nrcc_severe else " " ) )
2017-03-08 09:35:23 +00:00
2017-10-14 22:11:42 +00:00
if not " trainServices " in query and not " busServices " in query and not " ferryServices " in query :
2017-03-09 11:57:35 +00:00
return event [ " stdout " ] . write ( " %s : No services for the next %s minutes " % (
station_summary , filter [ " period " ] ) )
2016-12-20 12:01:16 +00:00
2016-10-06 13:27:35 +00:00
trains = [ ]
2017-10-14 22:11:42 +00:00
services = [ ]
if " trainServices " in query : services + = query [ " trainServices " ] [ 0 ]
if " busServices " in query : services + = query [ " busServices " ] [ 0 ]
if " ferryServices " in query : services + = query [ " ferryServices " ] [ 0 ]
for t in services :
2016-12-21 15:50:45 +00:00
parsed = {
2016-12-16 10:05:54 +00:00
" rid " : t [ " rid " ] ,
" uid " : t [ " uid " ] ,
" head " : t [ " trainid " ] ,
2017-08-31 18:08:09 +00:00
" platform " : ' ? ' if not " platform " in t else t [ " platform " ] ,
2017-02-26 12:27:00 +00:00
" platform_hidden " : " platformIsHidden " in t and t [ " platformIsHidden " ] ,
2017-08-31 18:08:09 +00:00
" platform_prefix " : " " ,
2016-12-21 15:50:45 +00:00
" toc " : t [ " operatorCode " ] ,
" cancelled " : t [ " isCancelled " ] if " isCancelled " in t else False ,
2017-08-29 21:53:49 +00:00
" delayed " : t [ " departureType " ] == " Delayed " if " departureType " in t else None ,
2017-03-08 09:35:23 +00:00
" cancel_reason " : t [ " cancelReason " ] [ " value " ] if " cancelReason " in t else " " ,
2017-08-29 21:53:49 +00:00
" delay_reason " : t [ " delayReason " ] [ " value " ] if " delayReason " in t else " " ,
2016-12-21 15:50:45 +00:00
" terminating " : not " std " in t and not " etd " in t and not " atd " in t ,
2017-03-09 17:01:56 +00:00
" bus " : t [ " trainid " ] == " 0B00 " ,
2017-12-10 15:20:48 +00:00
" times " : self . process ( t ) ,
" activity " : self . reduced_activities ( t [ " activities " ] ) ,
2016-12-17 17:34:19 +00:00
}
2016-12-20 12:01:16 +00:00
parsed [ " destinations " ] = [ { " name " : a [ " locationName " ] , " tiploc " : a [ " tiploc " ] ,
" crs " : a [ " crs " ] if " crs " in a else ' ' , " code " : a [ " crs " ] if " crs "
in a else a [ " tiploc " ] , " via " : a [ " via " ] if " via " in a else ' ' }
for a in t [ " destination " ] [ 0 ] ]
parsed [ " origins " ] = [ { " name " : a [ " locationName " ] , " tiploc " : a [ " tiploc " ] ,
" crs " : a [ " crs " ] if " crs " in a else ' ' , " code " : a [ " crs " ] if " crs "
in a else a [ " tiploc " ] , " via " : a [ " via " ] if " via " in a else ' ' }
for a in t [ " origin " ] [ 0 ] ]
2016-10-06 13:27:35 +00:00
2016-12-21 15:50:45 +00:00
parsed [ " departure_only " ] = location_code in [ a [ " code " ] for a in parsed [ " origins " ] ]
2017-08-29 21:53:49 +00:00
if parsed [ " cancelled " ] or parsed [ " delayed " ] :
2017-03-09 17:01:56 +00:00
for k , time in parsed [ " times " ] . items ( ) :
2017-08-29 21:53:49 +00:00
time [ " short " ] , time [ " on_time " ] , time [ " status " ] , time [ " prefix " ] = (
2017-09-05 19:50:20 +00:00
" %s : %s " % ( " C " if parsed [ " cancel_reason " ] else " D " , parsed [ " cancel_reason " ] or parsed [ " delay_reason " ] or " ? " ) ,
2017-08-29 21:53:49 +00:00
False , 2 , " "
)
2016-12-20 08:54:56 +00:00
2016-12-17 17:34:19 +00:00
trains . append ( parsed )
2016-12-16 10:05:54 +00:00
2017-08-11 09:03:05 +00:00
if eagle_url :
2018-02-04 09:27:35 +00:00
summary_query = Utils . get_url ( " %s /json/summaries/ %s ?uids= %s " % ( eagle_url , now . date ( ) . isoformat ( ) , " % 20 " . join ( [ a [ " uid " ] for a in trains ] ) ) , json = True , headers = { " x-eagle-key " : self . bot . config [ " eagle-api-key " ] } )
2017-08-11 09:03:05 +00:00
if summary_query :
for t in trains :
2017-08-31 18:08:09 +00:00
summary = summary_query [ t [ " uid " ] ]
t . update ( summary )
2017-09-02 09:11:26 +00:00
summary_plat = summary . get ( " platforms " , { } ) . get ( query [ " crs " ] )
2017-08-31 18:08:09 +00:00
if summary_plat and t [ " platform " ] == " ? " :
t [ " platform " ] , t [ " platform_prefix " ] = summary_plat , " s "
2017-08-11 09:03:05 +00:00
2016-11-04 17:06:07 +00:00
for t in trains :
2017-09-02 11:37:08 +00:00
t [ " dest_summary " ] = " / " . join ( [ " %s %s " % ( a [ " code " ] * filter [ " crs " ] or a [ " name " ] , " " + a [ " via " ]
2016-12-20 12:01:16 +00:00
if a [ " via " ] else ' ' ) for a in t [ " destinations " ] ] )
2017-09-02 11:37:08 +00:00
t [ " origin_summary " ] = " / " . join ( [ " %s %s " % ( a [ " code " ] * filter [ " crs " ] or a [ " name " ] , " " + a [ " via " ]
2016-12-21 15:50:45 +00:00
if a [ " via " ] else ' ' ) for a in t [ " origins " ] ] )
2016-11-04 17:06:07 +00:00
2017-03-21 14:05:27 +00:00
trains = sorted ( trains , key = lambda t : t [ " times " ] [ " max_sched " ] [ " ut " ] if filter [ " type " ] == " both " else t [ " times " ] [ " st " + filter [ " type " ] [ 0 ] ] [ " ut " ] )
2016-10-06 13:27:35 +00:00
trains_filtered = [ ]
2016-12-21 15:50:45 +00:00
train_locs_toc = [ ]
2016-10-06 13:27:35 +00:00
for train in trains :
2016-12-21 15:50:45 +00:00
if not True in [
( train [ " destinations " ] , train [ " toc " ] ) in train_locs_toc and ( filter [ " dedup " ] or filter [ " default " ] ) ,
filter [ " dest " ] and not filter [ " dest " ] . upper ( ) in [ a [ " code " ] for a in train [ " destinations " ] ] ,
filter [ " origin " ] and not filter [ " origin " ] . upper ( ) in [ a [ " code " ] for a in train [ " origins " ] ] ,
filter [ " toc " ] and not filter [ " toc " ] . upper ( ) == train [ " toc " ] ,
filter [ " plat " ] and not filter [ " plat " ] == train [ " platform " ] ,
2017-03-09 17:01:56 +00:00
filter [ " type " ] == " departure " and train [ " terminating " ] ,
filter [ " type " ] == " arrival " and train [ " departure_only " ] ,
2017-08-11 09:03:05 +00:00
filter [ " terminating " ] and not train [ " terminating " ] ,
filter [ " tops " ] and not filter [ " tops " ] in train . get ( " tops_possible " , [ ] ) ,
filter [ " power " ] and not filter [ " power " ] == train . get ( " power_type " , None ) ,
2016-12-21 15:50:45 +00:00
] :
train_locs_toc . append ( ( train [ " destinations " ] , train [ " toc " ] ) )
trains_filtered . append ( train )
2017-09-05 19:50:20 +00:00
if event . get ( " external " ) :
trains_string = " \n " . join ( [ " %-6s %-4s %-2s %-3s %1s %-6s %1s %s " % (
t [ " uid " ] , t [ " head " ] , t [ " toc " ] , " bus " if t [ " bus " ] else t [ " platform " ] ,
" ~ " if t [ " times " ] [ " both " ] [ " estimate " ] else ' ' ,
t [ " times " ] [ " both " ] [ " prefix " ] + t [ " times " ] [ " both " ] [ " short " ] ,
2017-10-14 22:11:42 +00:00
" ← " if t [ " terminating " ] or filter [ " type " ] == " arrival " else " → " ,
2017-09-05 19:50:20 +00:00
t [ " origin_summary " ] if t [ " terminating " ] or filter [ " type " ] == " arrival " else t [ " dest_summary " ]
) for t in trains_filtered ] )
else :
2018-09-21 10:38:55 +00:00
trains_string = " , " . join ( [ " %s %s ( %s , %s %s %s %s , %s %s %s ) " % (
2017-09-05 19:50:20 +00:00
" from " if not filter [ " type " ] [ 0 ] in " ad " and t [ " terminating " ] else ' ' ,
t [ " origin_summary " ] if t [ " terminating " ] or filter [ " type " ] == " arrival " else t [ " dest_summary " ] ,
t [ " uid " ] ,
t [ " platform_prefix " ] ,
" bus " if t [ " bus " ] else t [ " platform " ] ,
" * " if t [ " platform_hidden " ] else ' ' ,
" ? " if " platformsAreUnreliable " in query and query [ " platformsAreUnreliable " ] else ' ' ,
t [ " times " ] [ filter [ " type " ] ] [ " prefix " ] . replace ( filter [ " type " ] [ 0 ] , ' ' ) if not t [ " cancelled " ] else " " ,
2018-09-21 10:38:55 +00:00
Utils . color ( t [ " times " ] [ filter [ " type " ] ] [ " shortest " * filter [ " st " ] or " short " ] , colours [ t [ " times " ] [ filter [ " type " ] ] [ " status " ] ] ) ,
2017-12-10 15:20:48 +00:00
bool ( t [ " activity " ] ) * " , " + " + " . join ( t [ " activity " ] ) ,
2017-09-05 19:50:20 +00:00
) for t in trains_filtered ] )
if event . get ( " external " ) :
event [ " stdout " ] . write ( " %s %s \n %s " % (
station_summary , " \n calling at %s " % filter [ " inter " ] if filter [ " inter " ] else ' ' , trains_string ) )
else :
event [ " stdout " ] . write ( " %s %s : %s " % ( station_summary , " departures calling at %s " % filter [ " inter " ] if filter [ " inter " ] else ' ' , trains_string ) )
2016-11-02 01:28:47 +00:00
def service ( self , event ) :
2017-02-14 11:10:18 +00:00
client = self . client
2017-04-06 09:39:59 +00:00
colours = self . COLOURS
2017-09-05 19:50:20 +00:00
external = event . get ( " external " , False )
2016-11-04 16:11:03 +00:00
2017-05-30 21:11:27 +00:00
SCHEDULE_STATUS = { " B " : " perm bus " , " F " : " freight train " , " P " : " train " ,
" S " : " ship " , " T " : " trip " , " 1 " : " train " , " 2 " : " freight " ,
" 3 " : " trip " , " 4 " : " ship " , " 5 " : " bus " }
eagle_key = self . bot . config [ " eagle-api-key " ]
eagle_url = self . bot . config [ " eagle-api-url " ]
schedule = { }
2017-09-05 19:50:20 +00:00
sources = [ ]
2017-05-30 21:11:27 +00:00
2016-12-16 10:05:54 +00:00
service_id = event [ " args_split " ] [ 0 ]
2016-12-21 15:50:45 +00:00
filter = self . filter ( ' ' . join ( event [ " args_split " ] [ 1 : ] ) if len ( event [ " args_split " ] ) > 1 else " " , {
2017-03-30 15:38:56 +00:00
" passing " : ( False , lambda x : type ( x ) == type ( True ) ) ,
" type " : ( " arrival " , lambda x : x in [ " arrival " , " departure " ] )
2016-12-21 15:50:45 +00:00
} )
2016-11-04 17:06:07 +00:00
2017-01-16 16:22:09 +00:00
if filter [ " errors " ] :
event [ " stderr " ] . write ( " Filter: " + filter [ " errors_summary " ] )
return
2016-12-16 10:05:54 +00:00
rid = service_id
if len ( service_id ) < = 8 :
query = client . service . QueryServices ( service_id , datetime . utcnow ( ) . date ( ) . isoformat ( ) ,
datetime . utcnow ( ) . time ( ) . strftime ( " % H: % M: % S+0000 " ) )
2017-05-30 21:11:27 +00:00
if eagle_url :
2018-02-04 09:27:35 +00:00
schedule_query = Utils . get_url ( " %s /json/schedule/ %s / %s " % ( eagle_url , service_id , datetime . now ( ) . date ( ) . isoformat ( ) ) , json = True , headers = { " x-eagle-key " : eagle_key } )
2017-11-03 19:24:27 +00:00
if schedule_query :
schedule = schedule_query [ " current " ]
2017-05-30 21:11:27 +00:00
if not query and not schedule :
2017-03-09 11:57:35 +00:00
return event [ " stdout " ] . write ( " No service information is available for this identifier. " )
2017-05-30 21:11:27 +00:00
if query and len ( query [ " serviceList " ] [ 0 ] ) > 1 :
2017-03-09 11:57:35 +00:00
return event [ " stdout " ] . write ( " Identifier refers to multiple services: " +
2017-02-16 10:42:46 +00:00
" , " . join ( [ " %s ( %s -> %s ) " % ( a [ " uid " ] , a [ " originCrs " ] , a [ " destinationCrs " ] ) for a in query [ " serviceList " ] [ 0 ] ] ) )
2017-05-30 21:11:27 +00:00
if query : rid = query [ " serviceList " ] [ 0 ] [ 0 ] [ " rid " ]
if query :
2017-09-05 19:50:20 +00:00
sources . append ( " LDBSVWS " )
2017-05-30 21:11:27 +00:00
query = client . service . GetServiceDetailsByRID ( rid )
if schedule :
2017-11-02 22:56:00 +00:00
sources . append ( " Eagle/SCHEDULE " )
2017-12-04 18:28:13 +00:00
if not query : query = { " trainid " : schedule [ " signalling_id " ] or " 0000 " , " operator " : schedule [ " operator_name " ] or schedule [ " atoc_code " ] }
2017-12-04 17:50:06 +00:00
stype = " class %s %s " % ( schedule_query [ " tops_inferred " ] , schedule [ " power_type " ] ) if schedule_query [ " tops_inferred " ] else schedule [ " power_type " ]
2017-05-30 21:11:27 +00:00
for k , v in {
" operatorCode " : schedule [ " atoc_code " ] ,
2017-12-04 17:50:06 +00:00
" serviceType " : stype if stype else SCHEDULE_STATUS [ schedule [ " status " ] ] ,
2017-05-30 21:11:27 +00:00
} . items ( ) :
query [ k ] = v
2016-12-16 10:05:54 +00:00
2016-12-17 17:34:19 +00:00
disruptions = [ ]
if " cancelReason " in query :
2017-02-16 10:32:45 +00:00
disruptions . append ( " Cancelled ( %s %s ) " % ( query [ " cancelReason " ] [ " value " ] , " at " + query [ " cancelReason " ] [ " _tiploc " ] if query [ " cancelReason " ] [ " _tiploc " ] else " " ) )
2016-12-17 17:34:19 +00:00
if " delayReason " in query :
2017-02-16 10:32:45 +00:00
disruptions . append ( " Delayed ( %s %s ) " % ( query [ " delayReason " ] [ " value " ] , " at " + query [ " delayReason " ] [ " _tiploc " ] if query [ " delayReason " ] [ " _tiploc " ] else " " ) )
2017-09-05 19:50:20 +00:00
if disruptions and not external :
2018-09-21 10:38:55 +00:00
disruptions = Utils . color ( " , " . join ( disruptions ) , Utils . COLOR_RED ) + " "
2017-09-05 19:50:20 +00:00
elif disruptions and external :
disruptions = " , " . join ( disruptions )
2016-12-17 17:34:19 +00:00
else : disruptions = " "
2016-12-16 10:05:54 +00:00
stations = [ ]
2017-12-04 17:50:06 +00:00
for station in query [ " locations " ] [ 0 ] if " locations " in query else schedule [ " locations " ] :
2017-05-30 21:11:27 +00:00
if " locations " in query :
parsed = { " name " : station [ " locationName " ] ,
" crs " : ( station [ " crs " ] if " crs " in station else station [ " tiploc " ] ) . rstrip ( ) ,
2017-09-05 19:50:20 +00:00
" tiploc " : station [ " tiploc " ] . rstrip ( ) ,
2017-05-30 21:11:27 +00:00
" called " : " atd " in station ,
" passing " : station [ " isPass " ] if " isPass " in station else False ,
" first " : len ( stations ) == 0 ,
" last " : False ,
" cancelled " : station [ " isCancelled " ] if " isCancelled " in station else False ,
2017-10-31 10:58:05 +00:00
" associations " : [ ] ,
2017-05-30 21:11:27 +00:00
" length " : station [ " length " ] if " length " in station else None ,
2017-09-05 19:50:20 +00:00
" times " : self . process ( station ) ,
2017-12-10 15:20:48 +00:00
" platform " : station [ " platform " ] if " platform " in station else None ,
" activity " : self . activities ( station [ " activities " ] ) if " activities " in station else [ ] ,
" activity_p " : self . reduced_activities ( station [ " activities " ] ) if " activities " in station else [ ] ,
2017-05-30 21:11:27 +00:00
}
if parsed [ " cancelled " ] :
2017-10-23 15:13:58 +00:00
parsed [ " times " ] [ " arrival " ] . update ( { " short " : " Cancelled " , " on_time " : False , " status " : 2 } )
parsed [ " times " ] [ " departure " ] . update ( { " short " : " Cancelled " , " on_time " : False , " status " : 2 } )
2017-05-30 21:11:27 +00:00
2017-10-31 10:58:05 +00:00
associations = station [ " associations " ] [ 0 ] if " associations " in station else [ ]
for assoc in associations :
parsed_assoc = {
" uid_assoc " : assoc . uid ,
" category " : { " divide " : " VV " , " join " : " JJ " , " next " : " NP " } [ assoc [ " category " ] ] ,
2017-10-31 11:57:22 +00:00
" from " : parsed [ " first " ] , " direction " : assoc [ " destTiploc " ] . rstrip ( ) == parsed [ " tiploc " ] ,
2017-10-31 10:58:05 +00:00
" origin_name " : assoc [ " origin " ] , " origin_tiploc " : assoc [ " originTiploc " ] ,
" origin_crs " : assoc [ " originCRS " ] if " originCRS " in assoc else None ,
" dest_name " : assoc [ " destination " ] , " dest_tiploc " : assoc [ " destTiploc " ] ,
" dest_crs " : assoc [ " destCRS " ] if " destCRS " in assoc else None ,
" far_name " : assoc [ " destination " ] , " far_tiploc " : assoc [ " destTiploc " ] ,
" far_crs " : assoc [ " destCRS " ] if " destCRS " in assoc else None ,
}
2017-10-31 11:57:22 +00:00
if parsed_assoc [ " direction " ] :
2017-10-31 10:58:05 +00:00
parsed_assoc . update ( { " far_name " : parsed_assoc [ " origin_name " ] ,
" far_tiploc " : parsed_assoc [ " origin_tiploc " ] , " far_crs " : parsed_assoc [ " origin_crs " ] } )
parsed [ " associations " ] . append ( parsed_assoc )
2017-05-30 21:11:27 +00:00
else :
2018-02-04 09:42:09 +00:00
parsed = { " name " : ( station [ " name " ] or " none " ) ,
2017-12-04 17:50:06 +00:00
" crs " : station [ " crs " ] if station [ " crs " ] else station [ " tiploc " ] ,
" tiploc " : station [ " tiploc " ] ,
2017-05-30 21:11:27 +00:00
" called " : False ,
2017-10-31 10:58:05 +00:00
" passing " : bool ( station . get ( " pass " ) ) ,
2017-05-30 21:11:27 +00:00
" first " : len ( stations ) == 0 ,
" last " : False ,
" cancelled " : False ,
" length " : None ,
2017-09-05 19:50:20 +00:00
" times " : self . process ( station [ " dolphin_times " ] ) ,
2017-10-31 10:58:05 +00:00
" platform " : station [ " platform " ] ,
2017-12-10 15:20:48 +00:00
" associations " : station [ " associations " ] or [ ] ,
" activity " : self . activities ( station [ " activity " ] ) ,
" activity_p " : self . reduced_activities ( station [ " activity " ] ) ,
2017-05-30 21:11:27 +00:00
}
2016-12-17 17:34:19 +00:00
stations . append ( parsed )
2016-12-16 10:05:54 +00:00
[ a for a in stations if a [ " called " ] or a [ " first " ] ] [ - 1 ] [ " last " ] = True
2016-12-17 17:34:19 +00:00
for station in stations [ 0 : [ k for k , v in enumerate ( stations ) if v [ " last " ] ] [ 0 ] ] :
2016-12-16 10:05:54 +00:00
if not station [ " first " ] : station [ " called " ] = True
2016-11-02 01:28:47 +00:00
for station in stations :
2017-10-31 10:58:05 +00:00
for assoc in station [ " associations " ] :
2017-10-31 23:13:16 +00:00
assoc [ " summary " ] = " {arrow} {assoc[category]} {assoc[uid_assoc]} {dir_arrow} {assoc[far_name]} ( {code} ) " . format ( assoc = assoc , arrow = assoc [ " from " ] * " <- " or " -> " , dir_arrow = ( assoc [ " direction " ] ) * " <- " or " -> " , code = assoc [ " far_crs " ] or assoc [ " far_tiploc " ] )
2017-10-31 10:58:05 +00:00
2017-03-30 15:38:56 +00:00
if station [ " passing " ] :
2017-04-06 09:39:59 +00:00
station [ " times " ] [ " arrival " ] [ " status " ] , station [ " times " ] [ " departure " ] [ " status " ] = 5 , 5
elif station [ " called " ] :
station [ " times " ] [ " arrival " ] [ " status " ] , station [ " times " ] [ " departure " ] [ " status " ] = 0 , 0
2016-11-04 16:11:03 +00:00
2018-09-21 10:38:55 +00:00
station [ " summary " ] = " %s %s ( %s %s %s %s %s ) %s " % (
2017-10-31 10:58:05 +00:00
" * " * station [ " passing " ] ,
2017-03-21 14:05:27 +00:00
station [ " name " ] ,
station [ " crs " ] + " , " if station [ " name " ] != station [ " crs " ] else ' ' ,
2017-11-01 12:03:36 +00:00
station [ " length " ] + " cars, " if station [ " length " ] and ( station [ " first " ] or ( station [ " last " ] ) or station [ " associations " ] ) else ' ' ,
2017-03-30 15:38:56 +00:00
( " ~ " if station [ " times " ] [ filter [ " type " ] ] [ " estimate " ] else ' ' ) +
station [ " times " ] [ filter [ " type " ] ] [ " prefix " ] . replace ( filter [ " type " ] [ 0 ] , " " ) ,
2018-09-21 10:38:55 +00:00
Utils . color ( station [ " times " ] [ filter [ " type " ] ] [ " short " ] , colours [ station [ " times " ] [ filter [ " type " ] ] [ " status " ] ] ) ,
2017-12-10 15:20:48 +00:00
" , " * bool ( station [ " activity_p " ] ) + " + " . join ( station [ " activity_p " ] ) ,
2017-10-31 23:13:16 +00:00
" , " . join ( [ a [ " summary " ] for a in station [ " associations " ] ] ) ,
2016-11-04 16:11:03 +00:00
)
2017-12-10 15:20:48 +00:00
station [ " summary_external " ] = " %1s %-5s %1s %-5s %-3s %-3s %-3s %s %s " % (
" ~ " * station [ " times " ] [ " a " ] [ " estimate " ] + " s " * ( station [ " times " ] [ " a " ] [ " schedule " ] ) ,
2017-11-01 12:03:36 +00:00
station [ " times " ] [ " a " ] [ " short " ] ,
2017-12-10 15:20:48 +00:00
" ~ " * station [ " times " ] [ " d " ] [ " estimate " ] + " s " * ( station [ " times " ] [ " d " ] [ " schedule " ] ) ,
2017-11-01 12:03:36 +00:00
station [ " times " ] [ " d " ] [ " short " ] ,
2017-12-10 15:20:48 +00:00
station [ " platform " ] or ' ' ,
" , " . join ( station [ " activity " ] ) or ' ' ,
2017-09-05 19:50:20 +00:00
station [ " crs " ] or station [ " tiploc " ] ,
2017-10-31 10:58:05 +00:00
station [ " name " ] ,
2017-10-31 23:13:16 +00:00
" \n " + " \n " . join ( [ a [ " summary " ] for a in station [ " associations " ] ] ) if station [ " associations " ] else " " ,
2017-09-05 19:50:20 +00:00
)
2016-11-02 01:28:47 +00:00
2016-12-16 10:05:54 +00:00
stations_filtered = [ ]
for station in stations :
2016-12-21 15:50:45 +00:00
if station [ " passing " ] and not filter [ " passing " ] : continue
2017-09-05 19:50:20 +00:00
if station [ " called " ] and filter [ " default " ] and not external :
2016-12-16 10:05:54 +00:00
if not station [ " first " ] and not station [ " last " ] :
continue
2016-12-21 15:50:45 +00:00
2016-12-16 10:05:54 +00:00
stations_filtered . append ( station )
2017-09-05 19:50:20 +00:00
if station [ " first " ] and not station [ " last " ] and filter [ " default " ] and not external :
stations_filtered . append ( { " summary " : " (...) " , " summary_external " : " (...) " } )
2016-12-16 10:05:54 +00:00
2016-11-02 01:28:47 +00:00
done_count = len ( [ s for s in stations if s [ " called " ] ] )
total_count = len ( stations )
2017-09-05 19:50:20 +00:00
if external :
event [ " stdout " ] . write ( " %s : %s \n %s %s ( %s ) %s %s \n \n %s " % (
service_id , " , " . join ( sources ) ,
disruptions + " \n " if disruptions else ' ' ,
query [ " operator " ] , query [ " operatorCode " ] , query [ " trainid " ] , query [ " serviceType " ] ,
" \n " . join ( [ s [ " summary_external " ] for s in stations_filtered ] )
) )
else :
event [ " stdout " ] . write ( " %s %s %s %s ( %s %s %s / %s / %s ): %s " % ( disruptions , query [ " operatorCode " ] ,
query [ " trainid " ] , query [ " serviceType " ] ,
2018-09-21 10:38:55 +00:00
Utils . color ( done_count , Utils . COLOR_LIGHTBLUE ) ,
2017-09-05 19:50:20 +00:00
len ( stations_filtered ) , total_count ,
" , " . join ( [ s [ " summary " ] for s in stations_filtered ] ) ) )
2016-12-16 10:05:54 +00:00
def head ( self , event ) :
2017-02-14 11:10:18 +00:00
client = self . client
2016-12-16 10:05:54 +00:00
service_id = event [ " args_split " ] [ 0 ]
query = client . service . QueryServices ( service_id , datetime . utcnow ( ) . date ( ) . isoformat ( ) ,
datetime . utcnow ( ) . time ( ) . strftime ( " % H: % M: % S+0000 " ) )
2017-10-23 15:13:58 +00:00
if not query :
return event [ " stderr " ] . write ( " No currently running services match this identifier " )
2017-10-14 22:11:42 +00:00
services = query [ " serviceList " ] [ 0 ]
if event . get ( " external " ) :
event [ " stdout " ] . write ( " \n " . join ( [ " {a.uid:6} {a.trainid:4} {a.originName} ( {a.originCrs} ) → {a.destinationName} ( {a.destinationCrs} ) " . format ( a = a ) for a in services ] ) )
else :
event [ " stdout " ] . write ( " , " . join ( [ " h/ %s r/ %s u/ %s rs/ %s %s ( %s ) -> %s ( %s ) " % ( a [ " trainid " ] , a [ " rid " ] , a [ " uid " ] , a [ " rsid " ] , a [ " originName " ] , a [ " originCrs " ] , a [ " destinationName " ] , a [ " destinationCrs " ] ) for a in services ] ) )
2017-02-14 11:10:18 +00:00
def service_code ( self , event ) :
client = self . client
if not event [ " args " ] . isnumeric ( ) :
2017-03-09 11:57:35 +00:00
return event [ " stderr " ] . write ( " The delay/cancellation code must be a number " )
2017-02-14 11:10:18 +00:00
reasons = { a [ " code " ] : ( a [ " lateReason " ] , a [ " cancReason " ] ) for a in client . service . GetReasonCodeList ( ) [ 0 ] }
if event [ " args " ] in reasons :
event [ " stdout " ] . write ( " %s : %s " % ( event [ " args " ] , " / " . join ( reasons [ event [ " args " ] ] ) ) )
else :
event [ " stdout " ] . write ( " This doesn ' t seem to be a valid reason code " )