Getting Started: A Simple Holdings Lookup Script
In this example I'll take you through the process of creating a simple CGI script for showing holdings for a particular ISBN. This example uses the Python programming language but should be easy to replicate in other languages such as PHP, Perl, Java or Ruby. If you're new to Python then a good introductory text is Dive Into Python by Mark Pilgrim, which is available to download for free, although I think nothing beats buying a paper copy.
For those who prefer other languages, follow this link to see the same example coded in Perl and PHP
Before starting you need to obtain an API Key. The API Key identifies your usage of the Talis Platform and helps us keep count of how popular the services are. You get your API Key by joining the TDN (which is free), and clicking on the "api keys" tab in your account view. Use the "request new api key" button to create new key that can be used immediately.
Now, to the actual code. To make life easier we're going to use a couple of handy Python libraries. The first is RDFLib which provides a framework for easily parsing and using the RDF produced by the Platform services. The second is Mark Nottingham's sparta.py script which wraps up RDF into a simple object-oriented system. Sparta uses RDFLib for parsing and searching, but adds its own object layer on top. You don't need to understand RDF to follow this article, but learning more about it will help you make the most of the Platform services in the future. The RDF Primer published by the W3C is a good place to start learning.
Our script is going to run as a CGI on our webserver. If you're on a UNIX based system you need a line like the following at the top of the script:
#!/usr/bin/pythonWindows users might use something like this instead:
#!c:\Python24\python.exeFirst we'll import some RDF specific classes, more on which later:
from rdflib.Graph import Graph
from sparta import ThingFactory
from rdflib import RDFNext we need to import some CGI-specific libraries:
import cgi
import cgitb
cgitb.enable()The last two lines there are optional. They enable Python's informative CGI error handling which can be helpful when first creating the script. Our next step is to output the content type header, a requirement for any CGI script:
print "Content-type: text/html\r\n\r\n"Now we're going to add some interactivity. Our script is going to expect a single parameter called isbn which will be the ISBN we want to find holdings for. If the isbn parameter isn't supplied we'll print out an error message, otherwise we'll display a short piece of text reminding the user which ISBN they've specified.
form = cgi.FieldStorage()
if not (form.has_key("isbn") ):
print "No ISBN"
else:
print '<p>Libraries holding copies of %s</p>'%(form["isbn"].value)If you've been following on you should be able to run this script and test whether it prints out the correct message or not. It would be quite easy to modify it to display an HTML form with a text box for entering the ISBN rather than the error message.
Now we're going to introduce our first use of a Platform service: the bibliographic holdings
lookup which returns a list of collections that hold items matching the supplied criteria. The service URL is http://api.talis.com/1/bib/holdings and there are two parameters which are relevant for our purposes: isbn and output.
The isbn parameter denotes the ISBN of the book we want to look up and simply corresponds to our own isbn parameter. output controls the format that the holdings are returned in. For this project we're going to use the rdf output. The URL we will be accessing will look like this:
http://api.talis.com/1/bib/holdings?api_key={KEY}&output=rdf&isbn={ISBN}You need to replace {KEY} with your API Key. The {ISBN} value will be filled in by our script. You can try that in your browser, just insert your API Key and an ISBN like 0596006136 and point your browser at the resulting URL. You should see some RDF/XML output representing holdings of that item.
The relevant part of the holding RDF description uses a model like this:

