Tuesday, January 20. 2009Fetching item availability from Evergreen using the OpenSRF HTTP gatewayThis is a preview of one part of my upcoming session at the OLA SuperConference, Evergreen Exposed: Hacking the open source library system. In the Conifer implementation of Evergreen, at least one of the partners plans to use a decoupled discovery layer rather than the Evergreen OPAC. So we needed to answer the typical question "How do I retrieve the availability of copies for a given work at my institution?" Note that this mini-tutorial is based entirely on OpenSRF 1.0 / Evergreen 1.4; OpenSRF 0.9 will generate different JSON output, and the URL for the OpenSRF gateway will be different. Learning from the old masters: how the Evergreen OPAC does itThe Evergreen OPAC itself relies heavily on JavaScript to dynamically flesh out item details and retrieve item status, so it's actually pretty easy to work out how to do this without even delving too deeply into OpenSRF. First, let's use the Firebug Mozilla extension to follow network requests for a given "title details" page in the OPAC search results for the title: The new world guide to beer. Open up Firebug, enable network monitoring for the OPAC site, and watch the requests flood past for the title details page. We can see that there are a number of POST requests to http://dev.gapines.org/osrf-gateway-v1:
Interpreting the HTTP requests and responsesOkay, so we've found a couple of requests that are pertinent to our goal. And you might be able to guess that the fifth element of the __p entry in the copy status response is the numeric identifier for the copy status, while the sixth element is the copy status name (which, as of OpenSRF 1.0 / Evergreen 1.4, if you pass a different locale value can return a translated value). You might even be able to guess that the response from the copy_counts.summary request returns an array of responses consisting of the organization ID, the call number, and a hash of copy status and the respective counts for each copy status. And you would be guessing correctly. But why guess, when you can get an authoritative interpretation by looking up the class hint (the __c value in the copy_status response of "ccs") in Evergreen's intermediate definition language file /openils/conf/fm_IDL.xml:
<class id="ccs" controller="open-ils.cstore"
oils_obj:fieldmapper="config::copy_status" oils_persist:tablename="config.copy_status">
<fields oils_persist:primary="id" oils_persist:sequence="config.copy_status_id_seq">
<field name="isnew" oils_obj:array_position="0" oils_persist:virtual="true" />
<field name="ischanged" oils_obj:array_position="1" oils_persist:virtual="true" />
<field name="isdeleted" oils_obj:array_position="2" oils_persist:virtual="true" />
<field name="holdable" oils_obj:array_position="3"
oils_persist:virtual="false" reporter:datatype="bool"/>
<field name="id" oils_obj:array_position="4"
oils_persist:virtual="false" reporter:selector="name" reporter:datatype="id"/>
<field name="name" oils_obj:array_position="5"
oils_persist:virtual="false" reporter:datatype="text" oils_persist:i18n="true"/>
<field name="opac_visible" oils_obj:array_position="6"
oils_persist:virtual="false" reporter:datatype="bool"/>
</fields>
So now, by taking our first steps into Evergreen's object persistence model, we can determine authoritatively that the order of values in the __p array maps to "isnew", "ischanged", "isdeleted", "holdable", "id", "name", and "opac_visible". As for the response from the copy_counts.summary call, well, these are not Evergreen objects (they don't have a __c class hint) - but you can use the OpenSRF shell "srfsh" introspect command to view the documentation for the applicable method: bash$ srfsh
srfsh# introspect open-ils.search
... (truncated for legibility) ...
Received Data: {
"__c":"OpenILS_Application",
"__p":{
"api_level":1,
"stream":0,
"object_hint":"OpenILS_Application_Search_Biblio",
"package":"OpenILS::Application::Search::Biblio",
"remote":0,
"api_name":"open-ils.search.biblio.copy_counts.summary.retrieve",
"signature":{
"params":[
],
"desc":"returns an array of these: [
org_id,
callnumber_label,
The introspect output is a bit rough - it's really intended for the doxygen API help interface - but it's good enough for our purposes. If we want to dig into what's going on under the covers, we can follow the package_name value "OpenILS::Application::Search::Biblio" to read the source code for the OpenILS::Application::Search::Biblio Perl module, and look up the method "copy_count_summary" as indicated by the "method" value in the introspect output. That reveals that the input arguments are "($self, $client, $rid, $org, $depth)". Every OpenSRF method automatically receives $self and $client as the first two arguments, so $rid (record ID), $org (organization unit ID), and $depth (organization unit depth) are the variables over which we have control. Zeroing in on the copies for a particular library or library systemIf we want to retrieve the visible copies for just a single organization unit in the entire Evergreen system, we just have to adjust the values of the organization unit ID and organization unit depth parameters accordingly. If we ask for the visible copies for just org_unit ID "125" at depth "2", we narrow down our results to a single hit: {
"status" : 200,
"payload" : [
[
[
"125",
"663.42 JACKSON, MICHAEL",
{
"0" : 1
}
]
]
]
}
So, with all of that ammunition at your disposal, you can write an Evergreen copy status lookup in any decoupled discovery layer that supports HTTP POST or GET requests. Which should be pretty much any discovery layer, right? Frequently used tools and methods for Evergreen / OpenSRF hackingNote, the first: you can easily play with different parameter values for the HTTP POST requests using the json_xs command to pretty print the JSON response: curl -d service=open-ils.search -d locale=en-US \ -d method=open-ils.search.biblio.copy_counts.summary.retrieve \ -d param=8526 -d param=1 -d param=0 \ http://dev.gapines.org/osrf-gateway-v1 | json_xs -t json-pretty Note, the second: the OpenSRF gateway also supports GET requests; simply concatenate the request parameters in a single URL like this . |
QuicksearchAbout MeI'm Dan Scott: barista, library geek, and free-as-in-freedom software developer.
I hack on projects such as the Evergreen
open-source ILS project and PEAR's File_MARC package .
By day I'm the Systems Librarian for Laurentian University. You can reach me by email at dan@coffeecode.net. Identi.ca microblogging
LicenseCategories |

#!/bin/bash
EGSERVER=https://your.evergreenserver.org/
username=$1
password=$2
# first, get a random seed
seed=`curl -s -d service=open-ils.auth \
-d locale=en-US \
-d method=open-ils.auth.authenticate.init \
-d param=\"$username\" \
$EGSERVER/osrf-gateway-v1 | perl -npe "s/.*?payload\".*?\"//; s/\".*//;"`
echo $seed
# construct a hash of the seed and password and send to the server
# the hashes get compared, but neither the password nor a direct hash
# of the password need to get sent over the wire
hashedpw=`echo -n $password | md5sum | perl -npe "s/\s*-$//"`
hash=`echo -n $seed$hashedpw | md5sum | perl -npe "s/\s*-$//"`
echo $hash
echo -d param=\{\"username\":\"$username\",\"password\":\"$hash\"\}
resp=`curl -s -d service=open-ils.auth \
-d locale=en-US \
-d method=open-ils.auth.authenticate.complete \
-d param=\{\"username\":\"$username\",\"password\":\"$hash\",\"type\":\"opac\"\} \
$EGSERVER/osrf-gateway-v1`
echo $resp