diff --git a/ceo/pymazon.py b/ceo/pymazon.py index 47fe6df..d27cc0c 100644 --- a/ceo/pymazon.py +++ b/ceo/pymazon.py @@ -3,132 +3,153 @@ from xml.dom import minidom, Node import urllib import time +import datetime +import hashlib +import base64 +import hmac class PyMazonError(Exception): - """Holds information about an error that occured during a pymazon request""" - def __init__(self, messages): - self.__message = '\n'.join(messages) + """Holds information about an error that occured during a pymazon request""" + def __init__(self, messages): + self.__message = '\n'.join(messages) - def __get_message(self): - return self.__message + def __get_message(self): + return self.__message - def __str__(self): - return repr(self.__message) + def __str__(self): + return repr(self.__message) - message = property(fget=__get_message) + message = property(fget=__get_message) class PyMazonBook: - """Stores information about a book retrieved via PyMazon.""" - def __init__(self, title, authors, publisher, year, isbn10, isbn13, edition): - self.__title = title - self.__authors = authors - self.__publisher = publisher - self.__year = year - self.__isbn10 = isbn10 - self.__isbn13 = isbn13 - self.__edition = edition + """Stores information about a book retrieved via PyMazon.""" + def __init__(self, title, authors, publisher, year, isbn10, isbn13, edition): + self.__title = title + self.__authors = authors + self.__publisher = publisher + self.__year = year + self.__isbn10 = isbn10 + self.__isbn13 = isbn13 + self.__edition = edition - def __str__(self): - return 'Title: ' + self.title + '\n' + \ - 'Author(s): ' + ', '.join(self.authors) + '\n' \ - 'Publisher: ' + self.publisher + '\n' + \ - 'Year: ' + self.year + '\n' + \ - 'ISBN-10: ' + self.isbn10 + '\n' + \ - 'ISBN-13: ' + self.isbn13 + '\n' + \ - 'Edition: ' + self.edition + def __str__(self): + return 'Title: ' + self.title + '\n' + \ + 'Author(s): ' + ', '.join(self.authors) + '\n' \ + 'Publisher: ' + self.publisher + '\n' + \ + 'Year: ' + self.year + '\n' + \ + 'ISBN-10: ' + self.isbn10 + '\n' + \ + 'ISBN-13: ' + self.isbn13 + '\n' + \ + 'Edition: ' + self.edition - def __get_title(self): - return self.__title + def __get_title(self): + return self.__title - def __get_authors(self): - return self.__authors - - def __get_publisher(self): - return self.__publisher + def __get_authors(self): + return self.__authors + + def __get_publisher(self): + return self.__publisher - def __get_year(self): - return self.__year + def __get_year(self): + return self.__year - def __get_isbn10(self): - return self.__isbn10 + def __get_isbn10(self): + return self.__isbn10 - def __get_isbn13(self): - return self.__isbn13 + def __get_isbn13(self): + return self.__isbn13 - def __get_edition(self): - return self.__edition + def __get_edition(self): + return self.__edition - title = property(fget=__get_title) - authors = property(fget=__get_authors) - publisher = property(fget=__get_publisher) - year = property(fget=__get_year) - isbn10 = property(fget=__get_isbn10) - isbn13 = property(fget=__get_isbn13) - edition = property(fget=__get_edition) + title = property(fget=__get_title) + authors = property(fget=__get_authors) + publisher = property(fget=__get_publisher) + year = property(fget=__get_year) + isbn10 = property(fget=__get_isbn10) + isbn13 = property(fget=__get_isbn13) + edition = property(fget=__get_edition) class PyMazon: - """A method of looking up book information on Amazon.""" - def __init__(self, accesskey): - self.__key = accesskey - self.__last_query_time = 0 + """A method of looking up book information on Amazon.""" + def __init__(self, accesskey, secretkey): + self.__key = accesskey + self.__secret = secretkey + self.__last_query_time = 0 - def __form_request(self, isbn): - return 'http://webservices.amazon.com/onca/xml?' + \ - 'Service=AWSECommerceService' + \ - '&Version=2008-08-19' + \ - '&AWSAccessKeyId=' + self.__key + \ - '&Operation=ItemLookup' + \ - '&ResponseGroup=ItemAttributes' + \ - '&IdType=ISBN' + \ - '&SearchIndex=Books' + \ - '&ItemId=' + isbn + def __form_request(self, isbn): + content = {} + dstamp = datetime.datetime.utcfromtimestamp(time.time()) + content['Timestamp'] = dstamp.strftime('%Y-%m-%dT%H:%M:%S.000Z') + content['Service'] = 'AWSECommerceService' + content['Version'] = '2008-08-19' + content['Operation'] = 'ItemLookup' + content['ResponseGroup'] = 'ItemAttributes' + content['IdType'] = 'ISBN' + content['SearchIndex'] = 'Books' + content['ItemId'] = isbn + content['AWSAccessKeyId'] = self.__key - def __elements_text(self, element, name): - result = [] - matching = element.getElementsByTagName(name) - for match in matching: - if len(match.childNodes) != 1: - continue - child = match.firstChild - if child.nodeType != Node.TEXT_NODE: - continue - result.append(child.nodeValue.strip()) - return result + URI_String = [] - def __format_errors(self, errors): - error_list = [] - for error in errors: - error_list.extend(self.__elements_text(error, 'Message')) - return error_list + for key, value in sorted(content.items()): + URI_String.append('%s=%s' % (key, urllib.quote(value))) - def __extract_single(self, element, name): - matches = self.__elements_text(element, name) - if len(matches) == 0: - return '' - return matches[0] + req = '&'.join(URI_String) + to_sign_req = 'GET\necs.amazonaws.com\n/onca/xml\n' + req - def lookup(self, isbn): - file = urllib.urlretrieve(self.__form_request(isbn))[0] - xmldoc = minidom.parse(file) + h = hmac.new(self.__secret, to_sign_req, hashlib.sha256) + sig = base64.b64encode(h.digest()) + req += '&Signature=%s' % urllib.quote(sig) - cur_time = time.time() - while cur_time - self.__last_query_time < 1.0: - sleep(cur_time - self.__last_query_time) - cur_time = time.time() - self.__last_query_time = cur_time + return 'http://ecs.amazonaws.com/onca/xml?' + req - errors = xmldoc.getElementsByTagName('Errors') - if len(errors) != 0: - raise PyMazonError, self.__format_errors(errors) + def __elements_text(self, element, name): + result = [] + matching = element.getElementsByTagName(name) + for match in matching: + if len(match.childNodes) != 1: + continue + child = match.firstChild + if child.nodeType != Node.TEXT_NODE: + continue + result.append(child.nodeValue.strip()) + return result - title = self.__extract_single(xmldoc, 'Title') - authors = self.__elements_text(xmldoc, 'Author') - publisher = self.__extract_single(xmldoc, 'Publisher') - year = self.__extract_single(xmldoc, 'PublicationDate')[0:4] - isbn10 = self.__extract_single(xmldoc, 'ISBN') - isbn13 = self.__extract_single(xmldoc, 'EAN') - edition = self.__extract_single(xmldoc, 'Edition') + def __format_errors(self, errors): + error_list = [] + for error in errors: + error_list.extend(self.__elements_text(error, 'Message')) + return error_list - return PyMazonBook(title, authors, publisher, year, isbn10, isbn13, edition) + def __extract_single(self, element, name): + matches = self.__elements_text(element, name) + if len(matches) == 0: + return '' + return matches[0] + + def lookup(self, isbn): + file = urllib.urlretrieve(self.__form_request(isbn))[0] + xmldoc = minidom.parse(file) + + cur_time = time.time() + while cur_time - self.__last_query_time < 1.0: + sleep(cur_time - self.__last_query_time) + cur_time = time.time() + self.__last_query_time = cur_time + + errors = xmldoc.getElementsByTagName('Errors') + if len(errors) != 0: + raise PyMazonError, self.__format_errors(errors) + + title = self.__extract_single(xmldoc, 'Title') + authors = self.__elements_text(xmldoc, 'Author') + publisher = self.__extract_single(xmldoc, 'Publisher') + year = self.__extract_single(xmldoc, 'PublicationDate')[0:4] + isbn10 = self.__extract_single(xmldoc, 'ISBN') + isbn13 = self.__extract_single(xmldoc, 'EAN') + edition = self.__extract_single(xmldoc, 'Edition') + + return PyMazonBook(title, authors, publisher, year, isbn10, isbn13, edition) diff --git a/ceo/urwid/library.py b/ceo/urwid/library.py index 4d6118f..64d09c7 100644 --- a/ceo/urwid/library.py +++ b/ceo/urwid/library.py @@ -21,7 +21,7 @@ def configure(): """ Load configuration """ - cfg_fields = [ "aws_account_key" ] + cfg_fields = [ "aws_account_key", "aws_secret_key" ] temp_cfg = conf.read(CONFIG_FILE) conf.check_string_fields(CONFIG_FILE, cfg_fields, temp_cfg) cfg.update(temp_cfg) @@ -127,7 +127,7 @@ class BookAddPage(WizardPanel): isbn = self.isbn.get_edit_text() try: - pymazon = PyMazon(cfg["aws_account_key"]) + pymazon = PyMazon(cfg["aws_account_key"], cfg["aws_secret_key"]) book = pymazon.lookup(isbn) currents = lib.Book.select(lib.Book.q.isbn==isbn) diff --git a/etc/library.cf b/etc/library.cf index 41d11d8..afdeccb 100644 --- a/etc/library.cf +++ b/etc/library.cf @@ -2,3 +2,4 @@ library_connect_string = "postgres://librarian:PWPWPWPWPWPWPWPWPWPW@127.0.0.1/library" aws_account_key = "KEYKEYKEYKEYKEYKEYKY" +aws_secret_key = "KEYKEYKEYKEYKEYKEYKY"