Skip to content

Commit

Permalink
Added start of a DataFeed extension. Still early, not ready for prime…
Browse files Browse the repository at this point in the history
…-time.

Added an example of a DataFeed client in a new examples folder (more coming soon).
  • Loading branch information
aaront committed Jan 26, 2016
1 parent 02e7fc7 commit d4cb2ff
Show file tree
Hide file tree
Showing 4 changed files with 115 additions and 8 deletions.
44 changes: 44 additions & 0 deletions examples/data_feed/feeder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-

import click

from mygeotab import ext, API, dates


class ExceptionDataFeedListener(ext.DataFeedListener):
def __init__(self, api):
self.api = api
self._devices = {}
super(ext.DataFeedListener, self).__init__()

def on_data(self, data):
for d in data:
device = self._devices.get(d['device']['id'])
if not device:
devices = self.api.search('Device', id=d['device']['id'], results_limit=1)
if len(devices) > 0:
device = devices[0]
self._devices[device['id']] = device
else:
device = d['device']
date = dates.localize_datetime(d['activeFrom'])
click.echo('[{0}] {1}'.format(date, device.get('name', '**Unknown Vehicle')))

def on_error(self, error):
click.secho(error, fg='red')


@click.command(help="A console data feeder example")
@click.argument('database', nargs=1, required=False)
@click.option('--user', '-u', prompt=True)
@click.option('--password', '-p', prompt=True, hide_input=True)
@click.option('--server', default=None, help='The server (ie. my4.geotab.com)')
@click.option('--interval', '-i', type=click.IntRange(5, 300))
def main(database=None, user=None, password=None, server=None, interval=60):
api = API(database=database, username=user, password=password, server=server)
api.authenticate()
ext.DataFeed(api, ExceptionDataFeedListener(api), 'ExceptionEvent', interval=interval).start()


if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions examples/data_feed/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
click
27 changes: 19 additions & 8 deletions mygeotab/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,6 @@


class API(object):
_reauthorize_count = 0

def __init__(self, username, password=None, database=None, session_id=None, server='my.geotab.com'):
"""
Creates a new instance of this simple Pythonic wrapper for the MyGeotab API.
Expand All @@ -39,6 +37,7 @@ def __init__(self, username, password=None, database=None, session_id=None, serv
if password is None and session_id is None:
raise Exception('`password` and `session_id` must not both be None')
self.credentials = Credentials(username, session_id, database, server, password)
self.__reauthorize_count = 0

@staticmethod
def from_credentials(credentials):
Expand Down Expand Up @@ -67,6 +66,16 @@ def _api_url(self):
base_url.replace('/', '')
return 'https://' + base_url + '/apiv1'

@property
def _is_local(self):
"""
Are calls being made against a local server.
:rtype: bool
:return: True if the calls are being made locally
"""
return any(s in self._api_url for s in ['127.0.0.1', 'localhost'])

@staticmethod
def _process(data):
"""
Expand Down Expand Up @@ -112,12 +121,14 @@ def _query(self, method, parameters):
"""
params = dict(id=-1, method=method, params=parameters)
headers = {'Content-type': 'application/json; charset=UTF-8'}
is_live = not any(s in self._api_url for s in ['127.0.0.1', 'localhost'])
r = requests.post(self._api_url,
data=json.dumps(params,
default=mygeotab.serializers.object_serializer),
headers=headers, allow_redirects=True, verify=is_live)
return self._process(r.json(object_hook=mygeotab.serializers.object_deserializer))
headers=headers, allow_redirects=True, verify=(not self._is_local))
try:
return self._process(r.json(object_hook=mygeotab.serializers.object_deserializer))
finally:
r.close()

def call(self, method, **parameters):
"""
Expand All @@ -141,11 +152,11 @@ def call(self, method, **parameters):
try:
result = self._query(method, parameters)
if result is not None:
self._reauthorize_count = 0
self.__reauthorize_count = 0
return result
except MyGeotabException as exception:
if exception.name == 'InvalidUserException' and self._reauthorize_count == 0:
self._reauthorize_count += 1
if exception.name == 'InvalidUserException' and self.__reauthorize_count == 0:
self.__reauthorize_count += 1
self.authenticate()
return self.call(method, **parameters)
raise
Expand Down
51 changes: 51 additions & 0 deletions mygeotab/ext.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
# -*- coding: utf-8 -*-

from threading import Thread
from time import sleep


class DataFeedListener(object):
def on_data(self, data):
return

def on_error(self, error):
return False


class DataFeed(object):
def __init__(self, api, listener, type_name, interval, search=None):
self.api = api
self.listener = listener
self.type_name = type_name
self.interval = interval
self.search = search
self.running = False
self._version = None
self._thread = None

def _result(self, data):
if self.listener.on_result(data) is False:
self.running = False

def _run(self):
while self.running:
try:
result = self.api.call('GetFeed', type_name=self.type_name, search=self.search,
from_version=self._version)
self._version = result['toVersion']
self.listener.on_data(result['data'])
except Exception as e:
if self.listener.on_error(e) is False:
break
if not self.running:
break
sleep(self.interval)
self.running = False

def start(self, async=True):
self.running = True
if async:
self._thread = Thread(target=self._run)
self._thread.start()
else:
self._run()

0 comments on commit d4cb2ff

Please sign in to comment.