From 776acc6cd90b5e1d99af1e8d06aff7b179af2620 Mon Sep 17 00:00:00 2001 From: Roger Filmyer Date: Sat, 19 Nov 2016 13:25:45 -0800 Subject: [PATCH 1/5] setup.py changes to get pip to actually install the package --- setup.py | 14 ++++++-------- 1 file changed, 6 insertions(+), 8 deletions(-) diff --git a/setup.py b/setup.py index 9b3b26f..0da0fc8 100644 --- a/setup.py +++ b/setup.py @@ -3,8 +3,6 @@ import os import sys -import nextbus - try: from setuptools import setup except ImportError: @@ -18,19 +16,19 @@ 'nextbus', ] -requires = [] +requires = ['requests>=2.0.1', 'lxml>=3.2.4'] readme ='' history ='' setup( name='python-nextbus', - version=nextbus.__version__, + version='0.1b', description='python client for nextbus api', long_description=readme + '\n\n' + history, author='Sam Bolgert', author_email='sbolgert@gmail.com', - url='http://python.org', + url='https://github.com/linuxlewis/python-nextbus', packages=packages, package_data={'': ['LICENSE', 'NOTICE']}, package_dir={'nextbus': 'nextbus'}, @@ -39,7 +37,7 @@ license=license, zip_safe=False, classifiers=( - 'Development Status :: 3 - Alpha', + 'Development Status :: 6 - Mature', 'Intended Audience :: Developers', 'License :: OSI Approved :: Apache Software License', 'Programming Language :: Python', @@ -47,6 +45,6 @@ 'Programming Language :: Python :: 2.7', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3.3', - + 'Programming Language :: Python :: 3.5' ), -) \ No newline at end of file +) From 5e5ca5108ac5fee9c0886ee390836fe3f1644826 Mon Sep 17 00:00:00 2001 From: Roger Filmyer Date: Sat, 19 Nov 2016 14:31:20 -0800 Subject: [PATCH 2/5] added tests, fixed predictions call --- nextbus/api.py | 2 +- nextbus/model.py | 7 +++++++ nextbus/test_nextbus.py | 37 +++++++++++++++++++++++++------------ requirements.txt | 4 ++-- 4 files changed, 35 insertions(+), 15 deletions(-) diff --git a/nextbus/api.py b/nextbus/api.py index c6c774e..4ae53d4 100644 --- a/nextbus/api.py +++ b/nextbus/api.py @@ -27,7 +27,7 @@ def route_config(agency=None, route=None, **kwargs): def predictions(agency=None, route=None, stop_id=None, **kwargs): """prediction""" - result = __api_call('predictions', agency=agency, route=route, stop_id=stop_id, **kwargs) or [] + result = __api_call('predictions', agency=agency, route=route, stopId=stop_id, **kwargs) or [] return result def set_defaults(defaults): diff --git a/nextbus/model.py b/nextbus/model.py index 2a5186d..d1a9638 100644 --- a/nextbus/model.py +++ b/nextbus/model.py @@ -1,5 +1,10 @@ from lxml import etree + +class UnknownCommandError(Exception): + pass + + def parse_command(command, xml): """parses the xml document based on the command used. returns the object structure associated with that xml""" @@ -10,6 +15,8 @@ def parse_command(command, xml): result = __parse_routeList(xml) elif command == 'routeConfig': result = __parse_routeConfig(xml) + else: + raise UnknownCommandError("Command {0} is not recognized or not implemented".format(command)) return result diff --git a/nextbus/test_nextbus.py b/nextbus/test_nextbus.py index 5b00986..8cb1301 100644 --- a/nextbus/test_nextbus.py +++ b/nextbus/test_nextbus.py @@ -4,19 +4,32 @@ import unittest -def test_agencyList(): - #agency list should always return a list of agencies - agencies = nextbus.agency_list() - assert len(agencies) > 0 and type(agencies[0]) == Agency +class AgencyListTest(unittest.TestCase): + def test_agencyList(self): + #agency list should always return a list of agencies + agencies = nextbus.agency_list() + self.assertTrue(len(agencies) > 0, "Agencies list should not be empty") + self.assertTrue(isinstance(agencies[0], Agency), "agency_list not returning a list of Agencies") + # if willing to break 2.6, can use assertIsInstance instead -def test_routeList(): - #route list should always return a list of routes filtered by agency. - routes = nextbus.route_list(agency='sf-muni') - assert len(routes) > 0 and type(routes[0]) == Route and routes[0].title == 'F-Market & Wharves' + def test_routeList(self): + #route list should always return a list of routes filtered by agency. + routes = nextbus.route_list(agency='sf-muni') + self.assertTrue(len(routes) > 0, + "Routes list should not be empty") + self.assertTrue(isinstance(routes[0], Route), + "route_list not returning a list of Routes") + self.assertTrue("F-Market & Wharves" in [route.title for route in routes], + "A route is missing from the route list") + self.assertFalse("Sandwich" in [route.title for route in routes], + "route_list is including multiple agencies") -def test_routeConfig(): - #route config should return a route object by the given route tag - route = nextbus.route_config(agency='sf-muni', route='1') - assert route and type(route) == Route and route.title == '1-California' + def test_routeConfig(self): + #route config should return a route object by the given route tag + route = nextbus.route_config(agency='sf-muni', route='1') + self.assertTrue(route, "Route is empty") + self.assertTrue(isinstance(route, Route), "route_config is not returning a Route") + self.assertEqual(route.title, "1-California", + "Route title is not right (expected '{0}', got '{1}'".format("1-California", route.title)) diff --git a/requirements.txt b/requirements.txt index 9c1cff3..49ad547 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,2 +1,2 @@ -requests==2.0.1 -lxml==3.2.4 \ No newline at end of file +requests>=2.0.1 +lxml>=3.2.4 \ No newline at end of file From 591097e5588fbce3ef7fd2af5dce1f6a8ccab04b Mon Sep 17 00:00:00 2001 From: thewellington00 Date: Sat, 31 Dec 2016 08:59:52 -0800 Subject: [PATCH 3/5] add support for predictions --- nextbus/__init__.py | 2 +- nextbus/model.py | 38 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/nextbus/__init__.py b/nextbus/__init__.py index 3a69d94..1ae5060 100644 --- a/nextbus/__init__.py +++ b/nextbus/__init__.py @@ -1,4 +1,4 @@ -from .api import agency_list, route_list, route_config +from .api import agency_list, route_list, route_config, predictions __title__ = 'python-nextbus' __version__ = '0.1b' diff --git a/nextbus/model.py b/nextbus/model.py index d1a9638..2c7e373 100644 --- a/nextbus/model.py +++ b/nextbus/model.py @@ -15,6 +15,8 @@ def parse_command(command, xml): result = __parse_routeList(xml) elif command == 'routeConfig': result = __parse_routeConfig(xml) + elif command == 'predictions': + result = __parse_predictions(xml) else: raise UnknownCommandError("Command {0} is not recognized or not implemented".format(command)) @@ -51,6 +53,16 @@ def __parse_routeConfig(xml): return result +def __parse_predictions(xml): + """takes in nextbus xml element and returns a list of prediction objects""" + + #get the list of predictions + predictions = xml.xpath('//prediction') + + predictions = [Prediction.from_element(p) for p in predictions] + + return predictions + class Model(object): @classmethod @@ -217,3 +229,29 @@ def hydrate(self, element): self.lat = element.get('lat') self.lon = element.get('lon') self.stop_id = element.get('stopId') + +class Prediction(Model): + """class for predictions""" + def __init__(self): + self.epochTime = None + self.seconds = None + self.minutes = None + self.isDeparture = None + self.dirTag = None + self.vehicle = None + self.block = None + self.tripTag = None + + def __repr__(self): + return 'Vehicle in %s minutes' % self.minutes + + def hydrate(self, element): + self.epochTime = element.get('epochTime') + self.seconds = element.get('seconds') + self.minutes = element.get('minutes') + self.isDeparture = element.get('isDeparture') + self.dirTag = element.get('dirTag') + self.vehicle = element.get('vehicle') + self.block = element.get('block') + self.tripTag = element.get('tripTag') + From d821f9504c9eeccd328b1cc5f6aec38975bb4908 Mon Sep 17 00:00:00 2001 From: thewellington00 Date: Sat, 31 Dec 2016 09:00:23 -0800 Subject: [PATCH 4/5] example file to demo predictions --- example.py | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 example.py diff --git a/example.py b/example.py new file mode 100644 index 0000000..20e926e --- /dev/null +++ b/example.py @@ -0,0 +1,26 @@ +import nextbus + +# get a list of agencies +agencies = nextbus.agency_list() + +# grab the SF Muni agency object +sfmuni = [a for a in agencies if a.title=='San Francisco Muni'][0] + +# get a list of route objects on the SF Muni +routes = nextbus.route_list(agency=sfmuni.tag) + +# grab a specific route object, in this case "1-California" +r = [r for r in routes if r.title=='1-California'][0] + +# get more info on that route +route = nextbus.route_config(agency=sfmuni.tag, route=r.tag) + +# grab a stop on that route +stop = [s for s in route.stops if s.title=='California St & Spruce St'][0] + +# get the prediction objects for that route and stop +predictions = nextbus.predictions(agency=sfmuni.tag, route=route.tag, stop_id=stop.stop_id) +print 'Vehicles arrive in ' + ', '.join([p.minutes for p in predictions]) + ' minutes' + + + From c55812ccf1e09776ab5bb7cacca2c55a1d879a63 Mon Sep 17 00:00:00 2001 From: thewellington00 Date: Sat, 31 Dec 2016 09:05:22 -0800 Subject: [PATCH 5/5] added predicitons to readme --- README.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/README.md b/README.md index 97da349..f6de93a 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,12 @@ Depends on requests and lxml Route(1-California) ``` +### predictions +```python +>>> predictions = nextbus.predictions(agency='sf-muni', route='1', stop_id='13896') +>>> predictions[0] +Vehicle in 2 minutes +```