You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

175 lines
5.4 KiB

7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
7 years ago
  1. from dateutil.parser import parse as date_parse
  2. import argparse
  3. import ctabus
  4. import datetime
  5. import os
  6. import re
  7. import time
  8. # for logging
  9. import os.path as osp
  10. import sys
  11. import re
  12. # https://stackoverflow.com/a/5967539
  13. def atoi(text):
  14. return int(text) if text.isdigit() else text
  15. def numb_sort(text):
  16. '''
  17. alist.sort(key=natural_keys) sorts in human order
  18. http://nedbatchelder.com/blog/200712/human_sorting.html
  19. (See Toothy's implementation in the comments)
  20. '''
  21. return [ atoi(c) for c in re.split(r'(\d+)', text) ]
  22. def clearscr():
  23. os.system('cls' if os.name == 'nt' else 'clear')
  24. def pprint_delta(delta):
  25. delta = str(delta)
  26. days= None
  27. s1 = delta.split(', ')
  28. if len(s1) > 1:
  29. days,time = s1
  30. else:
  31. time = s1[0]
  32. time = time.split('.')[0]
  33. hour,minute,second = map(int,time.split(':'))
  34. time = ''
  35. if hour:
  36. time += '{hour} hour'.format(hour=hour) + ('s' if hour != 1 else '')
  37. if minute:
  38. if time and not time.endswith(', '):
  39. time += ', '
  40. time += '{minute} minute'.format(minute=minute) + ('s' if minute != 1 else '')
  41. if second:
  42. if time and not time.endswith(', '):
  43. time += ', '
  44. time += '{second} second'.format(second=second) + ('s' if second != 1 else '')
  45. ret = ''
  46. if days:
  47. ret = days + ', ' if time else ''
  48. ret += time
  49. return ret
  50. def gen_list(objs,data,*displays,key = None,sort = 0,num_pic = True):
  51. k = displays[sort]
  52. display_data = {obj[k]:obj[data] for obj in objs}
  53. srt_keys = sorted(display_data.keys(),key=key)
  54. display = sorted(
  55. [
  56. [obj[d] for d in displays] for obj in objs
  57. ],
  58. key = lambda row: key(row[sort]) if key else row[sort]
  59. )
  60. if num_pic:
  61. display = [[i] + data for i,data in enumerate(display)]
  62. opts = {
  63. 'spacer':' ',
  64. 'seperator':' ',
  65. 'interactive': True,
  66. 'bottom':'=',
  67. 'l_end':'<',
  68. 'r_end':'>',
  69. }
  70. print2d(display,**opts)
  71. if num_pic:
  72. which = None
  73. while not which:
  74. try:
  75. which = input('Which one?: ')
  76. except KeyboardInterrupt:
  77. quit()
  78. try:
  79. which = srt_keys[int(which)]
  80. except ValueError:
  81. which = None
  82. return display_data[which]
  83. else:
  84. ret = None
  85. while not ret:
  86. try:
  87. ret = display_data[input('Which one?: ')]
  88. except KeyError:
  89. pass
  90. return ret
  91. config = '''\
  92. {route} - {end} ({direction})
  93. {nm}, stop {stop_id}
  94. {delta} ({t})\
  95. '''
  96. def show(data,rt_filter=None,_clear=False):
  97. times = data['prd']
  98. today = datetime.datetime.today()
  99. arrivals = sorted(times,key = lambda t: t['prdtm'])
  100. if rt_filter is not None:
  101. arrivals =filter(lambda arrival: arrival['rt'] == rt_filter,arrivals)
  102. if _clear:
  103. clearscr()
  104. for time in arrivals:
  105. arrival = date_parse(time['prdtm'])
  106. if arrival > today:
  107. stop_id = time['stpid']
  108. delta = pprint_delta(arrival-today)
  109. t = arrival.strftime('%H:%M:%S')
  110. route = time['rt']
  111. direction = time['rtdir']
  112. end = time['des']
  113. nm = time['stpnm'].rstrip()
  114. print(
  115. config.format(**locals()),end= '\n'*2
  116. )
  117. if __name__ == '__main__':
  118. parser = argparse.ArgumentParser(prog = 'ctabus')
  119. parser.add_argument('-l','--lucky',action='store_true',help = 'picks first result')
  120. parser.add_argument('-p','--periodic',metavar = 'SEC',type=int,help='checks periodically')
  121. parser.add_argument('-r','--route',default = None)
  122. parser.add_argument('-d','--direction',default = None)
  123. parser.add_argument('arg',nargs = '+',metavar = '(stop-id | cross streets)')
  124. args = parser.parse_args()
  125. sys.stderr = open(osp.join(osp.dirname(__file__),'stderr.log'),'w')
  126. args.arg = ' '.join(args.arg)
  127. if not args.arg.isdecimal():
  128. # save on import time slightly
  129. from print2d import print2d
  130. from search import Search,StopSearch
  131. #routes
  132. if not args.route:
  133. data = ctabus.get_routes()['routes']
  134. route = gen_list(data,'rt','rt','rtnm',num_pic = False,key=numb_sort)
  135. else:
  136. route = args.route
  137. data = ctabus.get_directions(route)['directions']
  138. #direction
  139. if not args.direction:
  140. direction = gen_list(data,'dir','dir')
  141. else:
  142. s = Search(args.direction)
  143. direction = sorted((obj['dir'] for obj in data),key = s)[0]
  144. #direction
  145. stops = ctabus.get_stops(route,direction)['stops']
  146. s = StopSearch(args.arg)
  147. if args.lucky:
  148. stop_id = sorted(stops,key=lambda stop: s(stop['stpnm']))[0]['stpid']
  149. else:
  150. stop_id = gen_list(stops,'stpid','stpnm',key = s)
  151. else:
  152. stop_id = args.arg
  153. data = ctabus.get_times(stop_id)
  154. if args.periodic is not None:
  155. _done = False
  156. while not _done:
  157. try:
  158. show(data,args.route,True)
  159. s = time.perf_counter()
  160. data = ctabus.get_times(stop_id)
  161. e = time.perf_counter() - s
  162. if e < args.periodic:
  163. time.sleep(args.periodic-e)
  164. except KeyboardInterrupt as e:
  165. _done = True
  166. else:
  167. show(data,args.route)