PyEDS 0.2: a casual update

Update, September 25, 2013: In the interest of "transparency" (ironically, a word ever more hollow), I re-versioned my script and started it at 0.0 instead of 1.0, hence the code below is for version 0.2 instead of what it originally was (1.1). How dare I mess with versioning? My damn blog, that's how.

Last December, I posted this code which was a simple, starter Python library for using EBSCO's EDS API.

I posted the code and put a link on the EDS WIKI in the hopes that other people would contribute similar libraries in other scripting languages so that using the API would be really easy. Looks like there's one for Ruby underway here on GitHub, so that's really cool.

Unlike the Ruby one, I don't have plans to support retrieving the data in XML. Honestly, the XML returned from the API is not good XML because it uses sibling elements rather than attributes to describe an element's type, etc. As such, one can simply use the dictoxml library to convert the response to XML (after converting the JSON to a Python dictionary) if one wants to use XPath expressions to parse the API response.

Anyway, last week at work I was doing something that required me to use more than the API's "Search" method, so I added support in the Python library for the "Info" and "Retrieve" methods, aka "showInfo" and "retrieveRecord" below.

I'm really casual about this library for now, but I'll probably – once I add support for error handling and a few other things – make it a more formal personal project with better documentation, etc.

Anyway, the updated code is below. I'd love to know if anyone's using it and all.

This module provides a basic Python binding to Ebsco's EDS API, allowing one to:
  - authenticate with a UserID and Password,
  - open and close a session,
  - perform a search,
  - access the EDS Info and Retrieve methods,
  - pretty print the JSON response.
Nitin Arora;
#Usage example:
  import PyEDS as eds
  eds.authenticateUser('USERID_GOES_HERE', 'PASSWORD_GOES_HERE')
  #eds.authenticateFile() #alternative to using authenticateUser() and openSession()
  #uses values in JSON config file argument(default="")
  #sample "" file:
    "EDS_config": {
      "UserId": "USERID_GOES_HERE",
      "Password": "PASSWORD_GOES_HERE",
      "Profile": "PROFILE_GOES_HERE",
      "Guest": "GUEST_GOES_HERE",
      "Org": "ORG_GOES_HERE"
  kittens = eds.advancedSearch('{"SearchCriteria":{"Queries":[{"Term":"kittens"}],"SearchMode":"smart","IncludeFacets":"y","Sort":"relevance"},"RetrievalCriteria":{"View":"brief","ResultsPerPage":10,"PageNumber":1,"Highlight":"y"},"Actions":null}')
  puppies = eds.advancedSearch('{"SearchCriteria":{"Queries":[{"Term":"puppies"}],"SearchMode":"smart","IncludeFacets":"y","Sort":"relevance"},"RetrievalCriteria":{"View":"brief","ResultsPerPage":10,"PageNumber":1,"Highlight":"y"},"Actions":null}')
  cubs = eds.basicSearch('cubs')
  piglets = eds.basicSearch('piglets', view='brief', offset=1, limit=10, order='relevance')
  print 'Some search results with the EDS API ...'
  print '\n"kittens" advanced search as original JSON:'
  print kittens
  print '\n"puppies" advanced search as original JSON:'
  print puppies
  print '\n"kittens" advanced search as JSON with indentation and non-ascii escaping:'
  print eds.prettyPrint(kittens)
  print '\n"cubs" and "piglets" basic searches as original JSON:'
  print cubs, piglets
  print '\nGoodbye.'
  - version 0.2:
    - downgraded version 1.1 to 0.1 ... I was getting ahead of myself!
    - changed default file in authenticateFile() to "" for security.
    - changed data structures for everything except basicSearch() and advancedSearch() to be a
      Python dictionary instead of a JSON string literal.
    - fixed bug for "HighlightTerms" in retrieveRecord() as it needs to be an array, not a string.
    - changed global list name to "__EDS" so as to use the Python convention for priate names.
    - made sure all functions end in a return statement.
  - version 0.1:
    - added retrieveRecord() and showInfo() methods.
  - version 0.0:
    - first version ... that worked! :-]
  - add more options to basicSearch() like "facets", "search mode", "fulltext", "thesauras", etc.
    - can't hurt! :-]
  - consider adding an authenticateIP() function that uses the IP authentication method.
  - deal with expired tokens, etc.; see:
  - deal with any encoding issues re: Asian language characters returned from showInfo().
  - add some error handling stuff.
