/ 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 + "'),")