[med-svn] [python-fitbit] 01/05: Imported Upstream version 0.1.2

Iain Learmonth irl-guest at moszumanska.debian.org
Fri Sep 26 19:26:18 UTC 2014


This is an automated email from the git hooks/post-receive script.

irl-guest pushed a commit to branch master
in repository python-fitbit.

commit d993bc113f6c084c93136d3ab9e4fa5214889894
Author: Iain R. Learmonth <irl at fsfe.org>
Date:   Fri Sep 26 20:00:34 2014 +0100

    Imported Upstream version 0.1.2
---
 AUTHORS                         |  8 +++++++-
 MANIFEST.in                     |  2 +-
 README.md                       | 15 ---------------
 README.rst                      | 21 +++++++++++++++++++++
 fitbit/__init__.py              |  6 +++---
 fitbit/api.py                   | 30 +++++++++++++++++++-----------
 fitbit/exceptions.py            | 27 +++++++++++++++++++++++----
 fitbit_tests/test_api.py        |  7 +++++++
 fitbit_tests/test_auth.py       |  8 +++++++-
 fitbit_tests/test_exceptions.py | 20 +++++++++++++++++++-
 requirements/dev.txt            |  4 ++--
 setup.py                        |  4 ++--
 12 files changed, 111 insertions(+), 41 deletions(-)

diff --git a/AUTHORS b/AUTHORS
index 835f236..4beb310 100644
--- a/AUTHORS
+++ b/AUTHORS
@@ -4,4 +4,10 @@ Rebecca Lovewell (Caktus Consulting Group)
 Dan Poirier (Caktus Consulting Group)
 Brad Pitcher (ORCAS)
 Silvio Tomatis