import json, urllib2
__EDS = {}
def authenticateUser(UserId, Password):
  '''Authenticates user with an EDS UserId and Password.'''
  auth_dict = {'UserId':UserId,'Password':Password,'InterfaceId':'WSapi'}
  auth_json = json.dumps(auth_dict)
  req = urllib2.Request(url='',
  req_open = urllib2.urlopen(req)
  req_results =
  req_results_dictionary = eval(req_results) #convert JSON to dictionary.
  __EDS['AuthToken'] = req_results_dictionary['AuthToken']
  __EDS['AuthTimeout'] = req_results_dictionary['AuthTimeout']

def openSession(Profile, Guest, Org):
  '''Opens the EDS session with an EDS Profile, the Guest value ("y" or "n"), and the Org nickname.'''
  sessionOpen_dict = {'Profile':Profile,'Guest':Guest,'Org':Org}
  sessionOpen_json = json.dumps(sessionOpen_dict)
  req = urllib2.Request(url='',
  req_open = urllib2.urlopen(req)
  req_results =
  req_results_dictionary = eval(req_results)
  __EDS['SessionToken'] = req_results_dictionary['SessionToken'].replace('\\/', '/')

def closeSession():
  '''Closes the EDS sesssion.'''
  sessionClose_dict = {'SessionToken':__EDS['SessionToken']}
  sessionClose_dict = json.dumps(sessionClose_dict)
  req = urllib2.Request(url='',
def authenticateFile(config_file=''):
  '''Uses values in config file to authenticate *and* open a session.'''
  config = open(config_file, 'r').read()
  config = eval(config)
  config = config['EDS_config']
  authenticateUser(config['UserId'], config['Password'])
  openSession(config['Profile'], config['Guest'], config['Org'])
def basicSearch(query, view='brief', offset=1, limit=10, order='relevance'):
  '''Returns search results using basic arguments.'''
  search_json = '''{"SearchCriteria":{"Queries":[{"Term":"%s"}],"SearchMode":"smart","IncludeFacets":"n","Sort":"%s"},
                   ''' %(query, order, view, limit, offset)
  return advancedSearch(search_json)
def advancedSearch(search_json):
  '''Returns search results using the full EDS search syntax (JSON).'''
  req = urllib2.Request(url='',
                        data=search_json, headers={'Content-Type':'application/json',
  req_open = urllib2.urlopen(req)
  req_results =
  return req_results
def retrieveRecord(accession_number, db_code, terms_to_highlight=None, preferred_format='ebook-epub'):
  '''Returns metadata (including abstract and full-text if applicable) for a single record.'''
  retrieve_dict = {'EbookPreferredFormat':preferred_format,'HighlightTerms':terms_to_highlight,'An':accession_number,'DbId':db_code}
  retrieve_json = json.dumps(retrieve_dict)
  req = urllib2.Request(url='',
                        data=retrieve_json, headers={'Content-Type':'application/json',
  req_open = urllib2.urlopen(req)
  req_results =
  return req_results
def showInfo():
  '''Returns EDS limiters, sort options, search tags, expanders, and search modes available to the profile.'''   
  req = urllib2.Request(url='',
  req_open = urllib2.urlopen(req)
  req_results =
  return req_results
def prettyPrint(json_string):
  '''Returns a pretty-printed, UTF-8 encoded JSON string with escaped non-ASCII characters.'''
  dictionary = json.loads(json_string, encoding='utf=8')
  return json.dumps(dictionary, ensure_ascii=True, indent=2, encoding='utf-8')

Related Content:

Leave a Comment

Your email address will not be published. Required fields are marked *