Finished flushing out Amazon's use of the web services. Added actual tests to

the test suite and updated pyamazon from 0.4 to 0.61
This commit is contained in:
James Vega 2003-12-04 01:56:50 +00:00
parent a74d1d5aef
commit fa1c70b137
3 changed files with 531 additions and 289 deletions

View File

@ -1,4 +1,7 @@
"""Python wrapper for Amazon web APIs
"""Python wrapper
for Amazon web APIs
This module allows you to access Amazon's web APIs,
to do things like search Amazon and get the results programmatically.
@ -40,6 +43,7 @@ Other available functions:
- searchByManufacturer
- searchByListMania
- searchSimilar
- searchByWishlist
Other usage notes:
- Most functions can take product_line as well, see source for possible values
@ -50,11 +54,12 @@ Other usage notes:
"""
__author__ = "Mark Pilgrim (f8dy@diveintomark.org)"
__version__ = "0.4"
__version__ = "0.61"
__cvsversion__ = "$Revision$"[11:-2]
__date__ = "$Date$"[7:-2]
__copyright__ = "Copyright (c) 2002 Mark Pilgrim"
__license__ = "Python"
# Powersearch and return object type fix by Joseph Reagle <geek@goatee.net>
from xml.dom import minidom
import os, sys, getopt, cgi, urllib
@ -152,6 +157,14 @@ def unmarshal(element):
if type(getattr(rc, key)) <> type([]):
setattr(rc, key, [getattr(rc, key)])
setattr(rc, key, getattr(rc, key) + [unmarshal(child)])
elif isinstance(child, minidom.Element) and (child.tagName == 'Details'):
# make the first Details element a key
setattr(rc,key,[unmarshal(child)])
#dbg: because otherwise 'hasattr' only tests
#dbg: on the second occurence: if there's a
#dbg: single return to a query, it's not a
#dbg: list. This module should always
#dbg: return a list of Details objects.
else:
setattr(rc, key, unmarshal(child))
else:
@ -161,7 +174,7 @@ def unmarshal(element):
return rc
def buildURL(search_type, keyword, product_line, type, page, license_key):
url = "http://xml.amazon.com/onca/xml3?v=1.0&f=xml&t=webservices-20"
url = "http://xml.amazon.com/onca/xml?v=1.0&f=xml&t=webservices-20"
url += "&dev-t=%s" % license_key.strip()
url += "&type=%s" % type
if page:
@ -169,10 +182,12 @@ def buildURL(search_type, keyword, product_line, type, page, license_key):
if product_line:
url += "&mode=%s" % product_line
url += "&%s=%s" % (search_type, urllib.quote(keyword))
# print url
return url
## main functions
def search(search_type, keyword, product_line, type="heavy", page=None,
license_key = None, http_proxy = None):
"""search Amazon
@ -225,6 +240,10 @@ def search(search_type, keyword, product_line, type="heavy", page=None,
u = urllib.FancyURLopener(proxies)
usock = u.open(url)
xmldoc = minidom.parse(usock)
# from xml.dom.ext import PrettyPrint
# PrettyPrint(xmldoc)
usock.close()
data = unmarshal(xmldoc).ProductInfo
if hasattr(data, 'ErrorMsg'):
@ -272,4 +291,11 @@ def searchByListMania(listManiaID, type="heavy", page=1, license_key=None, http_
def searchSimilar(ASIN, type="heavy", page=1, license_key=None, http_proxy=None):
return search("SimilaritySearch", ASIN, None, type, page, license_key, http_proxy)
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78:
def searchByWishlist(wishlistID, type="heavy", page=1, license_key=None, http_proxy=None):
return search("WishlistSearch", wishlistID, None, type, page, license_key, http_proxy)
def searchByPower(keyword, product_line="books", type="heavy", page=1, license_key=None, http_proxy=None):
return search("PowerSearch", keyword, product_line, type, page, license_key, http_proxy)
# >>> RecentKing = amazon.searchByPower('author:Stephen King and pubdate:2003')
# >>> SnowCrash = amazon.searchByPower('title:Snow Crash')

View File

@ -69,6 +69,8 @@ class Amazon(callbacks.Privmsg):
def _genResults(self, reply, attribs, items, url):
results = {}
res = []
if isinstance(items, amazon.Bag):
items = [items]
for item in items:
try:
for k,v in attribs.iteritems():
@ -86,6 +88,8 @@ class Amazon(callbacks.Privmsg):
res.append(str(s))
except amazon.AmazonError, e:
self.log.warning(str(e))
except UnicodeEncodeError, e:
self.log.warning(str(e))
return res
def licensekey(self, irc, msg, args):
@ -121,7 +125,7 @@ class Amazon(callbacks.Privmsg):
'URL' : 'url'
}
s = '"%(title)s", written by %(author)s; published by '\
'%(publisher)s.%(url)s'
'%(publisher)s%(url)s'
try:
book = amazon.searchByKeyword(isbn)
res = self._genResults(s, attribs, book, url)
@ -130,7 +134,70 @@ class Amazon(callbacks.Privmsg):
return
except amazon.AmazonError, e:
pass
irc.reply(msg, 'No book was found with that ISBN.')
irc.error(msg, 'No book was found with that ISBN.')
def asin(self, irc, msg, args):
"""[--url] <asin>
Returns the item matching the given ASIN number. If --url is
specified, a link to amazon.com's page for the item will also be
returned.
"""
opts = ['url']
(optlist, rest) = getopt.getopt(args, '', opts)
url = False
for (option, argument) in optlist:
option = option.lstrip('-')
if option == 'url':
url = True
asin = privmsgs.getArgs(rest)
asin = asin.replace('-', '').replace(' ', '')
attribs = {'ProductName' : 'title',
'URL' : 'url'
}
s = '"%(title)s"%(url)s'
try:
item = amazon.searchByASIN(asin)
res = self._genResults(s, attribs, item, url)
if res:
irc.reply(msg, utils.commaAndify(res))
return
except amazon.AmazonError, e:
pass
irc.error(msg, 'No item was found with that ASIN.')
def upc(self, irc, msg, args):
"""[--url] <upc>
Returns the item matching the given UPC number. If --url is
specified, a link to amazon.com's page for the item will also be
returned. Only items in the following categories may be found via upc
search: music, classical, software, dvd, video, vhs, electronics,
pc-hardware, and photo.
"""
opts = ['url']
(optlist, rest) = getopt.getopt(args, '', opts)
url = False
for (option, argument) in optlist:
option = option.lstrip('-')
if option == 'url':
url = True
upc = privmsgs.getArgs(rest)
upc = upc.replace('-', '').replace(' ', '')
attribs = {'ProductName' : 'title',
'Manufacturer' : 'manufacturer',
'URL' : 'url'
}
s = '"%(title)s" %(manufacturer)s%(url)s'
try:
item = amazon.searchByUPC(upc)
res = self._genResults(s, attribs, item, url)
if res:
irc.reply(msg, utils.commaAndify(res))
return
except amazon.AmazonError, e:
pass
irc.error(msg, 'No item was found with that UPC.')
def author(self, irc, msg, args):
"""[--url] <author>
@ -147,14 +214,13 @@ class Amazon(callbacks.Privmsg):
if option == 'url':
url = True
author = privmsgs.getArgs(rest)
self.log.info(author)
attribs = {'ProductName' : 'title',
'Manufacturer' : 'publisher',
'Authors' : 'author',
'URL' : 'url'
}
s = '"%(title)s", written by %(author)s; published by '\
'%(publisher)s.%(url)s'
'%(publisher)s%(url)s'
try:
books = amazon.searchByAuthor(author)
res = self._genResults(s, attribs, books, url)
@ -163,7 +229,66 @@ class Amazon(callbacks.Privmsg):
return
except amazon.AmazonError, e:
pass
irc.reply(msg, 'No books were found by that author.')
irc.error(msg, 'No books were found by that author.')
# FIXME: Until I get a *good* list of categories (ones that actually work),
# these commands will remain unavailable
'''
_textToNode = {'dvds':'130', 'magazines':'599872', 'music':'301668',
'software':'491286', 'vhs':'404272', 'kitchen':'491864',
'video games':'471280', 'toys':'491290', 'camera':'502394',
'outdoor':'468250', 'computers':'565118', 'tools':'468240',
'electronics':'172282'
}
def categories(self, irc, msg, args):
"""takes no arguments
Returns a list of valid categories to use with the bestsellers
commands.
"""
cats = self._textToNode.keys()
cats.sort()
irc.reply(msg, utils.commaAndify(cats))
def bestsellers(self, irc, msg, args):
"""[--url] <category>
Returns a list of best selling items in <category>. The 'categories'
command will return a list of the available categores. If --url
is specified, a link to amazon.com's page for the item will also be
returned.
"""
opts = ['url']
(optlist, rest) = getopt.getopt(args, '', opts)
url = False
for (option, argument) in optlist:
option = option.lstrip('-')
if option == 'url':
url = True
category = privmsgs.getArgs(rest).lower()
if category not in self._textToNode:
irc.error(msg, 'An invalid category was specified. The categories'
' command will return a list of valid categories')
return
category = self._textToNode[category]
attribs = {'ProductName' : 'title',
'Manufacturer' : 'publisher',
'URL' : 'url'
}
s = '"%(title)s", from %(publisher)s.%(url)s'
try:
self.log.warning(category)
items = amazon.browseBestSellers(category)
self.log.warning(items)
res = self._genResults(s, attribs, items, url)
if res:
irc.reply(msg, utils.commaAndify(res))
return
except amazon.AmazonError, e:
pass
irc.error(msg, 'No items were found on that best seller list.')
'''
def artist(self, irc, msg, args):
"""[--url] [--{music,classical}] <artist>
@ -192,7 +317,7 @@ class Amazon(callbacks.Privmsg):
'URL' : 'url'
}
s = '"%(title)s" (%(media)s), by %(artist)s; published by '\
'%(publisher)s.%(url)s'
'%(publisher)s%(url)s'
try:
items = amazon.searchByArtist(artist, product_line=product)
res = self._genResults(s, attribs, items, url)
@ -201,7 +326,7 @@ class Amazon(callbacks.Privmsg):
return
except amazon.AmazonError, e:
pass
irc.reply(msg, 'No items were found by that artist.')
irc.error(msg, 'No items were found by that artist.')
def actor(self, irc, msg, args):
"""[--url] [--{dvd,vhs,video}] <actor>
@ -231,7 +356,7 @@ class Amazon(callbacks.Privmsg):
'URL' : 'url'
}
s = '"%(title)s" (%(media)s), rated %(mpaa)s; released '\
'%(date)s; published by %(publisher)s.%(url)s'
'%(date)s; published by %(publisher)s%(url)s'
try:
items = amazon.searchByActor(actor, product_line=product)
res = self._genResults(s, attribs, items, url)
@ -240,7 +365,46 @@ class Amazon(callbacks.Privmsg):
return
except amazon.AmazonError, e:
pass
irc.reply(msg, 'No items were found starring that actor.')
irc.error(msg, 'No items were found starring that actor.')
def director(self, irc, msg, args):
"""[--url] [--{dvd,vhs,video}] <director>
Returns a list of items by the given director. If --url is
specified, a link to amazon.com's page for the match will also be
returned. The search defaults to using --dvd.
"""
products = ['dvd', 'video', 'vhs']
opts = ['url']
(optlist, rest) = getopt.getopt(args, '', products + opts)
url = False
product = ''
for (option, argument) in optlist:
option = option.lstrip('-')
if option == 'url':
url = True
if option in products:
product = option
product = product or 'dvd'
director = privmsgs.getArgs(rest)
attribs = {'ProductName' : 'title',
'Manufacturer' : 'publisher',
'MpaaRating' : 'mpaa',
'Media' : 'media',
'ReleaseDate' : 'date',
'URL' : 'url'
}
s = '"%(title)s" (%(media)s), rated %(mpaa)s; released '\
'%(date)s; published by %(publisher)s%(url)s'
try:
items = amazon.searchByDirector(director, product_line=product)
res = self._genResults(s, attribs, items, url)
if res:
irc.reply(msg, utils.commaAndify(res))
return
except amazon.AmazonError, e:
pass
irc.error(msg, 'No items were found by that director.')
def manufacturer(self, irc, msg, args):
""" [--url] \
@ -255,7 +419,6 @@ class Amazon(callbacks.Privmsg):
'photo', 'pc-hardware']
opts = ['url']
(optlist, rest) = getopt.getopt(args, '', products + opts)
self.log.info(rest)
url = False
product = ''
for (option, argument) in optlist:
@ -267,11 +430,9 @@ class Amazon(callbacks.Privmsg):
product = product or 'pc-hardware'
manufacturer = privmsgs.getArgs(rest)
attribs = {'ProductName' : 'title',
'Catalog' : 'catalog',
'Manufacturer' : 'manufacturer',
'URL' : 'url'
}
s = '"%(title)s" (%(catalog)s), by %(manufacturer)s.%(url)s'
s = '"%(title)s"%(url)s'
try:
items = amazon.searchByManufacturer(manufacturer,
product_line=product)
@ -281,7 +442,7 @@ class Amazon(callbacks.Privmsg):
return
except amazon.AmazonError, e:
pass
irc.reply(msg, 'No items were found by that manufacturer.')
irc.error(msg, 'No items were found by that manufacturer.')
Class = Amazon

