Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Function to get all objects in paged results #6

Merged
merged 6 commits into from Sep 16, 2011
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
68 changes: 49 additions & 19 deletions facepy/graph_api.py
Expand Up @@ -28,7 +28,7 @@ def __init__(self, oauth_token=None, signed_request=None, app_secret=None):
else: else:
self.oauth_token = oauth_token self.oauth_token = oauth_token


def get(self, path='', **options): def get(self, path='', page=False, **options):
""" """
Get an item from the Graph API. Get an item from the Graph API.


Expand All @@ -37,7 +37,7 @@ def get(self, path='', **options):
**options -- Graph API parameters such as 'limit', 'offset' or 'since' (see http://developers.facebook.com/docs/reference/api/). **options -- Graph API parameters such as 'limit', 'offset' or 'since' (see http://developers.facebook.com/docs/reference/api/).
""" """


response = self._query('GET', path, options) response = self._query('GET', path, options, page)


if response is False: if response is False:
raise self.Error('Could not get "%s".' % path) raise self.Error('Could not get "%s".' % path)
Expand Down Expand Up @@ -100,7 +100,40 @@ def search(self, term, type, **options):


return response return response


def _query(self, method, path, data={}): def _load_url(self, method, url, data):
"""
Fetch an object from the Graph API and parse the output.

Arguments:
method -- A string describing the HTTP method.
url -- A string describing the URL.
data -- A dictionary of HTTP GET parameters (for GET requests) or POST data (for POST requests).

Returns a pair (obj, next_url) where obj is the object returned from the graph API (parsed
into a python object) and next_url is the URL for more results or None if this is the last
page of results.
"""

if method in ['GET', 'DELETE']:
response = requests.request(method, url, params=data)
else:
response = requests.request(method, url, data=data)

result = self._parse(response.content)

try:
obj = result['data']
except (KeyError, TypeError):
obj = result

try:
next_url = result['paging']['next']
except (KeyError, TypeError):
next_url = None

return obj, next_url

def _query(self, method, path, data={}, page=False):
""" """
Low-level access to Facebook's Graph API. Low-level access to Facebook's Graph API.


Expand All @@ -116,17 +149,21 @@ def _query(self, method, path, data={}):
if type(value) is list and all([type(item) in (str, unicode) for item in value]): if type(value) is list and all([type(item) in (str, unicode) for item in value]):
data[key] = ','.join(value) data[key] = ','.join(value)


url = 'https://graph.facebook.com/%s' % path
if self.oauth_token: if self.oauth_token:
data.update({'access_token': self.oauth_token }) data.update({'access_token': self.oauth_token })


url = 'https://graph.facebook.com/%s' % path if page:

def make_generator(url, data):
if method in ['GET', 'DELETE']: while url is not None:
response = requests.request(method, url, params=data) objs, url = self._load_url(method, url, data)
data = {}
for obj in objs:
yield objs
return make_generator(url, data)
else: else:
response = requests.request(method, url, data=data) obj, next_url = self._load_url(method, url, data)

return obj
return self._parse(response.content)


def _parse(self, data): def _parse(self, data):
""" """
Expand Down Expand Up @@ -154,19 +191,12 @@ def _parse(self, data):
# #
# We'll handle this discrepancy as gracefully as we can by implementing logic to deal with this behavior # We'll handle this discrepancy as gracefully as we can by implementing logic to deal with this behavior
# in the high-level access functions (get, post, delete etc.). # in the high-level access functions (get, post, delete etc.).
if type(data) is bool:
return data

if type(data) is dict: if type(data) is dict:


if 'error' in data: if 'error' in data:
raise self.Error(data['error']['message']) raise self.Error(data['error']['message'])


# If the response contains a 'data' key, strip everything else (it serves no purpose) return data
if 'data' in data:
data = data['data']

return data


class Error(FacepyError): class Error(FacepyError):
pass pass
12 changes: 12 additions & 0 deletions tests/test_graph_api.py
Expand Up @@ -28,6 +28,18 @@ def test_get():
assert isinstance(graph.get('me'), dict) assert isinstance(graph.get('me'), dict)
assert isinstance(graph.get('me/picture'), str) assert isinstance(graph.get('me/picture'), str)


def test_paged_get():
graph = GraphAPI(TEST_ACCESS_TOKEN)

posts = graph.get('Facebook/posts', until=1314742370, limit=6, page=True)

for (i, post) in enumerate(posts):
# require three pages of results
if i >= 21:
return

assert False, 'only first page of results returned'

def test_post(): def test_post():


graph = GraphAPI(TEST_ACCESS_TOKEN) graph = GraphAPI(TEST_ACCESS_TOKEN)
Expand Down