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.

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