/ MagTag_NextBus / nextbus_routefinder.py
nextbus_routefinder.py
1 # SPDX-FileCopyrightText: 2020 Phillip Burgess for Adafruit Industries 2 # 3 # SPDX-License-Identifier: MIT 4 5 #!/usr/bin/env python 6 7 """ 8 Nextbus route finder, for selecting bus routes/stops for use with the other 9 NextBus-related scripts. Crude textual interface is best used w/terminal 10 with scroll-back ability. Only need to use this for setup, hence the very 11 basic implementation. Prompts user for transit agency, bus line, direction 12 and stop, issues a string which can then be copied & pasted into predictor 13 program. Not fancy, just uses text prompts, minimal error checking. 14 15 THIS IS FOR "FULL" PYTHON USE, NOT CIRCUITPYTHON. 16 """ 17 18 # pylint: disable=superfluous-parens 19 20 from xml.dom.minidom import parseString 21 from six.moves import input # Python 3 input() vs Python 2 input_raw() 22 try: 23 from urllib import request # Python 3 24 except ImportError: 25 import urllib as request # Python 2 26 27 def print_numbered_list(items): 28 """Given a list, print it with a number (1-N) for each item and 29 the value corresponding to the 'title' attribute string.""" 30 for index, item in enumerate(items): 31 print(str(index + 1) + ') ' + item.getAttribute('title')) 32 33 def get_number(prompt, upper_limit): 34 """Prompt user for a number in a given range 1 to N. 35 Return value is actually 0 to N-1.""" 36 while True: 37 number = input('Enter ' + prompt + ' 1-' + str(upper_limit) + ': ') 38 try: 39 number = int(number) - 1 40 except ValueError: 41 continue # Ignore non-numbers 42 if 0 <= number < upper_limit: 43 return number # and out-of-range values 44 45 def req(cmd): 46 """Open connection, issue request, read & parse XML response.""" 47 connection = request.urlopen( 48 'http://webservices.nextbus.com' + 49 '/service/publicXMLFeed?command=' + cmd) 50 raw = connection.read() 51 connection.close() 52 return parseString(raw) 53 54 # Main application, kinda brute-force code ----------------------------------- 55 56 # Get list of transit agencies, prompt user for selection, get agency tag. 57 DOM = req('agencyList') 58 ELEMENTS = DOM.getElementsByTagName('agency') 59 print('TRANSIT AGENCIES:') 60 print_numbered_list(ELEMENTS) 61 NUMBER = get_number('transit agency', len(ELEMENTS)) 62 AGENCY_TAG = ELEMENTS[NUMBER].getAttribute('tag') 63 64 # Get list of routes for selected agency, prompt user, get route tag. 65 DOM = req('routeList&a=' + AGENCY_TAG) 66 ELEMENTS = DOM.getElementsByTagName('route') 67 print('\nROUTES:') 68 print_numbered_list(ELEMENTS) 69 NUMBER = get_number('route', len(ELEMENTS)) 70 ROUTE_TAG = ELEMENTS[NUMBER].getAttribute('tag') 71 72 # Get list of directions for selected agency & route, prompt user... 73 DOM = req('routeConfig&a=' + AGENCY_TAG + '&r=' + ROUTE_TAG) 74 ELEMENTS = DOM.getElementsByTagName('direction') 75 print('\nDIRECTIONS:') 76 print_numbered_list(ELEMENTS) 77 NUMBER = get_number('direction', len(ELEMENTS)) 78 DIR_TITLE = ELEMENTS[NUMBER].getAttribute('title') # Save for later 79 # ...then get list of stop numbers and descriptions -- these are 80 # nested in different parts of the XML and must be cross-referenced 81 STOP_NUMBERS = ELEMENTS[NUMBER].getElementsByTagName('stop') 82 STOP_DESCRIPTIONS = DOM.getElementsByTagName('stop') 83 84 # Cross-reference stop numbers and descriptions to provide a readable 85 # list of available stops for selected agency, route & direction. 86 # Prompt user for stop number and get corresponding stop tag. 87 print('\nSTOPS:') 88 for INDEX, ITEM in enumerate(STOP_NUMBERS): 89 STOP_NUM_TAG = ITEM.getAttribute('tag') 90 for desc in STOP_DESCRIPTIONS: 91 STOP_DESCRIPTION = desc.getAttribute('tag') 92 if STOP_NUM_TAG == STOP_DESCRIPTION: 93 print(str(INDEX + 1) + ') ' + desc.getAttribute('title')) 94 break 95 NUMBER = get_number('stop', len(STOP_NUMBERS)) 96 STOP_TAG = STOP_NUMBERS[NUMBER].getAttribute('tag') 97 98 # The prediction server wants the stop tag, NOT the stop ID, not sure 99 # what's up with that. 100 101 print('\nCOPY/PASTE INTO APPLICATION SCRIPT:') 102 print(" ('" + AGENCY_TAG + "', '" + ROUTE_TAG + "', '" + STOP_TAG + 103 "', '" + DIR_TITLE + "'),")