View File

@ -33,7 +33,62 @@ from testsupport import *
class AmazonTestCase(PluginTestCase, PluginDocumentation):
plugins = ('Amazon',)
def setUp(self):
PluginTestCase.setUp(self)
self.assertNotError('licensekey test')
def testIsbn(self):
self.assertHelp('isbn')
self.assertRegexp('isbn 0738203793', r'Buckminster Fuller\'s Universe')
self.assertRegexp('isbn --url 0738203793', r'Buck.*/exec/obidos/ASIN')
def testAsin(self):
self.assertHelp('asin')
self.assertRegexp('asin B00005JM5E', r'Pirates of the Caribbean')
self.assertRegexp('asin --url B00005JM5E', r'Pirate.*ASIN/B00005JM5E')
def testUpc(self):
self.assertHelp('upc')
self.assertRegexp('upc 093624586425', r'Short Bus')
self.assertRegexp('upc --url 093624586425', r'Short Bus.*/exec/obidos')
def testAuthor(self):
self.assertHelp('author')
self.assertRegexp('author torvalds', r'Just for Fun')
self.assertRegexp('author --url torvalds', r'Linus.*/exec/obidos')
def testArtist(self):
self.assertHelp('artist')
self.assertRegexp('artist rahzel', r'Audio CD')
self.assertRegexp('artist --url rahzel', r'Audio CD.*/exec/obidos')
self.assertRegexp('artist --classical rahzel', r'No items were found')
self.assertRegexp('artist --classical vivaldi', r'Audio CD')
def testActor(self):
self.assertHelp('actor')
self.assertRegexp('actor bruce lee', r'DVD')
self.assertRegexp('actor --url bruce lee', r'DVD.*/exec/obidos/')
self.assertRegexp('actor --vhs bruce lee', r'VHS Tape')
self.assertRegexp('actor --video bruce lee', r'DVD|VHS Tape')
def testDirector(self):
self.assertHelp('director')
self.assertRegexp('director gore verbinski', r'DVD')
self.assertRegexp('director --url gore verbinski',
r'DVD.*/exec/obidos/')
self.assertRegexp('director --vhs gore verbinski', r'VHS Tape')
self.assertRegexp('director --video gore verbinski', r'DVD|VHS Tape')
def testManufacturer(self):
self.assertHelp('manufacturer')
self.assertRegexp('manufacturer iomega', r'Iomega')
self.assertRegexp('manufacturer --url iomega',
r'Iomega.*/exec/obidos/')
self.assertRegexp('manufacturer --electronics plextor', r'Plextor')
self.assertRegexp('manufacturer --kitchen henckels', r'Henckels')
self.assertRegexp('manufacturer --videogames ea', r'Madden')
self.assertRegexp('manufacturer --software adobe', r'Photoshop')
self.assertRegexp('manufacturer --photo kodak', r'Kodak')
# vim:set shiftwidth=4 tabstop=8 expandtab textwidth=78: