-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #3 from fitnr/develop
Add results object
- Loading branch information
Showing
6 changed files
with
195 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,41 @@ | ||
nypl-digital-collections | ||
======================== | ||
|
||
Library to access the New York Public Library's Digital Collections API | ||
Library to access the New York Public Library's [Digital Collections API](http://api.repo.nypl.org). | ||
|
||
Basics: | ||
|
||
````python | ||
from nyplcollections import NYPLsearch | ||
|
||
# Create search object | ||
nypl = NYPLsearch(API_KEY) | ||
```` | ||
|
||
Methods: | ||
* NYPLsearch.captures | ||
* NYPLsearch.mods | ||
* NYPLsearch.search | ||
* NYPLsearch.uuid | ||
|
||
Search: | ||
|
||
````python | ||
cats = self.nypl.search('cats') | ||
|
||
cats.results | ||
# [...] | ||
```` | ||
|
||
MODS: | ||
|
||
````python | ||
# Get a MODS record based on uuid | ||
mods = nypl.mods('acfeeb2d-7c5e-4ce7-e040-e00a180644aa') | ||
|
||
mods.status_code | ||
200 | ||
|
||
mods.results | ||
# {...} | ||
```` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
__all__ = ['nyplcollections'] | ||
from .nyplcollections import NYPLsearch |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,70 +1,107 @@ | ||
#!/usr/bin/env python | ||
|
||
import requests | ||
import xmltodict | ||
|
||
|
||
class NYPLsearch(object): | ||
raw_results = None | ||
results = None | ||
error = None | ||
|
||
def __init__(self, token, format='json', page=1, per_page=10): | ||
self.token = token | ||
self.page = page | ||
self.per_page = per_page | ||
self.format = format | ||
def __init__(self, token, page=None, per_page=None): | ||
self._token = token | ||
self.format = 'json' | ||
self.page = page or 1 | ||
self.per_page = per_page or 10 | ||
self.base = "http://api.repo.nypl.org/api/v1/items" | ||
|
||
# Return the captures for a given uuid | ||
# optional value withTitles=yes | ||
def captures(self, uuid, withTitles=False): | ||
return self._get('/'.join([self.base, uuid]), | ||
{'withTitles': 'yes' if withTitles else 'no'}) | ||
"""Return the captures for a given uuid | ||
optional value withTitles=yes""" | ||
picker = lambda x: x.get('capture', []) | ||
return self._get((uuid,), picker, withTitles='yes' if withTitles else 'no') | ||
|
||
# Return the item-uuid for a identifier. | ||
def uuid(self, type, val): | ||
return self._get('/'.join([self.base, type, val])) | ||
"""Return the item-uuid for a identifier""" | ||
picker = lambda x: x.get('uuid', x) | ||
return self._get((type, val), picker) | ||
|
||
def search(self, q, field=None, page=None, per_page=None): | ||
"""Search across all (without field) or in specific field | ||
(valid fields at http://www.loc.gov/standards/mods/mods-outline.html)""" | ||
|
||
# Search across all (without field) or in specific field | ||
# (valid fields at http://www.loc.gov/standards/mods/mods-outline.html) | ||
def search(self, q, field=None): | ||
params = {'q': q} | ||
if field: | ||
params['field'] = field | ||
def picker(results): | ||
if type(results['result']) == list: | ||
return results['result'] | ||
else: | ||
return [results['result']] | ||
|
||
return self._get('/'.join([self.base, 'search']), params) | ||
return self._get(('search',), picker, q=q, field=field, page=page, per_page=per_page) | ||
|
||
# Return a mods record for a given uuid | ||
def mods(self, uuid): | ||
return self._get('/'.join([self.base, 'mods', uuid])) | ||
"""Return a mods record for a given uuid""" | ||
picker = lambda x: x.get('mods', {}) | ||
return self._get(('mods', uuid), picker) | ||
|
||
# Generic get which handles call to api and setting of results | ||
# Return: results dict | ||
def _get(self, url, params=None): | ||
self.raw_results = self.results = None | ||
def _get(self, components, picker, **params): | ||
"""Generic get which handles call to api and setting of results | ||
Return: Results object""" | ||
url = '/'.join((self.base,) + components) | ||
|
||
headers = {"Authorization": "Token token=" + self.token} | ||
params = params or dict() | ||
params['page'] = self.page | ||
params['per_page'] = self.per_page | ||
headers = {"Authorization": "Token token=" + self._token} | ||
|
||
params['page'] = params.get('page') or self.page | ||
params['per_page'] = params.get('per_page') or self.per_page | ||
|
||
r = requests.get(".".join([url, self.format]), | ||
params=params, | ||
headers=headers) | ||
|
||
self.raw_results = r.text | ||
self.results = self._to_dict(r)['nyplAPI']['response'] | ||
_next = self._nextify(components, picker, params) | ||
|
||
if self.results['headers']['status'] == 'error': | ||
self.error = { | ||
'code': self.results['headers']['code'], | ||
'message': self.results['headers']['message'] | ||
} | ||
else: | ||
self.error = None | ||
return Result(r, picker, _next) | ||
|
||
def _nextify(self, components, picker, params): | ||
params['page'] = 1 + params.get('page', 0) | ||
return lambda: self._get(components, picker, **params) | ||
|
||
|
||
class Result(object): | ||
|
||
'''Iterable wrapper for responses from NYPL API''' | ||
error = None | ||
|
||
return self.results | ||
def __init__(self, request_object, picker, _next): | ||
self.raw = request_object.text | ||
self.status_code = request_object.status_code | ||
|
||
def _to_dict(self, r): | ||
return r.json() if self.format == 'json' else xmltodict.parse(r.text) | ||
self._next = _next | ||
|
||
response = request_object.json().get('nyplAPI', {}) | ||
|
||
try: | ||
self.headers = response['response'].get('headers') | ||
|
||
if self.headers['status'] == 'error': | ||
self.error = { | ||
'code': self.headers['code'], | ||
'message': self.headers['message'] | ||
} | ||
|
||
else: | ||
self.request = response.get('request', {}) | ||
|
||
for k in ('numResults', 'totalPages', 'perPage', 'page'): | ||
v = self.request.get(k) | ||
if v: | ||
self.request[k] = int(v) | ||
|
||
if response['response'].get('numResults'): | ||
self.count = int(response['response']['numResults']) | ||
|
||
self.results = picker(response['response']) | ||
|
||
except IndexError: | ||
raise IndexError("Couldn't parse response.") | ||
|
||
def next(self): | ||
if self.request.get('totalPages') > self.request.get('page'): | ||
return self._next() | ||
else: | ||
raise StopIteration |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,12 +2,11 @@ | |
|
||
setup( | ||
name="nyplcollections", | ||
version="1", | ||
version="1.1", | ||
description="new york public library image collections api", | ||
author="nick mohoric", | ||
author_email="[email protected]", | ||
install_requires=[ | ||
"xmltodict", | ||
"requests" | ||
], | ||
packages=['nyplcollections'], | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
from nyplcollections import NYPLsearch | ||
import unittest | ||
|
||
# todo: use mock to mock in NYPL responses | ||
|
||
KEY = # insert your key here | ||
|
||
class TestNYPLsearch(unittest.TestCase): | ||
|
||
def setUp(self): | ||
self.nypl = NYPLsearch(KEY) | ||
|
||
def test_search(self): | ||
cats = self.nypl.search('cats') | ||
|
||
assert 200 == cats.status_code | ||
|
||
assert cats.request['perPage'] == 10 | ||
|
||
assert cats.results | ||
|
||
assert len(cats.results) | ||
|
||
assert cats.results[0].get('uuid') | ||
|
||
|
||
def test_next(self): | ||
cats = self.nypl.search('cats') | ||
mor_cats = next(cats) | ||
assert 200 == mor_cats.status_code | ||
|
||
def test_search_fields(self): | ||
maps = self.nypl.search('cartographic', field='typeOfResource') | ||
|
||
assert 200 == maps.status_code | ||
|
||
assert maps.results | ||
|
||
assert maps.results[0].get('uuid') | ||
|
||
def test_per_page(self): | ||
cats = self.nypl.search('cats', per_page=1) | ||
assert len(cats.results) == 1 | ||
|
||
def test_mods(self): | ||
uuid = '510d47dd-ab68-a3d9-e040-e00a18064a99' | ||
hades = '423990' | ||
mods = self.nypl.mods(uuid) | ||
assert 200 == mods.status_code | ||
|
||
identifiers = mods.results['identifier'] | ||
u = [i for i in identifiers if i['type'] == 'local_hades'] | ||
assert u[0]['$'] == hades | ||
|
||
assert mods.results['titleInfo'][1]['title']['$'] == u'Gowanus Bay - Brooklyn - 30th Street Pier.' | ||
|
||
def test_uuid(self): | ||
uuid = self.nypl.uuid('local_hades', '1017240') | ||
|
||
assert 200 == uuid.status_code | ||
assert 'ecaf7d80-c55f-012f-e3c7-58d385a7bc34' == uuid.results | ||
|
||
def test_captures(self): | ||
captures = self.nypl.captures('5fa75050-c6c7-012f-e24b-58d385a7bc34') | ||
|
||
assert 200 == captures.status_code | ||
|
||
assert 125 == captures.count | ||
|
||
assert type(captures.results) == list | ||
|
||
if __name__ == '__main__': | ||
unittest.main() |
This file was deleted.
Oops, something went wrong.