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.

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