-Steven Skoczen
\ No newline at end of file
+Steven Skoczen
+Eric Xu
+Josh Gachnang
+Lorenzo Mancini
+David Grandinetti
+Chris Streeter
+Mario Sangiorgio
diff --git a/MANIFEST.in b/MANIFEST.in
index 8bd82f5..706ec66 100644
--- a/MANIFEST.in
+++ b/MANIFEST.in
@@ -1 +1 @@
-include LICENSE AUTHORS README.md requirements/* docs/*
+include LICENSE AUTHORS README.rst requirements/* docs/*
diff --git a/README.md b/README.md
deleted file mode 100644
index d88db85..0000000
--- a/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-python-fitbit
-=============
-
-[![Build Status](https://travis-ci.org/orcasgit/python-fitbit.png?branch=master)](https://travis-ci.org/orcasgit/python-fitbit)
-[![Coverage Status](https://coveralls.io/repos/orcasgit/python-fitbit/badge.png?branch=master)](https://coveralls.io/r/orcasgit/python-fitbit?branch=master)
-[![Requirements Status](https://requires.io/github/orcasgit/python-fitbit/requirements.png?branch=master)](https://requires.io/github/orcasgit/python-fitbit/requirements/?branch=master)
-
-Fitbit API Python Client Implementation
-
-For documentation: [http://python-fitbit.readthedocs.org/](http://python-fitbit.readthedocs.org/)
-
-Requirements
-============
-
-* Python 2.6+  
diff --git a/README.rst b/README.rst
new file mode 100644
index 0000000..0e9d667
--- /dev/null
+++ b/README.rst
@@ -0,0 +1,21 @@
+python-fitbit
+=============
+
+.. image:: https://travis-ci.org/orcasgit/python-fitbit.svg?branch=master
+   :target: https://travis-ci.org/orcasgit/python-fitbit
+   :alt: Build Status
+.. image:: https://coveralls.io/repos/orcasgit/python-fitbit/badge.png?branch=master
+   :target: https://coveralls.io/r/orcasgit/python-fitbit?branch=master
+   :alt: Coverage Status
+.. image:: https://requires.io/github/orcasgit/python-fitbit/requirements.png?branch=master
+   :target: https://requires.io/github/orcasgit/python-fitbit/requirements/?branch=master
+   :alt: Requirements Status
+
+Fitbit API Python Client Implementation
+
+For documentation: `http://python-fitbit.readthedocs.org/ <http://python-fitbit.readthedocs.org/>`_
+
+Requirements
+============
+
+* Python 2.6+
diff --git a/fitbit/__init__.py b/fitbit/__init__.py
index e4957cb..f100b8a 100644
--- a/fitbit/__init__.py
+++ b/fitbit/__init__.py
@@ -3,7 +3,7 @@
 Fitbit API Library
 ------------------
 
-:copyright: 2012 ORCAS.
+:copyright: 2012-2014 ORCAS.
 :license: BSD, see LICENSE for more details.
 """
 
@@ -14,10 +14,10 @@ from .api import Fitbit, FitbitOauthClient
 __title__ = 'fitbit'
 __author__ = 'Issac Kelly and ORCAS'
 __author_email__ = 'bpitcher at orcasinc.com'
-__copyright__ = 'Copyright 2012 ORCAS'
+__copyright__ = 'Copyright 2012-2014 ORCAS'
 __license__ = 'Apache 2.0'
 
-__version__ = '0.1.0'
+__version__ = '0.1.2'
 
 # Module namespace.
 
diff --git a/fitbit/api.py b/fitbit/api.py
index 4ada88a..48b72e4 100644
--- a/fitbit/api.py
+++ b/fitbit/api.py
@@ -13,7 +13,8 @@ from requests_oauthlib import OAuth1, OAuth1Session
 
 from fitbit.exceptions import (BadResponse, DeleteError, HTTPBadRequest,
                                HTTPUnauthorized, HTTPForbidden,
-                               HTTPServerError, HTTPConflict, HTTPNotFound)
+                               HTTPServerError, HTTPConflict, HTTPNotFound,
+                               HTTPTooManyRequests)
 from fitbit.utils import curry
 
 
@@ -33,7 +34,7 @@ class FitbitOauthClient(object):
         Create a FitbitOauthClient object. Specify the first 5 parameters if
         you have them to access user data. Specify just the first 2 parameters
         to access anonymous data and start the set up for user authorization.
-        
+
         Set callback_uri to a URL and when the user has granted us access at
         the fitbit site, fitbit will redirect them to the URL you passed.  This
         is how we get back the magic verifier string from fitbit if we're a web
@@ -83,6 +84,11 @@ class FitbitOauthClient(object):
             raise HTTPNotFound(response)
         elif response.status_code == 409:
             raise HTTPConflict(response)
+        elif response.status_code == 429:
+            exc = HTTPTooManyRequests(response)
+            exc.retry_after_secs = int(response.headers['Retry-After'])
+            raise exc
+
         elif response.status_code >= 500:
             raise HTTPServerError(response)
         elif response.status_code >= 400:
@@ -101,14 +107,15 @@ class FitbitOauthClient(object):
         self.resource_owner_secret = token.get('oauth_token_secret')
         return token
 
-    def authorize_token_url(self):
+    def authorize_token_url(self, **kwargs):
         """Step 2: Return the URL the user needs to go to in order to grant us
         authorization to look at their data.  Then redirect the user to that
         URL, open their browser to it, or tell them to copy the URL into their
-        browser.
+        browser.  Allow the client to request the mobile display by passing
+        the display='touch' argument.
         """
-        
-        return self.oauth.authorization_url(self.authorization_url)
+
+        return self.oauth.authorization_url(self.authorization_url, **kwargs)
 
     def fetch_access_token(self, verifier, token=None):
         """Step 3: Given the verifier from fitbit, and optionally a token from
@@ -119,7 +126,7 @@ class FitbitOauthClient(object):
         if token:
             self.resource_owner_key = token.get('oauth_token')
             self.resource_owner_secret = token.get('oauth_token_secret')
-            
+
         self.oauth = OAuth1Session(
             self.client_key,
             client_secret=self.client_secret,
@@ -145,7 +152,7 @@ class Fitbit(object):
     _resource_list = [
         'body',
         'activities',
-        'foods',
+        'foods/log',
         'water',
         'sleep',
         'heart',
@@ -167,7 +174,8 @@ class Fitbit(object):
         # creating and deleting records once, and use curry to make individual
         # Methods for each
         for resource in self._resource_list:
-            setattr(self, resource, curry(self._COLLECTION_RESOURCE, resource))
+            setattr(self, resource.replace('/', '_'),
+                    curry(self._COLLECTION_RESOURCE, resource))
 
             if resource not in ['body', 'glucose']:
                 # Body and Glucose entries are not currently able to be deleted
@@ -251,7 +259,7 @@ class Fitbit(object):
 
             body(date=None, user_id=None, data=None)
             activities(date=None, user_id=None, data=None)
-            foods(date=None, user_id=None, data=None)
+            foods_log(date=None, user_id=None, data=None)
             water(date=None, user_id=None, data=None)
             sleep(date=None, user_id=None, data=None)
             heart(date=None, user_id=None, data=None)
@@ -611,7 +619,7 @@ class Fitbit(object):
             'duration': duration,
             'date': start_time.strftime("%Y-%m-%d"),
         }
-        url = "%s/%s/user/-/sleep" % (
+        url = "%s/%s/user/-/sleep.json" % (
             self.API_ENDPOINT,
             self.API_VERSION,
         )
diff --git a/fitbit/exceptions.py b/fitbit/exceptions.py
index b594b02..d6249ea 100644
--- a/fitbit/exceptions.py
+++ b/fitbit/exceptions.py
@@ -1,51 +1,70 @@
 import json
 
+
 class BadResponse(Exception):
     """
     Currently used if the response can't be json encoded, despite a .json extension
     """
     pass
 
+
 class DeleteError(Exception):
     """
     Used when a delete request did not return a 204
     """
     pass
 
+
 class HTTPException(Exception):
     def __init__(self, response, *args, **kwargs):
         try:
             errors = json.loads(response.content.decode('utf8'))['errors']
             message = '\n'.join([error['message'] for error in errors])
         except Exception:
-            if response.status_code == 401:
+            if hasattr(response, 'status_code') and response.status_code == 401:
                 message = response.content.decode('utf8')
             else:
                 message = response
         super(HTTPException, self).__init__(message, *args, **kwargs)
 
+
 class HTTPBadRequest(HTTPException):
+    """Generic >= 400 error
+    """
     pass
 
 
 class HTTPUnauthorized(HTTPException):
+    """401
+    """
     pass
 
 
 class HTTPForbidden(HTTPException):
+    """403
+    """
     pass
 
 
-class HTTPServerError(HTTPException):
+class HTTPNotFound(HTTPException):
+    """404
+    """
     pass
 
 
 class HTTPConflict(HTTPException):
+    """409 - returned when creating conflicting resources
     """
-    Used by Fitbit as rate limiter
+    pass
+
+
+class HTTPTooManyRequests(HTTPException):
+    """429 - returned when exceeding rate limits
     """
     pass
 
 
-class HTTPNotFound(HTTPException):
+class HTTPServerError(HTTPException):
+    """Generic >= 500 error
+    """
     pass
diff --git a/fitbit_tests/test_api.py b/fitbit_tests/test_api.py
index 0eb3f3e..4b93142 100644
--- a/fitbit_tests/test_api.py
+++ b/fitbit_tests/test_api.py
@@ -189,6 +189,10 @@ class DeleteCollectionResourceTest(TestBase):
         self.assertEqual(999, retval)
 
 class MiscTest(TestBase):
+    def test_activities(self):
+        user_id = "Qui-Gon Jinn"
+        self.common_api_test('activities', (), {}, (URLBASE + "/%s/activities/date/%s.json" % (user_id, datetime.date.today().strftime('%Y-%m-%d'),),), {})
+        self.common_api_test('activities', (), {}, (URLBASE + "/-/activities/date/%s.json" % datetime.date.today().strftime('%Y-%m-%d'),), {})
 
     def test_recent_activities(self):
         user_id = "LukeSkywalker"
@@ -254,12 +258,15 @@ class MiscTest(TestBase):
             expected_url=URLBASE + "/BAR/FOO/date/1992-05-12/1998-12-31.json")
 
     def test_foods(self):
+        today = datetime.date.today().strftime('%Y-%m-%d')
         self.common_api_test('recent_foods', ("USER_ID",), {}, (URLBASE+"/USER_ID/foods/log/recent.json",), {})
         self.common_api_test('favorite_foods', ("USER_ID",), {}, (URLBASE+"/USER_ID/foods/log/favorite.json",), {})
         self.common_api_test('frequent_foods', ("USER_ID",), {}, (URLBASE+"/USER_ID/foods/log/frequent.json",), {})
+        self.common_api_test('foods_log', (today, "USER_ID",), {}, ("%s/USER_ID/foods/log/date/%s.json" % (URLBASE, today), None), {})
         self.common_api_test('recent_foods', (), {}, (URLBASE+"/-/foods/log/recent.json",), {})
         self.common_api_test('favorite_foods', (), {}, (URLBASE+"/-/foods/log/favorite.json",), {})
         self.common_api_test('frequent_foods', (), {}, (URLBASE+"/-/foods/log/frequent.json",), {})
+        self.common_api_test('foods_log', (today,), {}, ("%s/-/foods/log/date/%s.json" % (URLBASE, today), None), {})
 
         url = URLBASE + "/-/foods/log/favorite/food_id.json"
         self.common_api_test('add_favorite_food', ('food_id',), {}, (url,), {'method': 'POST'})
diff --git a/fitbit_tests/test_auth.py b/fitbit_tests/test_auth.py
index 5bc2a2b..8f83b75 100644
--- a/fitbit_tests/test_auth.py
+++ b/fitbit_tests/test_auth.py
@@ -1,5 +1,5 @@
 from unittest import TestCase
-from fitbit import Fitbit
+from fitbit import Fitbit, FitbitOauthClient
 import mock
 from requests_oauthlib import OAuth1Session
 
@@ -42,6 +42,12 @@ class AuthTest(TestCase):
             self.assertEqual(1, au.call_count)
             self.assertEqual("FAKEURL", retval)
 
+    def test_authorize_token_url_with_parameters(self):
+        # authorize_token_url calls oauth and returns a URL
+        client = FitbitOauthClient(**self.client_kwargs)
+        retval = client.authorize_token_url(display="touch")
+        self.assertTrue("display=touch" in retval)
+
     def test_fetch_access_token(self):
         kwargs = self.client_kwargs
         kwargs['resource_owner_key'] = ''
diff --git a/fitbit_tests/test_exceptions.py b/fitbit_tests/test_exceptions.py
index 5c174bb..2b87e9a 100644
--- a/fitbit_tests/test_exceptions.py
+++ b/fitbit_tests/test_exceptions.py
@@ -1,6 +1,7 @@
 import unittest
 import mock
 import requests
+import sys
 from fitbit import Fitbit
 from fitbit import exceptions
 
@@ -76,6 +77,24 @@ class ExceptionTest(unittest.TestCase):
         r.status_code = 499
         self.assertRaises(exceptions.HTTPBadRequest, f.user_profile_get)
 
+    def test_too_many_requests(self):
+        """
+        Tests the 429 response, given in case of exceeding the rate limit
+        """
+        r = mock.Mock(spec=requests.Response)
+        r.content = b"{'normal': 'resource'}"
+        r.headers = {'Retry-After': '10'}
+
+        f = Fitbit(**self.client_kwargs)
+        f.client._request = lambda *args, **kwargs: r
+
+        r.status_code = 429
+        try:
+            f.user_profile_get()
+            self.assertEqual(True, False)  # Won't run if an exception's raised
+        except exceptions.HTTPTooManyRequests:
+            e = sys.exc_info()[1]
+            self.assertEqual(e.retry_after_secs, 10)
 
     def test_serialization(self):
         """
@@ -100,4 +119,3 @@ class ExceptionTest(unittest.TestCase):
         f = Fitbit(**self.client_kwargs)
         f.client._request = lambda *args, **kwargs: r
         self.assertRaises(exceptions.DeleteError, f.delete_activities, 12345)
-
diff --git a/requirements/dev.txt b/requirements/dev.txt
index 5ad7c4b..d586338 100644
--- a/requirements/dev.txt
+++ b/requirements/dev.txt
@@ -1,5 +1,5 @@
 -r base.txt
 -r test.txt
 
-Sphinx==1.2.2
-tox==1.7.1
+Sphinx==1.2.3
+tox==1.7.2
diff --git a/setup.py b/setup.py
index c0fbf8e..8dbbdb4 100644
--- a/setup.py
+++ b/setup.py
@@ -17,14 +17,14 @@ setup(
     name='fitbit',
     version=version,
     description='Fitbit API Wrapper.',
-    long_description=open('README.md').read(),
+    long_description=open('README.rst').read(),
     author=author,
     author_email=author_email,
     url='https://github.com/orcasgit/python-fitbit',
     packages=['fitbit'],
     package_data={'': ['LICENSE']},
     include_package_data=True,
-    install_requires=["distribute"] + required,
+    install_requires=["setuptools"] + required,
     license='Apache 2.0',
     test_suite='fitbit_tests.all_tests',
     tests_require=required_test,

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/debian-med/python-fitbit.git



More information about the debian-med-commit mailing list