|
Adventures in lightweight service composition
Three years after I started the LibraryLookup project, people are still regularly discovering and enjoying the ability to automatically broker a connection between Amazon (or another book site) and their local libraries. In a screencast entitled Content, services, and the yin-yang of intermediation I showed a more advanced version of the conventional bookmarklet: a Greasemonkey script that modifies an Amazon page to include a notice about the book's availability in my local library. The screencast ends with a demonstration of another kind of connection brokering. If a book isn't available at the library, I add it to my Amazon wishlist. Then, when it becomes available at the library, it shows up in a special RSS feed that watches my Amazon wishlist.
It's a great example of lightweight service composition. Recently I was reminded that I'd never published the code, so I've included it below. This small Python script orchestrates three different services. First, it queries the Amazon wishlist by way of Amazon's API. Second, it queries the OCLC's xISBN service [1, 2] to convert the single ISBN into a cluster of related ISBNs. Third, it queries my library's OPAC system for each of those related ISBNs.
The use of xISBN is actually gratuitous here, because I could also just query the OPAC for the book's author and title. And in fact, the link that's produced for the RSS file is an example of a title query. But I wanted to demonstrate the use of xISBN. It's important in situations where the author/title information is not readily available.
Other things to note about this example:
- It uses Mark Pilgrim's pyAmazon wrapper for the Amazon API.
- It requires both an Amazon API key and an Amazon wishlist ID.
- If your OPAC is supported by the LibraryLookup bookmarklet generator, you can find the pattern for your ISBN query there.
- If your OPAC isn't the same as mine, you'll need to find the pattern that, when matched, signifies availability. I wish OPACs would simplify this by offering optional XML-flavored outputs.
- I've scheduled the script to run once a day.
- When it runs, a piece of the script I haven't published uploads the resulting RSS file to the location at which I subscribe to it in Bloglines.
This is obviously way beyond the reach of a normal library patron. But forward-thinking libraries could pretty easily wrap all this up into another web-based service and make it available to their patrons. Or, a library superpatron could do that on the library's behalf.
import amazon, re, urllib
amazon.setLicense(YOUR_AMAZON_API_KEY)
wishes = amazon.searchByWishlist(YOUR_AMAZON_WISHLIST_ID)
isbnRE = '\d{7,9}[\d|X]'
libISBNQuery = 'http://ksclib.keene.edu/search/i=%s'
libTitleQuery = 'http://ksclib.keene.edu/search/t?SEARCH=%s'
libAvailRE = '(DUE\s+\d{2,2}\-\d{2,2}\-\d{2,2}|AVAILABLE)'
xisbnQuery = 'http://labs.oclc.org/xisbn/%s'
def rss(items):
return """<rss version="0.91">
<channel>
<title> LibraryLookup reminders </title>
<link> http://www.amazon.com </link>
<description>
Remind me when books on my Amazon wishlist become
available at the library
</description>
%s
</channel>
</rss> """ % items
def availability (isbn):
url = libISBNQuery % isbn
page = urllib.urlopen(url).read()
m = re.search(libAvailRE, page)
if m is not None:
return m.group(0)
else:
return None
def xisbn(isbn):
url = xisbnQuery % isbn
result = urllib.urlopen(url).read()
isbns = re.findall(isbnRE, result)
return isbns
items = ''
for wish in wishes:
isbn = wish.Asin
if re.match(isbnRE,isbn) is None:
continue
avail = None
for relatedISBN in xisbn(isbn):
avail = availability(relatedISBN)
if avail is not None:
break
if avail is not None:
items += """
<item>
<title>%s</title>
<link>%s</link>
<description>%s</description>
</item> """ % ( wish.ProductName,
libTitleQuery % urllib.quote(wish.ProductName), avail )
print rss ( items )
|