In this diagram the arrows depict the relationships we are interested in. In particular it says that a holding has a collection and that the collection has a title and an identifer.
Our script needs to construct the URL that we are going to use. This line does the job (remember to substitute your own API Key):
uri = "http://api.talis.com/1/bib/holdings?api_key={KEY}&output=rdf&isbn=%s"%(form["isbn"].value)The Python code above simply replaces the %s placeholder with the value of the isbn parameter passed into the script. Now we need to fetch that URL and process its contents. RDFLib can do this for us using its Graph class:
store = Graph()
store.parse(uri)
When this runs RDFLib will fetch the constructed URL and parse the resulting RDF into the store variable. Now we need to query it. For convenience we'll set up some namespace prefixes which will make our code much more readable:
store.bind("h","http://schemas.talis.com/2005/holdings/schema#")
store.bind("dc","http://purl.org/dc/elements/1.1/")
store.bind("owl", "http://www.w3.org/2002/07/owl#")
Next, we'll initialise Sparta to understand our Holdings model. This is a matter of telling Sparta how our isbn, collection, identifier and title relationships work. Sparta can read RDF schemas to get this information or we can simply add the information in our code like this:
SpartaHelper = ThingFactory(store)
SpartaHelper("h_collection", rdf_type=[SpartaHelper("owl_FunctionalProperty")])
SpartaHelper("dc_identifier", rdf_type=[SpartaHelper("owl_FunctionalProperty")])
SpartaHelper("dc_title", rdf_type=[SpartaHelper("owl_FunctionalProperty")])The first line connects Sparta to the RDFLib graph we created earlier. The following four lines tell Sparta that each relationship has only a single value. Sparta uses that information to create Python attributes representing each relationship. The h, dc and owl prefixes correspond to the namespace prefixes we set up earlier.
Now we're ready to process the results. We need to pick out each holding record from the data, determine the collection associated with the holding and output its title. We get the list of holding records by asking the RDF store for all things of the right type (i.e. those things with a type of http://schemas.talis.com/2005/holdings/schema#Holding)
records = store.subjects(RDF.type, "http://schemas.talis.com/2005/holdings/schema#Holding")Looping through the holdings record is simply:
for record in records:
holding = SpartaHelper(record)The holding variable here is a Sparta enhanced object. Sparta uses the relationship information we specified earlier to add custom properties to the object. One of which will be the collection relationship, so we can simply obtain the collection for this holding:
collection = holding.h_collectionNow to get the title of the collection we can just use collection.dc_title like this:
print '<p>%s</p>'%(collection.dc_title)Running our script now should result in a list of collections that hold items with the specified ISBN.
We can make this rather more functional with the addition of another Platform service: bibliographic deep linking. This service uses the directory to look up web service information for a collection and construct a deep link directly to the specified item in the collection's OPAC. The service needs the collection identifier which can be found manually by searching the directory, or we can just use the one returned by the holdings service. The kind of URL we need to construct is:
http://api.talis.com/1/node/items/{ID}/bib?api_key={KEY}&isbn={ISBN}Where {ID} is the collection's identifier, {KEY} is our API Key and {ISBN} is the isbn we are looking up. Again, you can experiment with this in your web browser. Try using a collection identifier of 0003.inst and an ISBN of 0596006136.
The Python code to generate these links is just another simple substitution, this time using collection.dc_identifier and our isbn parameter:
print '''<p><a href="http://api.talis.com/1/node/items/%s/bib?api_key={KEY}&isbn=%s">%s</a>
</p>'''%(collection.dc_identifier,form["isbn"].value,collection.dc_title)The result should be a list of collections each linked deep into the page in the their OPAC that represents the item we are looking for.
In this example I've covered how to use two Platform services to find and link to items that could be borrowed from libraries. Along the way we've seen how to construct the URLs for the services, fetch and parse RDF data and convert the results into usable HTML. Hopefully you'll have discovered that the Platform offers some simple but powerful services that hide a lot of the complexity of building this kind of application.
You can find out more about Platform services by reading the Platform reference guide and by joining in the conversation in our forum
The complete code for this example is shown below:
#!/usr/bin/python
from rdflib.Graph import Graph
from sparta import ThingFactory
from rdflib import RDF
import cgi
import cgitb
cgitb.enable()
print "Content-type: text/html\r\n\r\n"
form = cgi.FieldStorage()
if not (form.has_key("isbn") ):
print "No ISBN"
else:
print '<p>Libraries holding copies of %s</p>'%(form["isbn"].value)
uri = "http://api.talis.com/1/bib/holdings?api_key={KEY}&output=rdf&isbn=%s"%(form["isbn"].value)
store = Graph()
store.parse(uri)
store.bind("h","http://schemas.talis.com/2005/holdings/schema#")
store.bind("dc","http://purl.org/dc/elements/1.1/")
store.bind("owl", "http://www.w3.org/2002/07/owl#")
SpartaHelper = ThingFactory(store)
SpartaHelper("h_collection", rdf_type=[SpartaHelper("owl_FunctionalProperty")])
SpartaHelper("dc_identifier", rdf_type=[SpartaHelper("owl_FunctionalProperty")])
SpartaHelper("dc_title", rdf_type=[SpartaHelper("owl_FunctionalProperty")])
records = store.subjects(RDF.type, "http://schemas.talis.com/2005/holdings/schema#Holding")
for record in records:
holding = SpartaHelper(record)
collection = holding.h_collection
print '''<p><a href="http://api.talis.com/1/node/items/%s/bib?api_key={KEY}&isbn=%s">%s</a>
</p>'''%(collection.dc_identifier,form["isbn"].value,collection.dc_title)


Recent comments
5 days 2 hours ago
5 days 3 hours ago
5 days 4 hours ago
5 days 4 hours ago
5 days 7 hours ago
1 week 4 days ago
2 weeks 4 days ago
3 weeks 5 days ago
4 weeks 6 hours ago
4 weeks 5 days ago