diff --git a/README.md b/README.md index f21cb2a..2581fc7 100644 --- a/README.md +++ b/README.md @@ -1,202 +1,223 @@ -# almapipy: Python Wrapper for Alma API - -almapipy is python requests wrapper for easily accessing the Ex Libris Alma API. It is designed to be lightweight, and imposes a structure that mimics the [Alma API architecture](https://developers.exlibrisgroup.com/alma/apis). - -## Installation -```pip install almapipy``` - -## Progress and Roadmap -Get functionality has been developed around all the Alma APIs (listed below). -Post, Put and Delete functions will be gradually added in future releases. - -| API | Get | Post | Put | Delete | -| --- | :---: | :---: | :---: | :---: | -| [bibs](#access-bibliographic-data) | X | | | | -| [analytics](#access-reports) | X | NA | NA | NA | -| [acquisitions](#access-acquisitions) | X | | | | -| [configuration](#access-configuration-settings) | X | | | | -| [courses](#access-courses) | X | | | | -| [resource sharing partners](#access-resource-sharing-partners) | X | | | | -| [task-lists](#access-task-lists) | X | | | | -| [users](#access-users) | X | | | | -| [electronic](#access-electronic) | X | | | | - -## Use - -### Import -```python -# Import and call primary Client class -from almapipy import AlmaCnxn -alma = AlmaCnxn('your_api_key', format='json') -``` -### Access Bibliographic Data -Alma provides a set of Web services for handling bibliographic records related information, enabling you to quickly and easily manipulate bibliographic records related details. These Web services can be used by external systems to retrieve or update bibliographic records related data. -```python -# Use Alma mms_id for retrieving bib records -harry_potter = "9980963346303126" -bib_record = alma.bibs.catalog.get(harry_potter) - -# get holding items for a bib record -holdings = alma.bibs.catalog.get_holdings(harry_potter) - -# get loans by title -loans = alma.bibs.loans.get_by_title(harry_potter) -# or by a specific holding item -loans = alma.bibs.loans.get_by_item(harry_potter, holding_id, item_id) - -# get requests or availability of bib -alma.bibs.requests.get_by_title(harry_potter) -alma.bibs.requests.get_by_item(harry_potter, holding_id, item_id) -alma.bibs.requests.get_availability(harry_potter, period=20) - -# get digital representations -alma.bibs.representations.get(harry_potter) - -# get linked data -alma.bibs.linked_data.get(harry_potter) -``` - -### Access Reports -The Analytics API returns an Alma report. -```python -# Find the system path to the report if don't know path -alma.analytics.paths.get('/shared') - -# retrieve the report as an XML ET element (native response) -report = alma.analytics.reports.get('path_to_report') - -# or convert the xml to json after API call -report = alma.analytics.reports.get('path_to_report', return_json = True) -``` - -### Access Courses -Alma provides a set of Web services for handling courses and reading lists related information, enabling you to quickly and easily manipulate their details. These Web services can be used by external systems such as Courses Management Systems to retrieve or update courses and reading lists related data. -```python -# Get a complete list of courses. Makes multiple calls if necessary. -course_list = alma.courses.get(all_records = True) - -# or filter on search parameters -econ_courses = alma.courses.get(query = {'code': 'ECN'}) - -# get reading lists for a course -course_id = econ_courses['course'][0]['id'] -reading_lists = alma.courses.reading_lists.get(course_id) - -# get more detailed information about a specific reading list -reading_list_id = reading_lists['reading_list'][0]['id'] -alma.courses.reading_lists(course_id, reading_list_id, view = 'full') - -# get citations for a reading list -alma.courses.citations(course_id, reading_list_id) -``` - -### Access Users -Alma provides a set of Web services for handling user information, enabling you to quickly and easily manipulate user details. These Web services can be used by external systems—such as student information systems (SIS)—to retrieve or update user data. -```python -# Get a list of users or filter on search parameters -users = alma.users.get(query = {'first_name': 'Sterling', 'last_name': 'Archer'}) - -# get more information on that user -user_id = users['user'][0]['primary_id'] -alma.users.get(user_id) - -# get all loans or requests for a user. Makes multiple calls if necessary. -loans = alma.user.loans.get(user_id, all_records = True) -requests = alma.user.requests.get(user_id, all_records = True) - -# get deposits or fees for a user -deposits = alma.users.deposits.get(user_id) -fees = alma.users.fees.get(user_id) -``` -### Access Acquisitions -Alma provides a set of Web services for handling acquisitions information, enabling you to quickly and easily manipulate acquisitions details. These Web services can be used by external systems - such as subscription agent systems - to retrieve or update acquisitions data. -```python -# get all funds -alma.acq.funds.get(all_records=True) - -# get po_lines by search -amazon_lines = alma.acq.po_lines.get(query={'vendor_account': 'AMAZON'}) -single_line_id = amazon_lines['po_line'][0]['number'] -# or by a specific line number -alma.acq.po_lines.get(single_line_id) - -# search for a vendor -alma.acq.vendors.get(status='active', query={'name':'AMAZON'}) -# or get a specific vendor -alma.acq.vendors.get('AMAZON.COM') - -# get invoices or polines for a specific vendor -alma.acq.vendors.get_invoices('AMAZON.COM') -alma.acq.vendors.get_po_lines('AMAZON.COM') - -# or get specific invoices -alma.acq.invoices.get('invoice_id') - -# get all licenses -alma.acq.licenses.get(all_records=True) -``` -### Access Configuration Settings -Alma provides a set of Web services for handling Configuration related information, enabling you to quickly and easily receive configuration details. These Web services can be used by external systems in order to get list of possible data. -```python -# Get libraries, locations, departments, and hours -libraries = alma.conf.units.get_libaries() -library_id = libraries['library'][0]['code'] -locations = alma.conf.units.get_locations(library_id) -hours = alma.conf.general.get_hours(library_id) -departments = alma.conf.units.get_departments() - -# Get system code tables -table = 'UserGroups' -alma.conf.general.get_code_table(table) - -# Get scheduled jobs and run history -jobs = alma.conf.jobs.get() -job_id = jobs['job'][0]['id'] -run_history = alma.conf.jobs.get_instances(job_id) - -# Get sets and set members -sets = alma.conf.sets.get() -set_id = sets['set'][0]['id'] -set_members = alma.conf.sets.get_members(set_id) - -# get profiles and reminders -depost_profiles = alma.conf.deposit_profiles.get() -import_profiles = alma.conf.import_profiles.get() -reminders = alma.conf.reminders.get() -``` -### Access Resource Sharing Partners -Alma provides a set of Web services for handling Resource Sharing Partner information, enabling you to quickly and easily manipulate partner details. These Web services can be used by external systems to retrieve or update partner data. -```python -# get partners -partners = alma.partners.get() -``` -### Access Electronic -Alma provides a set of Web services for handling electronic information, enabling you to quickly and easily manipulate electronic details. These Web services can be used by external systems in order to retrieve or update electronic data. -```python -# get e-collections -collections = alma.electronic.collections.get() -collection_id = collections['electronic_collection'][0]['id'] - -# get services for a collection -services = alma.electronic.services.get(collection_id) -service_id = services['electronic_service'][0]['id'] - -# get portfolios for a service -alma.electronic.portfolios.get(collection_id, service_id) - -``` -### Access Task Lists -Alma provides a set of Web services for handling task lists information, enabling you to quickly and easily manipulate their details. These Web services can be used by external systems. -```python -# get requested resources for a specific circulation desk -alma.task_lists.resources.get(library_id, circ_desk) - -# get lending requests for a specific library -alma.task_lists.lending.get(library_id) - -``` - -## Attribution and Contact - - -* **Author**: [Steve Pelkey](mailto:spelkey@ucdavis.edu) + +# almapipy: Python Wrapper for Alma API + +almapipy is python requests wrapper for easily accessing the Ex Libris Alma API. It is designed to be lightweight, and imposes a structure that mimics the [Alma API architecture](https://developers.exlibrisgroup.com/alma/apis). + +## Installation +```pip install (--upgrade) git+https://github.com/pac0san/almapipy``` + +## Progress and Roadmap +Get functionality has been developed around all the Alma APIs (listed below). +Post, Put and Delete functions will be gradually added in future releases. + +| API | Get | Post | Put | Delete | +| --- | :---: | :---: | :---: | :---: | +| [bibs](#access-bibliographic-data) | X | | | | +| [analytics](#access-reports) | X | NA | NA | NA | +| [acquisitions](#access-acquisitions) | X | | | | +| [configuration](#access-configuration-settings) | X | | | | +| [courses](#access-courses) | X | | | | +| [resource sharing partners](#access-resource-sharing-partners) | X | | | | +| [task-lists](#access-task-lists) | X | | | | +| [users](#access-users) | X | X | X | X | +| [electronic](#access-electronic) | X | | | | + +## Use + +### Import +```python +# Import and call primary Client class +from almapipy import AlmaCnxn +alma = AlmaCnxn('your_api_key', location='Europe', data_format='json') +``` +### Access Bibliographic Data +Alma provides a set of Web services for handling bibliographic records related information, enabling you to quickly and easily manipulate bibliographic records related details. These Web services can be used by external systems to retrieve or update bibliographic records related data. +```python +# Use Alma mms_id for retrieving bib records +harry_potter = "9980963346303126" +bib_record = alma.bibs.catalog.get(harry_potter) + +# get holding items for a bib record +holdings = alma.bibs.catalog.get_holdings(harry_potter) + +# get loans by title +loans = alma.bibs.loans.get_by_title(harry_potter) +# or by a specific holding item +loans = alma.bibs.loans.get_by_item(harry_potter, holding_id, item_id) + +# get requests or availability of bib +alma.bibs.requests.get_by_title(harry_potter) +alma.bibs.requests.get_by_item(harry_potter, holding_id, item_id) +alma.bibs.requests.get_availability(harry_potter, period=20) + +# get digital representations +alma.bibs.representations.get(harry_potter) + +# get linked data +alma.bibs.linked_data.get(harry_potter) +``` + +### Access Reports +The Analytics API returns an Alma report. +```python +# Find the system path to the report if don't know path +alma.analytics.paths.get('/shared') + +# retrieve the report as an XML ET element (native response) +report = alma.analytics.reports.get('path_to_report') + +# or convert the xml to json after API call +report = alma.analytics.reports.get('path_to_report', return_json = True) +``` + +### Access Courses +Alma provides a set of Web services for handling courses and reading lists related information, enabling you to quickly and easily manipulate their details. These Web services can be used by external systems such as Courses Management Systems to retrieve or update courses and reading lists related data. +```python +# Get a complete list of courses. Makes multiple calls if necessary. +course_list = alma.courses.get(all_records = True) + +# or filter on search parameters +econ_courses = alma.courses.get(query = {'code': 'ECN'}) + +# get reading lists for a course +course_id = econ_courses['course'][0]['id'] +reading_lists = alma.courses.reading_lists.get(course_id) + +# get more detailed information about a specific reading list +reading_list_id = reading_lists['reading_list'][0]['id'] +alma.courses.reading_lists(course_id, reading_list_id, view = 'full') + +# get citations for a reading list +alma.courses.citations(course_id, reading_list_id) +``` + +### Access Users +Alma provides a set of Web services for handling user information, enabling you to quickly and easily manipulate user details. These Web services can be used by external systems—such as student information systems (SIS)—to retrieve or update user data. +```python + +# Create a user, providing an identifier and some necessary or even additional data +# (according to each Alma implementation) +data = {'first_name': 'Tester #001', 'last_name': 'from Alma', + 'account_type': {'value': 'EXTERNAL', 'desc': 'External'}, + 'external_id': 'SIS', + 'contact_info': { + 'email': [ + {'email_address': 'alma.tester.001@alma.example', + 'email_type': [{'value': 'work', 'desc': 'Work'}]}]}} +user = alma.users.create(identifier = 'alma.tester.001', id_type = 'primary_id', user_data = data) +user = alma.users.create(identifier = '20181126001650001', id_type = 'OTHER_ID_1', user_data = data) + +# Retrieve a list of users or filter on search parameters +users = alma.users.read(query = {'first_name': 'Sterling', 'last_name': 'Archer'}) + +# Retrieve more information on that user +user_id = users['user'][0]['primary_id'] +user = alma.users.read(user_id) + +# Update an user (This function is for advanced users) +response = alma.users.update(primary_id = 'alma.tester.001', user_data = full_user_data) + +# Remove a user, providing an identifier. +response = alma.users.delete(identifier = 'alma.tester.001', id_type = 'primary_id') +response = alma.users.delete(identifier = '20181126001650001', id_type = 'OTHER_ID_1') + +# Retrieve all loans or requests for a user. Makes multiple calls if necessary. +loans = alma.user.loans.read(user_id, all_records = True) +requests = alma.user.requests.read(user_id, all_records = True) + +# Retrieve deposits or fees for a user +deposits = alma.users.deposits.read(user_id) +fees = alma.users.fees.read(user_id) +``` +### Access Acquisitions +Alma provides a set of Web services for handling acquisitions information, enabling you to quickly and easily manipulate acquisitions details. These Web services can be used by external systems - such as subscription agent systems - to retrieve or update acquisitions data. +```python +# get all funds +alma.acq.funds.get(all_records=True) + +# get po_lines by search +amazon_lines = alma.acq.po_lines.get(query={'vendor_account': 'AMAZON'}) +single_line_id = amazon_lines['po_line'][0]['number'] +# or by a specific line number +alma.acq.po_lines.get(single_line_id) + +# search for a vendor +alma.acq.vendors.get(status='active', query={'name':'AMAZON'}) +# or get a specific vendor +alma.acq.vendors.get('AMAZON.COM') + +# get invoices or polines for a specific vendor +alma.acq.vendors.get_invoices('AMAZON.COM') +alma.acq.vendors.get_po_lines('AMAZON.COM') + +# or get specific invoices +alma.acq.invoices.get('invoice_id') + +# get all licenses +alma.acq.licenses.get(all_records=True) +``` +### Access Configuration Settings +Alma provides a set of Web services for handling Configuration related information, enabling you to quickly and easily receive configuration details. These Web services can be used by external systems in order to get list of possible data. +```python +# Restrieve libraries, locations, departments, and hours +libraries = alma.conf.units.read_libaries() +library_id = libraries['library'][0]['code'] +locations = alma.conf.units.read_locations(library_id) +hours = alma.conf.general.read_hours(library_id) +departments = alma.conf.units.read_departments() + +# Retrieve system code tables +table = 'UserGroups' +alma.conf.general.read_code_table(table) + +# Retrieve scheduled jobs and run history +jobs = alma.conf.jobs.read() +job_id = jobs['job'][0]['id'] +run_history = alma.conf.jobs.read_instances(job_id) + +# Retrieve sets and set members +sets = alma.conf.sets.get() +set_id = sets['set'][0]['id'] +set_members = alma.conf.sets.read_members(set_id) + +# Retrieve profiles and reminders +depost_profiles = alma.conf.deposit_profiles.get() +import_profiles = alma.conf.import_profiles.get() +reminders = alma.conf.reminders.read() +``` +### Access Resource Sharing Partners +Alma provides a set of Web services for handling Resource Sharing Partner information, enabling you to quickly and easily manipulate partner details. These Web services can be used by external systems to retrieve or update partner data. +```python +# get partners +partners = alma.partners.get() +``` +### Access Electronic +Alma provides a set of Web services for handling electronic information, enabling you to quickly and easily manipulate electronic details. These Web services can be used by external systems in order to retrieve or update electronic data. +```python +# get e-collections +collections = alma.electronic.collections.get() +collection_id = collections['electronic_collection'][0]['id'] + +# get services for a collection +services = alma.electronic.services.get(collection_id) +service_id = services['electronic_service'][0]['id'] + +# get portfolios for a service +alma.electronic.portfolios.get(collection_id, service_id) + +``` +### Access Task Lists +Alma provides a set of Web services for handling task lists information, enabling you to quickly and easily manipulate their details. These Web services can be used by external systems. +```python +# get requested resources for a specific circulation desk +alma.task_lists.resources.get(library_id, circ_desk) + +# get lending requests for a specific library +alma.task_lists.lending.get(library_id) + +``` + +## Attribution and Contact + + +* **Author**: [Steve Pelkey](mailto:spelkey@ucdavis.edu), [Fco. Sanchez](mailto:GitHub@minarnet.es) diff --git a/almapipy/__init__.py b/almapipy/__init__.py index 77a690e..0d465f5 100644 --- a/almapipy/__init__.py +++ b/almapipy/__init__.py @@ -1,83 +1,95 @@ -""" -Python client for Ex Libris Alma -""" -__author__ = "Steve Pelkey (spelkey@ucdavis.edu)" -__version__ = "0.0.9" - -import os - -from .client import Client -from .bibs import SubClientBibs -from .analytics import SubClientAnalytics -from .courses import SubClientCourses -from .users import SubClientUsers -from .acquisitions import SubClientAcquistions -from .conf import SubClientConfiguration -from .partners import SubClientPartners -from .electronic import SubClientElectronic -from .task_lists import SubClientTaskList -from . import utils - - -class AlmaCnxn(Client): - """"Interface with Alma APIs. - - Various Apis are namespaced beneath this object according to documentation - at https://developers.exlibrisgroup.com/alma/apis. - - E.g. - > alma = AlmaCnxn(your_api_key) - > alma.bibs.catalog.get_record(bib_id) # returns bibliographic records - - Args: - api_key (str): Your Api Key - Location (str): Geographic location of library. - data_format (str): Format of returned data. json or xml. - If xml is selected, data will be returned as python xml ElementTree. - """ - - def __init__(self, apikey, location='America', data_format='json'): - - super(AlmaCnxn, self).__init__() - - # determine base uri based on location - locations = {'America': 'https://api-na.hosted.exlibrisgroup.com', - 'Europe': 'https://api-eu.hosted.exlibrisgroup.com', - 'Asia Pacific': 'https://api-ap.hosted.exlibrisgroup.com', - 'Canada': 'https://api-ca.hosted.exlibrisgroup.com', - 'China': 'https://api-cn.hosted.exlibrisgroup.com'} - if location != 'America': - if location not in locations.keys(): - message = "Valid location arguments are " - message += ", ".join(locations.keys()) - raise utils.ArgError(message=message) - self.cnxn_params['location'] = location - self.cnxn_params['base_uri'] = locations[location] - - # handle preferred format - if data_format not in ['json', 'xml']: - message = "Format argument must be either 'json' or 'xml'" - raise utils.ArgError(message) - self.cnxn_params['format'] = data_format - ns = {'header': 'http://com/exlibris/urm/general/xmlbeans'} - self.cnxn_params['xml_ns'] = ns - - # TODO: validate api key. return list of accessible endpoints - # call __validate_key__ - self.cnxn_params['api_key'] = apikey - - # Hook in the various Alma APIs based on what API key can access - self.bibs = SubClientBibs(self.cnxn_params) - self.analytics = SubClientAnalytics(self.cnxn_params) - self.courses = SubClientCourses(self.cnxn_params) - self.users = SubClientUsers(self.cnxn_params) - self.acq = SubClientAcquistions(self.cnxn_params) - self.conf = SubClientConfiguration(self.cnxn_params) - self.partners = SubClientPartners(self.cnxn_params) - self.electronic = SubClientElectronic(self.cnxn_params) - self.task_lists = SubClientTaskList(self.cnxn_params) - - def __validate_key__(self, apikey): - # loop through each api and access the /test endpoint. - # return list of accessible apis. - pass +# -*- coding: utf-8 -*- + +""" +Python requests wrapper for the Ex Libris Alma API +""" + + +from .client import Client +from .bibs import SubClientBibs +from .analytics import SubClientAnalytics +from .courses import SubClientCourses +from .users import SubClientUsers +from .acquisitions import SubClientAcquistions +from .conf import SubClientConfiguration +from .partners import SubClientPartners +from .electronic import SubClientElectronic +from .task_lists import SubClientTaskList +from . import utils + + +__author__ = "Steve Pelkey, Fco. Sanchez" +__author_email__ = "spelkey@ucdavis.edu, GitHub@minarnet.es" +__project_name__ = "almapipy" +__project_description__ = "Python requests wrapper for the Ex Libris Alma API" +__project_url__ = "https://github.com/pac0san/almapipy" +__license__ = "MIT License" +__version__ = "1.0.1.dev0" +__status__ = "Development" + + +class AlmaCnxn(Client): + """"Interface with Alma APIs. + + Various Apis are namespaced beneath this object according to documentation + at https://developers.exlibrisgroup.com/alma/apis. + + E.g. + > alma = AlmaCnxn(your_api_key) + > alma.bibs.catalog.get_record(bib_id) # returns bibliographic records + + Args: + api_key (str): Your Api Key + Location (str): Geographic location of library. + data_format (str): Format of returned data. json or xml. + If xml is selected, data will be returned as python xml ElementTree. + """ + + def __init__(self, apikey, location='America', data_format='json'): + + super(AlmaCnxn, self).__init__() + + # determine base uri based on location + locations = {'America': 'https://api-na.hosted.exlibrisgroup.com', + 'Europe': 'https://api-eu.hosted.exlibrisgroup.com', + 'Asia Pacific': 'https://api-ap.hosted.exlibrisgroup.com', + 'Canada': 'https://api-ca.hosted.exlibrisgroup.com', + 'China': 'https://api-cn.hosted.exlibrisgroup.com'} + if location != 'America': + if location not in locations.keys(): + message = "Valid location arguments are " + message += ", ".join(locations.keys()) + raise utils.ArgError(message=message) + self.cnxn_params['location'] = location + self.cnxn_params['base_uri'] = locations[location] + + # handle preferred format + if data_format not in ['json', 'xml']: + message = "Format argument must be either 'json' or 'xml'" + raise utils.ArgError(message) + self.cnxn_params['format'] = data_format + ns = {'header': 'http://com/exlibris/urm/general/xmlbeans'} + self.cnxn_params['xml_ns'] = ns + + # TODO: validate api key. return list of accessible endpoints + # call __validate_key__ + self.cnxn_params['api_key'] = apikey + + # Set 'User-Agent' for REST queries + self.cnxn_params['User-Agent'] = '{}/{}'.format(__name__,__version__) + + # Hook in the various Alma APIs based on what API key can access + self.bibs = SubClientBibs(self.cnxn_params) + self.analytics = SubClientAnalytics(self.cnxn_params) + self.courses = SubClientCourses(self.cnxn_params) + self.users = SubClientUsers(self.cnxn_params) + self.acq = SubClientAcquistions(self.cnxn_params) + self.conf = SubClientConfiguration(self.cnxn_params) + self.partners = SubClientPartners(self.cnxn_params) + self.electronic = SubClientElectronic(self.cnxn_params) + self.task_lists = SubClientTaskList(self.cnxn_params) + + def __validate_key__(self, apikey): + # loop through each api and access the /test endpoint. + # return list of accessible apis. + pass diff --git a/almapipy/acquisitions.py b/almapipy/acquisitions.py index 1fcace9..4612645 100644 --- a/almapipy/acquisitions.py +++ b/almapipy/acquisitions.py @@ -1,473 +1,475 @@ -from .client import Client -from . import utils - - -class SubClientAcquistions(Client): - """ - Alma provides a set of Web services for handling acquisitions information, - enabling you to quickly and easily manipulate acquisitions details. - These Web services can be used by external systems - - such as subscription agent systems - to retrieve or update acquisitions data. - For more info: https://developers.exlibrisgroup.com/alma/apis/acq - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to API. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/acq" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/acq" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/d5b14609-b590-470e-baba-9944682f8c7e.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - - # Hook in subclients of api - self.funds = SubClientAcquistionsFunds(self.cnxn_params) - self.po_lines = SubClientAcquistionsPO(self.cnxn_params) - self.vendors = SubClientAcquistionsVendors(self.cnxn_params) - self.invoices = SubClientAcquistionsInvoices(self.cnxn_params) - self.licenses = SubClientAcquistionsLicenses(self.cnxn_params) - - -class SubClientAcquistionsFunds(Client): - """Handles the Funds endpoints of Acquisitions API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/funds' - self.cnxn_params['api_uri_full'] += '/funds' - - def get(self, limit=10, offset=0, library=None, all_records=False, - q_params={}, raw=False): - """Retrieve a list of funds. - - Args: - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - library (str): The code of the library that owns the PO line - for which the relevant funds should be retrieved. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of funds. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - url = self.cnxn_params['api_uri_full'] - args['limit'] = limit - args['offset'] = int(offset) - - if library: - args['library'] = str(library) - - response = self.read(url, args, raw=raw) - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='fund') - return response - - -class SubClientAcquistionsPO(Client): - """Handles the PO Lines endpoints of Acquisitions API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/po-lines' - self.cnxn_params['api_uri_full'] += '/po-lines' - - def get(self, po_line_id=None, query={}, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a list or a single PO-Line. - - Args: - po_line_id (str): The PO-Line number ('number' field in record). - query (dict): Search query for filtering a user list. Optional. - Searching for words from fields: [title, author, mms_id, - publisher, publication_year, publication_place, issn_isbn, - shelving_location, vendor_code, vendor_name, vendor_account, - fund_code, fund_name, number, po_number, invoice_reference & all]. - Only AND operator is supported for multiple filters. - Format {'field': 'value', 'field2', 'value2'}. - e.g. query = {'vendor_account': 'AMAZON'} - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of po-lines or a specific po-line. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if po_line_id: - url += ("/" + str(po_line_id)) - else: - # include paramets specific to user list - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - # add search query if specified in desired format - if query: - args['q'] = self.__format_query__(query) - - response = self.read(url, args, raw=raw) - if po_line_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='po_line') - return response - - def get_items(self, po_line_id, q_params={}, raw=False): - """Retrieve a list items related to a specific PO-line - - Args: - po_line_id (str): The PO-Line number ('number' field in record). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of items in PO-Line - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(po_line_id) + "/items") - - response = self.read(url, args, raw=raw) - return response - - -class SubClientAcquistionsVendors(Client): - """Handles the Vendor endpoints of Acquisitions API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/vendors' - self.cnxn_params['api_uri_full'] += '/vendors' - - def get(self, vendor_id=None, status='ALL', type_='ALL', - query={}, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieve a vendor list or a single vendor. - - Args: - vendor_id (str): A unique identifier for the user (vendorCode). - status (str): Vendor Status. Valid values: [active, inactive, ALL]. - type_ (str): Vendor Type.Valid values: [material_supplier, - access_provider, licensor, governmental]. - query (dict): Search query for filtering a user list. Optional. - Searching for words from fields: [nterface_name, name, code, library & all.]. - Only AND operator is supported for multiple filters. - Format {'field': 'value', 'field2', 'value2'}. - e.g. query = {'name': 'AMAZON'} - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of vendor or a specific vendor's details. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if vendor_id: - url += ("/" + str(vendor_id)) - else: - # include paramets specific to user list - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - args['status'] = str(status) - args['type'] = str(type_) - - # add search query if specified in desired format - if query: - args['q'] = self.__format_query__(query) - - response = self.read(url, args, raw=raw) - if vendor_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='vendor') - return response - - def get_invoices(self, vendor_id, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieve invoices for a specific vendor. - - Args: - vendor_id (str): A unique identifier for the user (vendorCode). - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - library (str): The code of the library that owns the PO line - for which the relevant funds should be retrieved. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of invoices for vendor. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += "/" + (str(vendor_id)) - url += "/invoices" - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='invoice') - return response - - def get_po_lines(self, vendor_id, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieve po-lines for a specific vendor. - - Args: - vendor_id (str): A unique identifier for the user (vendorCode). - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - library (str): The code of the library that owns the PO line - for which the relevant funds should be retrieved. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of po-lines for vendor. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += "/" + (str(vendor_id)) - url += "/po-lines" - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='po_line') - return response - - -class SubClientAcquistionsInvoices(Client): - """Handles the Invoices endpoints of Acquisitions API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/invoices' - self.cnxn_params['api_uri_full'] += '/invoices' - - def get(self, invoice_id=None, query={}, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a list or a single invoice. - - Args: - invoice_id (str): The invoice id. - query (dict): Search query for filtering a user list. Optional. - Searching for words from fields: [invoice_number, vendor_code]. - Only AND operator is supported for multiple filters. - Format {'field': 'value', 'field2', 'value2'}. - e.g. query = {'vendor_code': 'AMAZON'} - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of invoices or specific invoice. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if invoice_id: - url += ("/" + str(invoice_id)) - else: - # include paramets specific to user list - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - # add search query if specified in desired format - if query: - args['q'] = self.__format_query__(query) - - response = self.read(url, args, raw=raw) - if invoice_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='invoice') - return response - - -class SubClientAcquistionsLicenses(Client): - """Handles the Licenses endpoints of Acquisitions API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/licenses' - self.cnxn_params['api_uri_full'] += '/licenses' - - def get(self, license_id=None, status='ALL', review_status='ALL', - query={}, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieve a list or a single license. - - Args: - license_id (str): The license id (license_code). - status (str): Valid values are ACTIVE, DELETED, DRAFT, EXPIRED, RETIRED, ALL - review_status (str): alid values are ALL, and the listed values in LicenseReviewStatuses code table - query (dict): Search query for filtering licenses. Optional. - Searching for words from fields: [name, code, licensor]. - Only AND operator is supported for multiple filters. - Format {'field': 'value', 'field2', 'value2'}. - e.g. query = {'name': 'license_name'} - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of licenses or a specific license. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if license_id: - url += ("/" + str(license_id)) - else: - # include paramets specific to user list - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - args['status'] = str(status) - args['review_status'] = str(review_status) - - # add search query if specified in desired format - if query: - args['q'] = self.__format_query__(query) - - response = self.read(url, args, raw=raw) - if license_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='license') - return response - - def get_amendments(self, license_id, amendment_id=None, q_params={}, raw=False): - """Retrieve a specific license's amendments. - - Args: - license_id (str): The license id (license_code). - amendment_id (str): The amendment id (amendment_code). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of amendments or specific amendment for a license - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(license_id) + "/amendments") - if amendment_id: - url += ("/" + str(amendment_id)) - - response = self.read(url, args, raw=raw) - return response +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils + + +class SubClientAcquistions(Client): + """ + Alma provides a set of Web services for handling acquisitions information, + enabling you to quickly and easily manipulate acquisitions details. + These Web services can be used by external systems - + such as subscription agent systems - to retrieve or update acquisitions data. + For more info: https://developers.exlibrisgroup.com/alma/apis/acq + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to API. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/acq" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/acq" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/d5b14609-b590-470e-baba-9944682f8c7e.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + + # Hook in subclients of api + self.funds = SubClientAcquistionsFunds(self.cnxn_params) + self.po_lines = SubClientAcquistionsPO(self.cnxn_params) + self.vendors = SubClientAcquistionsVendors(self.cnxn_params) + self.invoices = SubClientAcquistionsInvoices(self.cnxn_params) + self.licenses = SubClientAcquistionsLicenses(self.cnxn_params) + + +class SubClientAcquistionsFunds(Client): + """Handles the Funds endpoints of Acquisitions API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/funds' + self.cnxn_params['api_uri_full'] += '/funds' + + def get(self, limit=10, offset=0, library=None, all_records=False, + q_params={}, raw=False): + """Retrieve a list of funds. + + Args: + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + library (str): The code of the library that owns the PO line + for which the relevant funds should be retrieved. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of funds. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + url = self.cnxn_params['api_uri_full'] + args['limit'] = limit + args['offset'] = int(offset) + + if library: + args['library'] = str(library) + + response = self.read(url, args, raw=raw) + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='fund') + return response + + +class SubClientAcquistionsPO(Client): + """Handles the PO Lines endpoints of Acquisitions API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/po-lines' + self.cnxn_params['api_uri_full'] += '/po-lines' + + def get(self, po_line_id=None, query={}, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve a list or a single PO-Line. + + Args: + po_line_id (str): The PO-Line number ('number' field in record). + query (dict): Search query for filtering a user list. Optional. + Searching for words from fields: [title, author, mms_id, + publisher, publication_year, publication_place, issn_isbn, + shelving_location, vendor_code, vendor_name, vendor_account, + fund_code, fund_name, number, po_number, invoice_reference & all]. + Only AND operator is supported for multiple filters. + Format {'field': 'value', 'field2', 'value2'}. + e.g. query = {'vendor_account': 'AMAZON'} + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of po-lines or a specific po-line. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if po_line_id: + url += ("/" + str(po_line_id)) + else: + # include paramets specific to user list + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + # add search query if specified in desired format + if query: + args['q'] = self.__format_query__(query) + + response = self.read(url, args, raw=raw) + if po_line_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='po_line') + return response + + def get_items(self, po_line_id, q_params={}, raw=False): + """Retrieve a list items related to a specific PO-line + + Args: + po_line_id (str): The PO-Line number ('number' field in record). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of items in PO-Line + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(po_line_id) + "/items") + + response = self.read(url, args, raw=raw) + return response + + +class SubClientAcquistionsVendors(Client): + """Handles the Vendor endpoints of Acquisitions API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/vendors' + self.cnxn_params['api_uri_full'] += '/vendors' + + def get(self, vendor_id=None, status='ALL', type_='ALL', + query={}, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieve a vendor list or a single vendor. + + Args: + vendor_id (str): A unique identifier for the user (vendorCode). + status (str): Vendor Status. Valid values: [active, inactive, ALL]. + type_ (str): Vendor Type.Valid values: [material_supplier, + access_provider, licensor, governmental]. + query (dict): Search query for filtering a user list. Optional. + Searching for words from fields: [nterface_name, name, code, library & all.]. + Only AND operator is supported for multiple filters. + Format {'field': 'value', 'field2', 'value2'}. + e.g. query = {'name': 'AMAZON'} + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of vendor or a specific vendor's details. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if vendor_id: + url += ("/" + str(vendor_id)) + else: + # include paramets specific to user list + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + args['status'] = str(status) + args['type'] = str(type_) + + # add search query if specified in desired format + if query: + args['q'] = self.__format_query__(query) + + response = self.read(url, args, raw=raw) + if vendor_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='vendor') + return response + + def get_invoices(self, vendor_id, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieve invoices for a specific vendor. + + Args: + vendor_id (str): A unique identifier for the user (vendorCode). + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + library (str): The code of the library that owns the PO line + for which the relevant funds should be retrieved. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of invoices for vendor. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += "/" + (str(vendor_id)) + url += "/invoices" + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.read(url, args, raw=raw) + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='invoice') + return response + + def get_po_lines(self, vendor_id, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieve po-lines for a specific vendor. + + Args: + vendor_id (str): A unique identifier for the user (vendorCode). + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + library (str): The code of the library that owns the PO line + for which the relevant funds should be retrieved. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of po-lines for vendor. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += "/" + (str(vendor_id)) + url += "/po-lines" + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.read(url, args, raw=raw) + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='po_line') + return response + + +class SubClientAcquistionsInvoices(Client): + """Handles the Invoices endpoints of Acquisitions API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/invoices' + self.cnxn_params['api_uri_full'] += '/invoices' + + def get(self, invoice_id=None, query={}, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve a list or a single invoice. + + Args: + invoice_id (str): The invoice id. + query (dict): Search query for filtering a user list. Optional. + Searching for words from fields: [invoice_number, vendor_code]. + Only AND operator is supported for multiple filters. + Format {'field': 'value', 'field2', 'value2'}. + e.g. query = {'vendor_code': 'AMAZON'} + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of invoices or specific invoice. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if invoice_id: + url += ("/" + str(invoice_id)) + else: + # include paramets specific to user list + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + # add search query if specified in desired format + if query: + args['q'] = self.__format_query__(query) + + response = self.read(url, args, raw=raw) + if invoice_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='invoice') + return response + + +class SubClientAcquistionsLicenses(Client): + """Handles the Licenses endpoints of Acquisitions API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/licenses' + self.cnxn_params['api_uri_full'] += '/licenses' + + def get(self, license_id=None, status='ALL', review_status='ALL', + query={}, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieve a list or a single license. + + Args: + license_id (str): The license id (license_code). + status (str): Valid values are ACTIVE, DELETED, DRAFT, EXPIRED, RETIRED, ALL + review_status (str): alid values are ALL, and the listed values in LicenseReviewStatuses code table + query (dict): Search query for filtering licenses. Optional. + Searching for words from fields: [name, code, licensor]. + Only AND operator is supported for multiple filters. + Format {'field': 'value', 'field2', 'value2'}. + e.g. query = {'name': 'license_name'} + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of licenses or a specific license. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if license_id: + url += ("/" + str(license_id)) + else: + # include paramets specific to user list + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + args['status'] = str(status) + args['review_status'] = str(review_status) + + # add search query if specified in desired format + if query: + args['q'] = self.__format_query__(query) + + response = self.read(url, args, raw=raw) + if license_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='license') + return response + + def get_amendments(self, license_id, amendment_id=None, q_params={}, raw=False): + """Retrieve a specific license's amendments. + + Args: + license_id (str): The license id (license_code). + amendment_id (str): The amendment id (amendment_code). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of amendments or specific amendment for a license + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(license_id) + "/amendments") + if amendment_id: + url += ("/" + str(amendment_id)) + + response = self.read(url, args, raw=raw) + return response diff --git a/almapipy/analytics.py b/almapipy/analytics.py index 50403df..26accc8 100644 --- a/almapipy/analytics.py +++ b/almapipy/analytics.py @@ -1,174 +1,176 @@ -from .client import Client -from . import utils -import xml.etree.ElementTree as ET - - -class SubClientAnalytics(Client): - """ - Handles requests to analytics API. - For more info: https://developers.exlibrisgroup.com/alma/apis/analytics - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to API. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/analytics" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/analytics" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/10788916-19f6-4f19-aaf1-c18fa0c31ccd.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - self.cnxn_params['xml_ns']['report'] = 'urn:schemas-microsoft-com:xml-analysis:rowset' - - # Hook in subclients of api - self.paths = SubClientAnalyticsPaths(self.cnxn_params) - self.reports = SubClientAnalyticsReports(self.cnxn_params) - - -class SubClientAnalyticsPaths(Client): - """Handles the path endpoints of analytics API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/paths' - self.cnxn_params['api_uri_full'] += '/paths' - - def get(self, path=None, q_params={}, raw=False): - """This API lists the contents of the Alma Analytics report directory. - If path is not specified, will just return info of root folder. - - Args: - path (str): folder directory relative to root. - Does not need to be url encoded. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - ls of directory specificed by path. - - """ - url = self.cnxn_params['api_uri_full'] - if path: - url += ("/" + str(path)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - -class SubClientAnalyticsReports(Client): - """Handles the reports endpoints of analytics API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/reports' - self.cnxn_params['api_uri_full'] += '/reports' - - def get(self, path, _filter=None, limit=25, col_names=True, return_json=False, - all_records=False, q_params={}, raw=False): - """This API returns an Alma Analytics report as XML. - JSON currently unavailable. Use return_json param to convert after the call. - - Args: - path (str): path of report relative to report root. - non-URL-encoded. Leave slashes and spaces. - _filter (str): An XML representation of a filter in OBI format. - See documentation for more info. - limit (int): Maximum number of results to return - Between 25 and 1000 (multiples of 25). - col_names (bool): Include column heading information. - To ensure consistent sort order it might be required to turn it off. - return_json (false): If True, converts xml into json-like structure. - all_records (bool): Return all rows for a report. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - If all_records == True, returns a list. - - Returns: - XML ET or json-like structure of report, - - """ - url = self.cnxn_params['api_uri_full'] - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - args['path'] = path - args['format'] = 'xml' - args['limit'] = str(int(limit)) - args['col_names'] = col_names - if _filter: - args['filter'] = _filter - row_tag = "{urn:schemas-microsoft-com:xml-analysis:rowset}Row" - set_tag = "{urn:schemas-microsoft-com:xml-analysis:rowset}rowset" - columns_tag = "{http://www.w3.org/2001/XMLSchema}element" - report = self.read(url, args, raw=raw) - - if raw: - # extract xml from raw response - # start list with raw responses - if not all_records: - return report - responses = [report] - report = ET.fromstring(report.text) - - if all_records: - # check if there are more records to get - if report[0].find('IsFinished').text == 'false': - get_more = True - - # just need token and apikey for future calls - margs = {'apikey': self.cnxn_params['api_key']} - margs['token'] = report[0].find('ResumptionToken').text - margs['format'] = 'xml' - - # find report content in XML report - xml_rows = list(report.iter(set_tag))[0] - else: - get_more = False - - # make additional api calls and append rows to original xml - while get_more: - - report_more = self.read(url, margs, raw=raw) - - if raw: - responses += [report_more] - report_more = ET.fromstring(report_more.text) - - else: - for new_row in report_more.iter(row_tag): - xml_rows.append(new_row) - - # break loop if no more records - if report_more[0].find('IsFinished').text != 'false': - get_more = False - - if raw: - return responses - - if return_json: - # extract column names - columns_tag = "{http://www.w3.org/2001/XMLSchema}element" - columns = list(report.iter(columns_tag)) - headers = {} - for col in columns: - key = col.attrib['name'] - try: - value = col.attrib['{urn:saw-sql}columnHeading'] - except: - value = col.attrib['name'] - value = value.lower().replace(" ", "_") - headers[key] = value - - # covert to list of dicts - dicts = [] - rows = report.iter(row_tag) - for row in rows: - values = [col.text for col in row] - keys = [headers[col.tag.split('}')[-1]] for col in row] - dicts.append({key: value for key, value in zip(keys, values)}) - return dicts - - return report +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils +import xml.etree.ElementTree as ET + + +class SubClientAnalytics(Client): + """ + Handles requests to analytics API. + For more info: https://developers.exlibrisgroup.com/alma/apis/analytics + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to API. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/analytics" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/analytics" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/10788916-19f6-4f19-aaf1-c18fa0c31ccd.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + self.cnxn_params['xml_ns']['report'] = 'urn:schemas-microsoft-com:xml-analysis:rowset' + + # Hook in subclients of api + self.paths = SubClientAnalyticsPaths(self.cnxn_params) + self.reports = SubClientAnalyticsReports(self.cnxn_params) + + +class SubClientAnalyticsPaths(Client): + """Handles the path endpoints of analytics API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/paths' + self.cnxn_params['api_uri_full'] += '/paths' + + def get(self, path=None, q_params={}, raw=False): + """This API lists the contents of the Alma Analytics report directory. + If path is not specified, will just return info of root folder. + + Args: + path (str): folder directory relative to root. + Does not need to be url encoded. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + ls of directory specificed by path. + + """ + url = self.cnxn_params['api_uri_full'] + if path: + url += ("/" + str(path)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + +class SubClientAnalyticsReports(Client): + """Handles the reports endpoints of analytics API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/reports' + self.cnxn_params['api_uri_full'] += '/reports' + + def get(self, path, _filter=None, limit=25, col_names=True, return_json=False, + all_records=False, q_params={}, raw=False): + """This API returns an Alma Analytics report as XML. + JSON currently unavailable. Use return_json param to convert after the call. + + Args: + path (str): path of report relative to report root. + non-URL-encoded. Leave slashes and spaces. + _filter (str): An XML representation of a filter in OBI format. + See documentation for more info. + limit (int): Maximum number of results to return + Between 25 and 1000 (multiples of 25). + col_names (bool): Include column heading information. + To ensure consistent sort order it might be required to turn it off. + return_json (false): If True, converts xml into json-like structure. + all_records (bool): Return all rows for a report. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + If all_records == True, returns a list. + + Returns: + XML ET or json-like structure of report, + + """ + url = self.cnxn_params['api_uri_full'] + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + args['path'] = path + args['format'] = 'xml' + args['limit'] = str(int(limit)) + args['col_names'] = col_names + if _filter: + args['filter'] = _filter + row_tag = "{urn:schemas-microsoft-com:xml-analysis:rowset}Row" + set_tag = "{urn:schemas-microsoft-com:xml-analysis:rowset}rowset" + columns_tag = "{http://www.w3.org/2001/XMLSchema}element" + report = self.read(url, args, raw=raw) + + if raw: + # extract xml from raw response + # start list with raw responses + if not all_records: + return report + responses = [report] + report = ET.fromstring(report.text) + + if all_records: + # check if there are more records to get + if report[0].find('IsFinished').text == 'false': + get_more = True + + # just need token and apikey for future calls + margs = {'apikey': self.cnxn_params['api_key']} + margs['token'] = report[0].find('ResumptionToken').text + margs['format'] = 'xml' + + # find report content in XML report + xml_rows = list(report.iter(set_tag))[0] + else: + get_more = False + + # make additional api calls and append rows to original xml + while get_more: + + report_more = self.read(url, margs, raw=raw) + + if raw: + responses += [report_more] + report_more = ET.fromstring(report_more.text) + + else: + for new_row in report_more.iter(row_tag): + xml_rows.append(new_row) + + # break loop if no more records + if report_more[0].find('IsFinished').text != 'false': + get_more = False + + if raw: + return responses + + if return_json: + # extract column names + columns_tag = "{http://www.w3.org/2001/XMLSchema}element" + columns = list(report.iter(columns_tag)) + headers = {} + for col in columns: + key = col.attrib['name'] + try: + value = col.attrib['{urn:saw-sql}columnHeading'] + except: + value = col.attrib['name'] + value = value.lower().replace(" ", "_") + headers[key] = value + + # covert to list of dicts + dicts = [] + rows = report.iter(row_tag) + for row in rows: + values = [col.text for col in row] + keys = [headers[col.tag.split('}')[-1]] for col in row] + dicts.append({key: value for key, value in zip(keys, values)}) + return dicts + + return report diff --git a/almapipy/bibs.py b/almapipy/bibs.py index 3d9d32e..a4f46b3 100644 --- a/almapipy/bibs.py +++ b/almapipy/bibs.py @@ -1,498 +1,500 @@ -from .client import Client -from . import utils - - -class SubClientBibs(Client): - """ - Handles requests to bib endpoint. - For more info: https://developers.exlibrisgroup.com/alma/apis/bibs - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to Bibs. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/bibs" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/bibs" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/af2fb69d-64f4-42bc-bb05-d8a0ae56936e.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - - # Hook in subclients of bib - self.catalog = SubClientBibsCatalog(self.cnxn_params) - self.collections = SubClientBibsCollections(self.cnxn_params) - self.loans = SubClientBibsLoans(self.cnxn_params) - self.requests = SubClientBibsRequests(self.cnxn_params) - self.representations = SubClientBibsRepresentations(self.cnxn_params) - self.linked_data = SubClientBibsLinkedData(self.cnxn_params) - - -class SubClientBibsCatalog(Client): - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params - - def get(self, bib_ids, expand=None, q_params={}, raw=False): - """ - Returns Bib records from a list of Bib IDs submitted in a parameter. - - Args: - bib_ids (list or str): list of bib Record IDs. len = 1-100. - or string of one record id. - expand (str): provides additional information: - p_avail - Expand physical inventory information. - e_avail - Expand electronic inventory information. - d_avail - Expand digital inventory information. - To use more than one, use a comma separator. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - Returns a single or list of bib records. - https://developers.exlibrisgroup.com/alma/apis/xsd/rest_bibs.xsd?tags=GET - - """ - url = self.cnxn_params['api_uri_full'] - - # validate arguments - if type(q_params) != dict: - message = "q_params must be a dictionary." - raise utils.ArgError(message) - if type(bib_ids) != list and type(bib_ids) != str: - message = "bib_ids must be a list of ids, or single string." - raise utils.ArgError(message) - - # format arguments - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - # determine which endpoint to call. - if type(bib_ids) == str: - url += ('/' + bib_ids) - else: - args['mms_id'] = bib_ids - - if expand: - if expand not in ['p_avail', 'e_avail', 'd_avail']: - message = 'expand must be one of the follow: ' + str(expand) - raise utils.ArgError(message) - args['expand'] = expand - - return self.read(url, args, raw=raw) - - def get_holdings(self, bib_id, holding_id=None, q_params={}, raw=False): - """Returns list of holding records or single holding record - for a given bib record ID. - - If retrieving a single holding record with the holding_id param, - it is returned as MARC XML format, so it is not recommended to - use this service with JSON format. - You can overide the json global setting - by entering 'format':'xml' as item in q_params parameter. - - Args: - bib_id (str): The bib ID (mms_id). - holding_id (str): The Holding Record ID. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of holding records or single holding record - for a given bib record ID. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - url += '/holdings' - if holding_id: - url += ('/' + str(holding_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - def get_holding_items(self, bib_id, holding_id, item_id=None, q_params={}, raw=False): - """Returns list of holding record items or a single item - for a given holding record of a bib. - Includes label printing information - - Args: - bib_id (str): The bib ID (mms_id). - holding_id (str): The Holding Record ID (holding_id). - item_id (str): The holding item id (item_pid). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of holding record items or a single item - for a given bib record ID. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - url += ('/holdings/' + str(holding_id)) + '/items' - if item_id: - url += ('/' + str(item_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - def get_portfolios(self, bib_id, portfolio_id=None, q_params={}, raw=False): - """Returns a list or single portfolio for a Bib. - - If retrieving a single holding record with the holding_id param, - it is returned as MARC XML format, so it is not recommended to - use this service with JSON format. - You can overide the json global setting - by entering 'format':'xml' as item in q_params parameter. - - Args: - bib_id (str): The bib ID (mms_id). - portfolio_id (str): The Electronic Portfolio ID. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - Returns a list or single portfolio for a Bib. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - url += '/portfolios' - if portfolio_id: - url += ('/' + str(portfolio_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - -class SubClientBibsCollections(Client): - """Handles collections""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/collections' - self.cnxn_params['api_uri_full'] += '/collections' - - def get(self, pid=None, query={}, q_params={}, raw=False): - """Returns meta data about collections in libraries. - If pid argument is used, will only return one collection. - - Args: - pid (str): The collection ID. - query (dict): Search query for filtering list. Optional. - Searching for words from fields: [library, collection name, external system, external ID]. - Only AND operator is supported for multiple filters. - Format {'field': 'value', 'field2', 'value2'}. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - A list of collections or a collection for a given pid. - - """ - url = self.cnxn_params['api_uri_full'] - if pid: - url += ("/" + str(pid)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - if query: - args['q'] = self.__format_query__(query) - - response = self.read(url, args, raw=raw) - return response - - def get_bibs(self, pid, q_params={}, raw=False): - """Get bibs in a collection using pid. - - Args: - pid (str): The collection ID. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - A a list of bibliographic titles in a given collection. - - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(pid)) - url += '/bibs' - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - -class SubClientBibsLoans(Client): - """Accesses loans endpoints""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - - def get_by_item(self, bib_id, holding_id, item_id, - loan_id=None, q_params={}, raw=False): - """Returns Loan by Item information. - - Args: - bib_id (str): The bib ID (mms_id). - holding_id (str): The Holding Record ID (holding_id). - item_id (str): The holding item ID (item_pid). - loan_id (str): The loan ID - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of loans or a single loan for a given item. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - url += ('/holdings/' + str(holding_id)) - url += ('/items/' + str(item_id) + "/loans") - if loan_id: - url += ('/' + str(loan_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - def get_by_title(self, bib_id, loan_id=None, q_params={}, raw=False): - """Returns Loan by title information. - - Args: - bib_id (str): The bib ID (mms_id). - loan_id (str): The loan ID - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of loans or a single loan for a given title. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id) + '/loans') - if loan_id: - url += ('/' + str(loan_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - -class SubClientBibsRequests(Client): - """Accesses user request endpoints""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - - def get_by_item(self, bib_id, holding_id, item_id, - request_id=None, q_params={}, raw=False): - """Returns Loan by Item information. - - Args: - bib_id (str): The bib ID (mms_id). - holding_id (str): The Holding Record ID (holding_id). - item_id (str): The holding item ID (item_pid). - request_id (str): The loan ID - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of loans or a single loan for a given item. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - url += ('/holdings/' + str(holding_id)) - url += ('/items/' + str(item_id) + "/requests") - if request_id: - url += ('/' + str(request_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - def get_by_title(self, bib_id, request_id=None, q_params={}, raw=False): - """Returns Loan by title information. - - Args: - bib_id (str): The bib ID (mms_id). - request_id (str): The loan ID - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of loans or a single loan for a given title. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id) + '/requests') - if request_id: - url += ('/' + str(request_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - def get_availability(self, bib_id, period, period_type='days', - holding_id=None, item_id=None, q_params={}, raw=False): - """Returns list of periods in which specific title or item - is unavailable for booking. - - To get a specific item, holding_id and item_id parameters are required. - - Note: user_id does not populate if retrieving by just bid_id. - - Args: - bib_id (str): The bib ID (mms_id). - period (str or int): The number of days/weeks/months to retrieve availability for. - period_type (str): The type of period of interest. Optional. Possible values: days, weeks, months. - holding_id (str): The Holding Record ID (holding_id). - item_id (str): The holding item ID (item_pid). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of periods title/item is unavailable for booking. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - if holding_id and item_id: - url += ("/holdings/" + str(holding_id)) - url += ('/items/' + str(item_id)) - elif holding_id or item_id: - message = "If getting availability for an item, " - message += "Both holding_id and item_id are required arguments." - raise utils.ArgError(message) - url += "/booking-availability" - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - args['period'] = str(period) - args['period_type'] = str(period_type) - - return self.read(url, args, raw=raw) - - def get_options(self, bib_id, user_id='GUEST', - holding_id=None, item_id=None, - q_params={}, raw=False): - """Returns request options for a specific title or item based on user. - - To get a specific item, holding_id and item_id parameters are required. - - Note: user_id does not populate if retrieving by just bid_id. - - Args: - bib_id (str): The bib ID (mms_id). - user_id (str): The id of the user for which the request options will be calculated. - holding_id (str): The Holding Record ID (holding_id). - item_id (str): The holding item ID (item_pid). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - Request options for a specific title or item based on user. - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - if holding_id and item_id: - url += ("/holdings/" + str(holding_id)) - url += ('/items/' + str(item_id)) - elif holding_id or item_id: - message = "If getting request options for an item, " - message += "Both holding_id and item_id are required arguments." - raise utils.ArgError(message) - url += "/request-options" - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - args['user_id'] = str(user_id) - - return self.read(url, args, raw=raw) - - -class SubClientBibsRepresentations(Client): - """Handles Digital Representations""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - - def get(self, bib_id, q_params={}, raw=False): - """Returns a list of Digital Representations for a given Bib MMS-ID. - - Args: - bib_id (str): The bib ID (mms_id). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - A list of Digital Representations for a given bib record. - - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - url += "/representations" - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - def get_details(self, bib_id, rep_id, files=False, q_params={}, raw=False): - """Returns a specific Digital Representation's details. - Supported for Remote and Non-Remote Representations. - - Args: - bib_id (str): The bib ID (mms_id). - rep_id (str): The Representation ID. - files (bool): Denote whether to return files? - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - A list of Digital Representations for a given bib record. - - """ - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(bib_id)) - url += "/representations/" - url += rep_id - if files: - url += "/files" - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - -class SubClientBibsLinkedData(Client): - """Handles Linked Data for a Bib Record""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - - def get(self, bib_id, q_params={}, raw=False): - """Returns Linked data for a given Bib MMS-ID. - - Args: - bib_id (str): The bib ID (mms_id). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - Linked data URIs for a given bib record. - - """ - url = self.cnxn_params['api_uri_full'] - url += "/linked-open-data" - url += ("/" + str(bib_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils + + +class SubClientBibs(Client): + """ + Handles requests to bib endpoint. + For more info: https://developers.exlibrisgroup.com/alma/apis/bibs + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to Bibs. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/bibs" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/bibs" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/af2fb69d-64f4-42bc-bb05-d8a0ae56936e.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + + # Hook in subclients of bib + self.catalog = SubClientBibsCatalog(self.cnxn_params) + self.collections = SubClientBibsCollections(self.cnxn_params) + self.loans = SubClientBibsLoans(self.cnxn_params) + self.requests = SubClientBibsRequests(self.cnxn_params) + self.representations = SubClientBibsRepresentations(self.cnxn_params) + self.linked_data = SubClientBibsLinkedData(self.cnxn_params) + + +class SubClientBibsCatalog(Client): + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params + + def get(self, bib_ids, expand=None, q_params={}, raw=False): + """ + Returns Bib records from a list of Bib IDs submitted in a parameter. + + Args: + bib_ids (list or str): list of bib Record IDs. len = 1-100. + or string of one record id. + expand (str): provides additional information: + p_avail - Expand physical inventory information. + e_avail - Expand electronic inventory information. + d_avail - Expand digital inventory information. + To use more than one, use a comma separator. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + Returns a single or list of bib records. + https://developers.exlibrisgroup.com/alma/apis/xsd/rest_bibs.xsd?tags=GET + + """ + url = self.cnxn_params['api_uri_full'] + + # validate arguments + if type(q_params) != dict: + message = "q_params must be a dictionary." + raise utils.ArgError(message) + if type(bib_ids) != list and type(bib_ids) != str: + message = "bib_ids must be a list of ids, or single string." + raise utils.ArgError(message) + + # format arguments + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + # determine which endpoint to call. + if type(bib_ids) == str: + url += ('/' + bib_ids) + else: + args['mms_id'] = bib_ids + + if expand: + if expand not in ['p_avail', 'e_avail', 'd_avail']: + message = 'expand must be one of the follow: ' + str(expand) + raise utils.ArgError(message) + args['expand'] = expand + + return self.read(url, args, raw=raw) + + def get_holdings(self, bib_id, holding_id=None, q_params={}, raw=False): + """Returns list of holding records or single holding record + for a given bib record ID. + + If retrieving a single holding record with the holding_id param, + it is returned as MARC XML format, so it is not recommended to + use this service with JSON format. + You can overide the json global setting + by entering 'format':'xml' as item in q_params parameter. + + Args: + bib_id (str): The bib ID (mms_id). + holding_id (str): The Holding Record ID. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of holding records or single holding record + for a given bib record ID. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + url += '/holdings' + if holding_id: + url += ('/' + str(holding_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + def get_holding_items(self, bib_id, holding_id, item_id=None, q_params={}, raw=False): + """Returns list of holding record items or a single item + for a given holding record of a bib. + Includes label printing information + + Args: + bib_id (str): The bib ID (mms_id). + holding_id (str): The Holding Record ID (holding_id). + item_id (str): The holding item id (item_pid). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of holding record items or a single item + for a given bib record ID. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + url += ('/holdings/' + str(holding_id)) + '/items' + if item_id: + url += ('/' + str(item_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + def get_portfolios(self, bib_id, portfolio_id=None, q_params={}, raw=False): + """Returns a list or single portfolio for a Bib. + + If retrieving a single holding record with the holding_id param, + it is returned as MARC XML format, so it is not recommended to + use this service with JSON format. + You can overide the json global setting + by entering 'format':'xml' as item in q_params parameter. + + Args: + bib_id (str): The bib ID (mms_id). + portfolio_id (str): The Electronic Portfolio ID. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + Returns a list or single portfolio for a Bib. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + url += '/portfolios' + if portfolio_id: + url += ('/' + str(portfolio_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + +class SubClientBibsCollections(Client): + """Handles collections""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/collections' + self.cnxn_params['api_uri_full'] += '/collections' + + def get(self, pid=None, query={}, q_params={}, raw=False): + """Returns meta data about collections in libraries. + If pid argument is used, will only return one collection. + + Args: + pid (str): The collection ID. + query (dict): Search query for filtering list. Optional. + Searching for words from fields: [library, collection name, external system, external ID]. + Only AND operator is supported for multiple filters. + Format {'field': 'value', 'field2', 'value2'}. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + A list of collections or a collection for a given pid. + + """ + url = self.cnxn_params['api_uri_full'] + if pid: + url += ("/" + str(pid)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + if query: + args['q'] = self.__format_query__(query) + + response = self.read(url, args, raw=raw) + return response + + def get_bibs(self, pid, q_params={}, raw=False): + """Get bibs in a collection using pid. + + Args: + pid (str): The collection ID. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + A a list of bibliographic titles in a given collection. + + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(pid)) + url += '/bibs' + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + +class SubClientBibsLoans(Client): + """Accesses loans endpoints""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + + def get_by_item(self, bib_id, holding_id, item_id, + loan_id=None, q_params={}, raw=False): + """Returns Loan by Item information. + + Args: + bib_id (str): The bib ID (mms_id). + holding_id (str): The Holding Record ID (holding_id). + item_id (str): The holding item ID (item_pid). + loan_id (str): The loan ID + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of loans or a single loan for a given item. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + url += ('/holdings/' + str(holding_id)) + url += ('/items/' + str(item_id) + "/loans") + if loan_id: + url += ('/' + str(loan_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + def get_by_title(self, bib_id, loan_id=None, q_params={}, raw=False): + """Returns Loan by title information. + + Args: + bib_id (str): The bib ID (mms_id). + loan_id (str): The loan ID + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of loans or a single loan for a given title. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id) + '/loans') + if loan_id: + url += ('/' + str(loan_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + +class SubClientBibsRequests(Client): + """Accesses user request endpoints""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + + def get_by_item(self, bib_id, holding_id, item_id, + request_id=None, q_params={}, raw=False): + """Returns Loan by Item information. + + Args: + bib_id (str): The bib ID (mms_id). + holding_id (str): The Holding Record ID (holding_id). + item_id (str): The holding item ID (item_pid). + request_id (str): The loan ID + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of loans or a single loan for a given item. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + url += ('/holdings/' + str(holding_id)) + url += ('/items/' + str(item_id) + "/requests") + if request_id: + url += ('/' + str(request_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + def get_by_title(self, bib_id, request_id=None, q_params={}, raw=False): + """Returns Loan by title information. + + Args: + bib_id (str): The bib ID (mms_id). + request_id (str): The loan ID + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of loans or a single loan for a given title. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id) + '/requests') + if request_id: + url += ('/' + str(request_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + def get_availability(self, bib_id, period, period_type='days', + holding_id=None, item_id=None, q_params={}, raw=False): + """Returns list of periods in which specific title or item + is unavailable for booking. + + To get a specific item, holding_id and item_id parameters are required. + + Note: user_id does not populate if retrieving by just bid_id. + + Args: + bib_id (str): The bib ID (mms_id). + period (str or int): The number of days/weeks/months to retrieve availability for. + period_type (str): The type of period of interest. Optional. Possible values: days, weeks, months. + holding_id (str): The Holding Record ID (holding_id). + item_id (str): The holding item ID (item_pid). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of periods title/item is unavailable for booking. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + if holding_id and item_id: + url += ("/holdings/" + str(holding_id)) + url += ('/items/' + str(item_id)) + elif holding_id or item_id: + message = "If getting availability for an item, " + message += "Both holding_id and item_id are required arguments." + raise utils.ArgError(message) + url += "/booking-availability" + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + args['period'] = str(period) + args['period_type'] = str(period_type) + + return self.read(url, args, raw=raw) + + def get_options(self, bib_id, user_id='GUEST', + holding_id=None, item_id=None, + q_params={}, raw=False): + """Returns request options for a specific title or item based on user. + + To get a specific item, holding_id and item_id parameters are required. + + Note: user_id does not populate if retrieving by just bid_id. + + Args: + bib_id (str): The bib ID (mms_id). + user_id (str): The id of the user for which the request options will be calculated. + holding_id (str): The Holding Record ID (holding_id). + item_id (str): The holding item ID (item_pid). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + Request options for a specific title or item based on user. + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + if holding_id and item_id: + url += ("/holdings/" + str(holding_id)) + url += ('/items/' + str(item_id)) + elif holding_id or item_id: + message = "If getting request options for an item, " + message += "Both holding_id and item_id are required arguments." + raise utils.ArgError(message) + url += "/request-options" + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + args['user_id'] = str(user_id) + + return self.read(url, args, raw=raw) + + +class SubClientBibsRepresentations(Client): + """Handles Digital Representations""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + + def get(self, bib_id, q_params={}, raw=False): + """Returns a list of Digital Representations for a given Bib MMS-ID. + + Args: + bib_id (str): The bib ID (mms_id). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + A list of Digital Representations for a given bib record. + + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + url += "/representations" + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + def get_details(self, bib_id, rep_id, files=False, q_params={}, raw=False): + """Returns a specific Digital Representation's details. + Supported for Remote and Non-Remote Representations. + + Args: + bib_id (str): The bib ID (mms_id). + rep_id (str): The Representation ID. + files (bool): Denote whether to return files? + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + A list of Digital Representations for a given bib record. + + """ + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(bib_id)) + url += "/representations/" + url += rep_id + if files: + url += "/files" + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) + + +class SubClientBibsLinkedData(Client): + """Handles Linked Data for a Bib Record""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + + def get(self, bib_id, q_params={}, raw=False): + """Returns Linked data for a given Bib MMS-ID. + + Args: + bib_id (str): The bib ID (mms_id). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + Linked data URIs for a given bib record. + + """ + url = self.cnxn_params['api_uri_full'] + url += "/linked-open-data" + url += ("/" + str(bib_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.read(url, args, raw=raw) diff --git a/almapipy/client.py b/almapipy/client.py index de6213c..431bd02 100644 --- a/almapipy/client.py +++ b/almapipy/client.py @@ -1,183 +1,386 @@ -""" -Common Client for interacting with Alma API -""" - -import json -import xml.etree.ElementTree as ET - -import requests - -from . import utils - - -class Client(object): - """ - Reads responses from Alma API and handles response. - """ - - def __init__(self, cnxn_params={}): - # instantiate dictionary for storing alma api connection parameters - self.cnxn_params = cnxn_params - - def read(self, url, args, raw=False): - """ - Uses requests library to makes Exlibris API call. - Parses XML into python. - - Args: - url (str): Exlibris API endpoint url. - args (dict): Query string parameters for API call. - raw (bool): If true, returns raw response. - - Returns: - JSON-esque, xml, or raw response. - """ - print(url) - - # handle data format. Allow for overriding of global setting. - data_format = self.cnxn_params['format'] - if 'format' not in args.keys(): - args['format'] = data_format - data_format = args['format'] - - # Send request. - response = requests.get(url, params=args) - if raw: - return response - - # Get meta data from headers. - status = response.status_code - try: - response_type = response.headers['Content-Type'] - response_type, charset = response_type.split(";") - except: - message = str(status) + " - Unknown Error" - raise utils.AlmaError(message, status, url) - - # decode response if xml. - if response_type == 'application/xml': - xml_ns = self.cnxn_params['xml_ns'] # xml namespace - content = ET.fromstring(response.text) - - # Received response from ex libris, but error retrieving data. - if str(status)[0] in ['4', '5']: - try: - first_error = content.find("header:errorList", xml_ns)[0] - message = first_error.find("header:errorCode", xml_ns).text - message += " - " - message += first_error.find("header:errorMessage", xml_ns).text - message += ". See Alma documentation for more information." - except: - message = str(status) + " - Unknown Error" - raise utils.AlmaError(message, status, url) - - # decode response if json. - elif response_type == 'application/json': - content = response.json() - - # Received response from ex libris, but error retrieving data. - if str(status)[0] in ['4', '5']: - try: - first_error = content['errorList']['error'][0] - message = first_error['errorCode'] - message += " - " - message += first_error['errorMessage'] - message += ". See Alma documentation for more information." - except: - message = str(status) + " - Unknown Error" - raise utils.AlmaError(message, status, url) - - else: - content = response - - if str(status)[0] in ['4', '5']: - message = str(status) + " - " - message += str(content.text) - raise utils.AlmaError(message, status, url) - - return content - - def __format_query__(self, query): - """Converts dictionary of brief search query to a formated string. - https://developers.exlibrisgroup.com/blog/How-we-re-building-APIs-at-Ex-Libris#BriefSearch - - Args: - query: dictionary of brief search query. - Format - {'field': 'value', 'field2', 'value2'}. - Returns: - String of query. - """ - q_str = "" - i = 0 - if type(query) != 'dict': - message = "Brief search query must be a dictionary." - for field, filter_value in query.items(): - field = str(field) - filter_value = str(filter_value) - if i > 0: - q_str += " AND " - q_str += (field + "~") - q_str += filter_value.replace(" ", "_") - i += 1 - - return q_str - - def __read_all__(self, url, args, raw, response, data_key, max_limit=100): - """Makes multiple API calls until all records for a query are retrieved. - Called by the 'all_records' parameter. - - Args: - url (str): Exlibris API endpoint url. - args (dict): Query string parameters for API call. - raw (bool): If true, returns raw response. - response (xml, raw, or json): First API call. - data_key (str): Dictionary key for accessing data. - max_limit (int): Max number of records allowed to be retrieved in a single call. - Overrides limit parameter. Reduces the number of API calls needed to retrieve data. - - Returns: - response with remainder of data appended. - """ - # raw will return a list of responses - if raw: - responses = [response] - response = response.json() - - args['offset'] = args['limit'] - limit = args['limit'] - - # get total record count of query - if type(response) == dict: - total_records = int(response['total_record_count']) - elif type(response) == ET.Element: - total_records = int(response.attrib['total_record_count']) - else: - total_records = limit - - # set new retrieval limit - records_retrieved = limit - args['limit'] = max_limit - limit = max_limit - - while True: - if total_records <= records_retrieved: - break - - # make call and increment counter variables - new_response = self.read(url, args, raw=raw) - records_retrieved += limit - args['offset'] += limit - - # append new records to initial response - if type(new_response) == dict: - response[data_key] += new_response[data_key] - elif type(new_response) == ET.Element: - for row in list(new_response): - response.append(row) - elif raw: - responses.append(new_response) - - if raw: - response = responses - - return response +# -*- coding: utf-8 -*- + +""" +Common Client for interacting with Alma API +""" + +import json +import xml.etree.ElementTree as ET + +import requests + +from . import utils + + +class Client(object): + """ + Reads responses from Alma API and handles response. + """ + + def __init__(self, cnxn_params={}): + # instantiate dictionary for storing alma api connection parameters + self.cnxn_params = cnxn_params + +# def post(self, url, data, args, object_type, raw=False): + def Post(self, url, data, args, headers, raw=False): + """ + Uses requests library to make Exlibris API Post call. + Returns data of type specified during init of base class. + + Args: + url (str): Exlibris API endpoint url. + data (dict): Data to be posted. + args (dict): Query string parameters for API call. + headers (dict): API Key Auth in Headers. +# object_type (str): Type of object to be posted (see alma docs) + raw (bool): If true, returns raw response. + + Returns: + JSON-esque, xml, or raw response. + """ + + data_aux = data.copy() + args_aux = args.copy() + + # Preserve Auth and add 'User-Agent' and 'content-type' in headers + headers_aux = headers.copy() + headers_aux['User-Agent'] = self.cnxn_params['User-Agent'] + + # Determine format of data to be posted according to order of importance: + # 1) Local declaration, 2) dtype of data parameter, 3) global setting. + if 'format' not in args_aux.keys(): + if type(data_aux) == ET or type(data_aux) == ET.Element: + content_type = 'xml' + elif type(data_aux) == dict: + content_type = 'json' + else: + content_type = self.cnxn_params['format'] + args_aux['format'] = self.cnxn_params['format'] + else: + content_type = args_aux['format'] + + # Determine 'content-type' and set 'data_aux' format in consecuence + if content_type == 'json': + headers_aux['content-type'] = 'application/json' + if type(data_aux) != str: + data_aux = json.dumps(data_aux) + elif content_type == 'xml': + headers_aux['content-type'] = 'application/xml' + if type(data_aux) == ET or type(data_aux) == ET.Element: + data_aux = ET.tostring(data_aux, encoding='unicode') + elif type(data_aux) != str: + message = "XML payload must be either string or ElementTree." + raise utils.ArgError(message) + else: + message = "Post content type must be either 'json' or 'xml'" + raise utils.ArgError(message) + + # Send request + response = requests.post(url, data=data_aux, params=args_aux, headers=headers_aux) + if raw: + return response + + # Parse content + content = self.__parse_response__(response) + + return content + + def Get(self, url, args, headers, raw=False): + """ + Uses requests library to make Exlibris API Get call. + Returns data of type specified during init of base class. + + Args: + url (str): Exlibris API endpoint url. + args (dict): Query string parameters for API call. + headers (dict): API Key Auth in Headers. + raw (bool): If true, returns raw response. + + Returns: + JSON-esque, xml, or raw response. + """ + + args_aux = args.copy() + headers_aux = headers.copy() + + # handle data format. Allow for overriding of global setting. + data_format = self.cnxn_params['format'] + if 'format' not in args_aux.keys(): + args_aux['format'] = data_format + + # Preserve Auth and add 'User-Agent' in headers + headers_aux['User-Agent'] = self.cnxn_params['User-Agent'] + + # Send request. + response = requests.get(url, params=args_aux, headers=headers_aux) + + if raw: + return response + + # Parse content + content = self.__parse_response__(response) + + return content + + def Put(self, url, data, headers, raw=False): + """ + Uses requests library to make Exlibris API Put call. + Returns data of type specified during init of base class. + + Args: + url (str): Exlibris API endpoint url. + data (dict): Data to be puted. + headers (dict): API Key Auth in Headers. +# object_type (str): Type of object to be puted (see alma docs) + raw (bool): If true, returns raw response. + + Returns: + JSON-esque, xml, or raw response. + """ + + data_aux = data.copy() + + # Preserve Auth and add 'User-Agent' in headers + headers_aux = headers.copy() + headers_aux['User-Agent'] = self.cnxn_params['User-Agent'] + + # Determine format of data to be puted according to order of importance: + # 1) Local declaration, 2) dtype of data parameter, 3) global setting. + if type(data_aux) == ET or type(data_aux) == ET.Element: + content_type = 'xml' + elif type(data_aux) == dict: + content_type = 'json' + else: + content_type = self.cnxn_params['format'] + + # Determine 'content-type' and set 'data_aux' format in consecuence + if content_type == 'json': + headers_aux['content-type'] = 'application/json' + if type(data_aux) != str: + data_aux = json.dumps(data_aux) + elif content_type == 'xml': + headers_aux['content-type'] = 'application/xml' + if type(data_aux) == ET or type(data_aux) == ET.Element: + data_aux = ET.tostring(data_aux, encoding='unicode') + elif type(data_aux) != str: + message = "XML payload must be either string or ElementTree." + raise utils.ArgError(message) + else: + message = "Put content type must be either 'json' or 'xml'" + raise utils.ArgError(message) + + # Send request + response = requests.put(url, data=data_aux, headers=headers_aux) + + if raw: + return response + + # Parse content + content = self.__parse_response__(response) + + return content + + def Delete(self, url, args, headers, raw=False): + """ + Uses requests library to make Exlibris API Delete call. + Returns data of type specified during init of base class. + + Args: + url (str): Exlibris API endpoint url. + args (dict): Query string parameters for API call. + headers (dict): API Key Auth in Headers. + raw (bool): If true, returns raw response. + + Returns: + JSON-esque, xml, or raw response. + """ + + args_aux = args.copy() + + # Preserve Auth and add 'User-Agent' in headers + headers_aux = headers.copy() + headers_aux['User-Agent'] = self.cnxn_params['User-Agent'] + + # Determine format of data to be posted + if 'format' not in args_aux.keys(): + args_aux['format'] = self.cnxn_params['format'] + + # Determine 'content-type' + if args_aux['format'] == 'json': + headers_aux['content-type'] = 'application/json' + elif args_aux['format'] == 'xml': + headers_aux['content-type'] = 'application/xml' + else: + message = "Post content type must be either 'json' or 'xml'" + raise utils.ArgError(message) + + # Send request + response = requests.delete(url, params=args_aux, headers=headers_aux) + + if raw: + return response + +#TODO: Eval exception 204 + """ + # Parse content + content = self.__parse_response__(response) + + return content + """ + return response + + def __format_query__(self, query): + """Converts dictionary of brief search query to a formated string. + https://developers.exlibrisgroup.com/blog/How-we-re-building-APIs-at-Ex-Libris#BriefSearch + + Args: + query: dictionary of brief search query. + Format - {'field': 'value', 'field2', 'value2'}. + Returns: + String of query. + """ + q_str = "" + i = 0 + if type(query) != 'dict': + message = "Brief search query must be a dictionary." + for field, filter_value in query.items(): + field = str(field) + filter_value = str(filter_value) + if i > 0: + q_str += " AND " + q_str += (field + "~") + q_str += filter_value.replace(" ", "_") + i += 1 + + return q_str + + def __Get_all__(self, url, args, headers, raw, response, data_key, max_limit=100): + """Makes multiple API calls until all records for a query are retrieved. + Called by the 'all_records' parameter. + + Args: + url (str): Exlibris API endpoint url. + args (dict): Query string parameters for API call. + headers (dict): API Key Auth in Headers. + raw (bool): If true, returns raw response. + response (xml, raw, or json): First API call. + data_key (str): Dictionary key for accessing data. + max_limit (int): Max number of records allowed to be retrieved in a single call. + Overrides limit parameter. Reduces the number of API calls needed to retrieve data. + + Returns: + response with remainder of data appended. + """ + # raw will return a list of responses + if raw: + responses = [response] + response = response.json() + + args['offset'] = args['limit'] + limit = args['limit'] + + # get total record count of query + if type(response) == dict: + total_records = int(response['total_record_count']) + elif type(response) == ET.Element: + total_records = int(response.attrib['total_record_count']) + else: + total_records = limit + + # set new retrieval limit + records_retrieved = limit + args['limit'] = max_limit + limit = max_limit + + # Preserve Auth and add 'User-Agent' in headers + headers_aux = headers.copy() + headers_aux['User-Agent'] = self.cnxn_params['User-Agent'] + + while True: + if total_records <= records_retrieved: + break + + # make call and increment counter variables + new_response = self.get(url, args=args, headers=headers_aux, raw=raw) + records_retrieved += limit + args['offset'] += limit + + # append new records to initial response + if type(new_response) == dict: + response[data_key] += new_response[data_key] + elif type(new_response) == ET.Element: + for row in list(new_response): + response.append(row) + elif raw: + responses.append(new_response) + + if raw: + response = responses + + return response + + def __parse_response__(self, response): + """Parses alma response depending on content type. + + Args: + response: requests object from Alma. + + Returns: + Content of response in format specified in header. + """ + status = response.status_code + url = response.url + try: + response_type = response.headers['content-type'] + if ";" in response_type: + response_type, charset = response_type.split(";") + except: + message = 'Error ' + str(status) + response.text + raise utils.AlmaError(message, status, url) + + # decode response if xml. + if response_type == 'application/xml': + xml_ns = self.cnxn_params['xml_ns'] # xml namespace + content = ET.fromstring(response.text) + + # Received response from ex libris, but error retrieving data. + if str(status)[0] in ['4', '5']: + try: + first_error = content.find("header:errorList", xml_ns)[0] + message = first_error.find("header:errorCode", xml_ns).text + message += " - " + message += first_error.find("header:errorMessage", xml_ns).text + message += " See Alma documentation for more information." + except: + message = 'Error ' + str(status) + " - " + str(content) + raise utils.AlmaError(message, status, url) + + # decode response if json. + elif response_type == 'application/json': + content = response.json() + + # Received response from ex libris, but error retrieving data. + if str(status)[0] in ['4', '5']: + try: + if 'web_service_result' in content.keys(): + first_error = content['web_service_result']['errorList']['error'][0] + else: + first_error = content['errorList']['error'][0] + message = first_error['errorCode'] + message += " - " + message += first_error['errorMessage'] + if 'trackingID' in first_error.keys(): + message += "TrackingID: " + message['trackingID'] + message += " See Alma documentation for more information." + except: + message = 'Error ' + str(status) + " - " + str(content) + raise utils.AlmaError(message, status, url) + + else: + content = response + + if str(status)[0] in ['4', '5']: + message = str(status) + " - " + message += str(content.text) + raise utils.AlmaError(message, status, url) + return content diff --git a/almapipy/conf.py b/almapipy/conf.py index 001d375..70575bd 100644 --- a/almapipy/conf.py +++ b/almapipy/conf.py @@ -1,567 +1,569 @@ -from .client import Client -from . import utils - - -class SubClientConfiguration(Client): - """ - Alma provides a set of Web services for handling Configuration related - information, enabling you to quickly and easily receive configuration details. - These Web services can be used by external systems in order to get list of - possible data. - For more info: https://developers.exlibrisgroup.com/alma/apis/conf - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to API. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/conf" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/conf" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/37088dc9-c685-4641-bc7f-60b5ca7cabed.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - - # Hook in subclients of api - self.units = SubClientConfigurationUnits(self.cnxn_params) - self.general = SubClientConfigurationGeneral(self.cnxn_params) - self.jobs = SubClientConfigurationJobs(self.cnxn_params) - self.sets = SubClientConfigurationSets(self.cnxn_params) - self.deposit_profiles = SubClientConfigurationDeposit(self.cnxn_params) - self.import_profiles = SubClientConfigurationImport(self.cnxn_params) - self.reminders = SubClientConfigurationReminders(self.cnxn_params) - - -class SubClientConfigurationUnits(Client): - """Handles the Organization Unit endpoints of Configurations API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - - def get_libaries(self, library_id=None, q_params={}, raw=False): - """Retrieve a list of libraries or a specific library - - Args: - library_id (str): The code of the library (libraryCode). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of libraries or single library - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += '/libraries' - if library_id: - url += ("/" + str(library_id)) - - response = self.read(url, args, raw=raw) - return response - - def get_locations(self, library_id, location_id=None, q_params={}, raw=False): - """Retrieve a list of locations for a library - - Args: - library_id (str): The code of the library (libraryCode). - location_id (str): Code for a specific location (locationCode). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of library locations. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += ('/libraries/' + str(library_id) + "/locations") - if location_id: - url += ("/" + str(location_id)) - - response = self.read(url, args, raw=raw) - return response - - def get_departments(self, q_params={}, raw=False): - """Retrieve a list of configured departments - - Args: - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of departments. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += '/departments' - - response = self.read(url, args, raw=raw) - return response - - -class SubClientConfigurationGeneral(Client): - """Handles the General endpoints of Configurations API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - - def get(self, library_id=None, q_params={}, raw=False): - """Retrieve general configuration of the institution - - Args: - library_id (str): The code of the library (libraryCode). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - General configuration of the institution - """ - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += "/general" - - response = self.read(url, args, raw=raw) - return response - - def get_hours(self, library_id=None, q_params={}, raw=False): - """Retrieve open hours as configured in Alma. - Note that the library-hours do not necessarily reflect when the - library doors are actually open, but rather start and end times that - effect loan period. - This API is limited to one month of days from 1 year ago to - 3 years ahead for a single request. - - Args: - library_id (str): The code of the library (libraryCode). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of open hours - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - - if library_id: - url += '/libraries' - url += ("/" + str(library_id)) - - url += '/open-hours' - - response = self.read(url, args, raw=raw) - return response - - def get_code_table(self, table_name, q_params={}, raw=False): - """This API returns all rows defined for a code-table. - - The main usage of this API is for applications that use Alma APIs, - and need to give the user a drop-down of valid values to choose from. - - See Alma documentation for code-table names. - - Args: - table_name (str): Code table name. (codeTableName) - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - Code-table rows - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += ('/code-tables/' + str(table_name)) - - response = self.read(url, args, raw=raw) - return response - - -class SubClientConfigurationJobs(Client): - """Handles the Jobs endpoints of Configurations API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/jobs' - self.cnxn_params['api_uri_full'] += '/jobs' - - def get(self, job_id=None, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieve a list of jobs that can be submitted or details for a given job. - - Args: - job_id (str): Unique id of the job. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of jobs or a specific job - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - - if job_id: - url += ("/" + str(job_id)) - else: - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - if job_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='job') - return response - - def get_instances(self, job_id, instance_id=None, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve all the job instances (runs) for a given job id, or specific instance. - - Args: - job_id (str): Unique id of the job. - instance_id (str): Unique id of the specific job instance. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of jobs or a specific job - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(job_id) + "/instances") - - if instance_id: - url += ("/" + str(instance_id)) - else: - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - if instance_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='job_instance') - return response - - -class SubClientConfigurationSets(Client): - """Handles the Sets endpoints of Configurations API - A set is a collection of items, such as users or the results of a repository search. - Sets may be used for publishing metadata in bulk, moving a group of records, or to run jobs. - """ - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/sets' - self.cnxn_params['api_uri_full'] += '/sets' - - def get(self, set_id=None, content_type=None, set_type=None, - query={}, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieve a list of sets or a single set. - - Args: - set_id (str): A unique identifier of the set. - content_type (str): Content type for filtering. - Valid values are from the SetContentType code table. - set_type (str): Set type for filtering. - Valid values are 'ITEMIZED' or 'LOGICAL'. - query (dict): Search query. Searching for words in created_by or name - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of sets or a specific set. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if set_id: - url += ("/" + str(set_id)) - else: - # include paramets specific to user list - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - if content_type: - args['content_type'] = str(content_type) - if set_type: - args['set_type'] = str(set_type) - - # add search query if specified in desired format - if query: - args['q'] = self.__format_query__(query) - response = self.read(url, args, raw=raw) - if set_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='set') - return response - - def get_members(self, set_id, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieves members of a Set given a Set ID. - - Args: - set_id (str): A unique identifier of the set. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of members for a specific set - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(set_id) + "/members") - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='member') - return response - - -class SubClientConfigurationDeposit(Client): - """Handles the Deposit profiles endpoints of Configurations API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/deposit-profiles' - self.cnxn_params['api_uri_full'] += '/deposit-profiles' - - def get(self, deposit_profile_id=None, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieves list of deposit profiles or specific profile - - Args: - deposit_profile_id (str): A unique identifier of the deposit profile. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of deposit profiles or specific profile. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if deposit_profile_id: - url += ("/" + str(deposit_profile_id)) - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - if deposit_profile_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='deposit_profile') - - -class SubClientConfigurationImport(Client): - """Handles the Import profiles endpoints of Configurations API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/md-import-profiles' - self.cnxn_params['api_uri_full'] += '/md-import-profiles' - - def get(self, profile_id=None, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieves list of import profiles or specific profile - - Args: - profile_id (str): A unique identifier of the import profile. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of import profiles or specific profile. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if profile_id: - url += ("/" + str(profile_id)) - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - if profile_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='import_profile') - return response - - -class SubClientConfigurationReminders(Client): - """Handles the Reminder endpoints of Configurations API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/reminders' - self.cnxn_params['api_uri_full'] += '/reminders' - - def get(self, reminder_id=None, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieves list of reminders or specific reminder. - - Args: - reminder_id (str): A unique identifier of the reminder. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of reminders or specific reminder. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if reminder_id: - url += ("/" + str(reminder_id)) - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - if reminder_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='reminder') - return response +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils + + +class SubClientConfiguration(Client): + """ + Alma provides a set of Web services for handling Configuration related + information, enabling you to quickly and easily receive configuration details. + These Web services can be used by external systems in order to get list of + possible data. + For more info: https://developers.exlibrisgroup.com/alma/apis/conf + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to API. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/conf" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/conf" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/37088dc9-c685-4641-bc7f-60b5ca7cabed.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + + # Hook in subclients of api + self.units = SubClientConfigurationUnits(self.cnxn_params) + self.general = SubClientConfigurationGeneral(self.cnxn_params) + self.jobs = SubClientConfigurationJobs(self.cnxn_params) + self.sets = SubClientConfigurationSets(self.cnxn_params) + self.deposit_profiles = SubClientConfigurationDeposit(self.cnxn_params) + self.import_profiles = SubClientConfigurationImport(self.cnxn_params) + self.reminders = SubClientConfigurationReminders(self.cnxn_params) + + +class SubClientConfigurationUnits(Client): + """Handles the Organization Unit endpoints of Configurations API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + + def read_libaries(self, library_id=None, q_params={}, raw=False): + """Retrieve a list of libraries or a specific library + + Args: + library_id (str): The code of the library (libraryCode). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of libraries or single library + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += '/libraries' + if library_id: + url += ("/" + str(library_id)) + + response = self.get(url, args, raw=raw) + return response + + def read_locations(self, library_id, location_id=None, q_params={}, raw=False): + """Retrieve a list of locations for a library + + Args: + library_id (str): The code of the library (libraryCode). + location_id (str): Code for a specific location (locationCode). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of library locations. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += ('/libraries/' + str(library_id) + "/locations") + if location_id: + url += ("/" + str(location_id)) + + response = self.get(url, args, raw=raw) + return response + + def read_departments(self, q_params={}, raw=False): + """Retrieve a list of configured departments + + Args: + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of departments. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += '/departments' + + response = self.get(url, args, raw=raw) + return response + + +class SubClientConfigurationGeneral(Client): + """Handles the General endpoints of Configurations API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + + def read(self, library_id=None, q_params={}, raw=False): + """Retrieve general configuration of the institution + + Args: + library_id (str): The code of the library (libraryCode). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + General configuration of the institution + """ + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += "/general" + + response = self.get(url, args, raw=raw) + return response + + def read_hours(self, library_id=None, q_params={}, raw=False): + """Retrieve open hours as configured in Alma. + Note that the library-hours do not necessarily reflect when the + library doors are actually open, but rather start and end times that + effect loan period. + This API is limited to one month of days from 1 year ago to + 3 years ahead for a single request. + + Args: + library_id (str): The code of the library (libraryCode). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of open hours + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + + if library_id: + url += '/libraries' + url += ("/" + str(library_id)) + + url += '/open-hours' + + response = self.get(url, args, raw=raw) + return response + + def read_code_table(self, table_name, q_params={}, raw=False): + """This API returns all rows defined for a code-table. + + The main usage of this API is for applications that use Alma APIs, + and need to give the user a drop-down of valid values to choose from. + + See Alma documentation for code-table names. + + Args: + table_name (str): Code table name. (codeTableName) + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + Code-table rows + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += ('/code-tables/' + str(table_name)) + + response = self.get(url, args, raw=raw) + return response + + +class SubClientConfigurationJobs(Client): + """Handles the Jobs endpoints of Configurations API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/jobs' + self.cnxn_params['api_uri_full'] += '/jobs' + + def read(self, job_id=None, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieve a list of jobs that can be submitted or details for a given job. + + Args: + job_id (str): Unique id of the job. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of jobs or a specific job + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + + if job_id: + url += ("/" + str(job_id)) + else: + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.get(url, args, raw=raw) + + if job_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__get_all__(url=url, args=args, raw=raw, + response=response, data_key='job') + return response + + def read_instances(self, job_id, instance_id=None, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve all the job instances (runs) for a given job id, or specific instance. + + Args: + job_id (str): Unique id of the job. + instance_id (str): Unique id of the specific job instance. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of jobs or a specific job + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(job_id) + "/instances") + + if instance_id: + url += ("/" + str(instance_id)) + else: + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.get(url, args, raw=raw) + + if instance_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__get_all__(url=url, args=args, raw=raw, + response=response, data_key='job_instance') + return response + + +class SubClientConfigurationSets(Client): + """Handles the Sets endpoints of Configurations API + A set is a collection of items, such as users or the results of a repository search. + Sets may be used for publishing metadata in bulk, moving a group of records, or to run jobs. + """ + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/sets' + self.cnxn_params['api_uri_full'] += '/sets' + + def read(self, set_id=None, content_type=None, set_type=None, + query={}, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieve a list of sets or a single set. + + Args: + set_id (str): A unique identifier of the set. + content_type (str): Content type for filtering. + Valid values are from the SetContentType code table. + set_type (str): Set type for filtering. + Valid values are 'ITEMIZED' or 'LOGICAL'. + query (dict): Search query. Searching for words in created_by or name + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of sets or a specific set. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if set_id: + url += ("/" + str(set_id)) + else: + # include paramets specific to user list + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + if content_type: + args['content_type'] = str(content_type) + if set_type: + args['set_type'] = str(set_type) + + # add search query if specified in desired format + if query: + args['q'] = self.__format_query__(query) + response = self.get(url, args, raw=raw) + if set_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__get_all__(url=url, args=args, raw=raw, + response=response, data_key='set') + return response + + def read_members(self, set_id, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieves members of a Set given a Set ID. + + Args: + set_id (str): A unique identifier of the set. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of members for a specific set + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(set_id) + "/members") + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.get(url, args, raw=raw) + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__get_all__(url=url, args=args, raw=raw, + response=response, data_key='member') + return response + + +class SubClientConfigurationDeposit(Client): + """Handles the Deposit profiles endpoints of Configurations API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/deposit-profiles' + self.cnxn_params['api_uri_full'] += '/deposit-profiles' + + def read(self, deposit_profile_id=None, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieves list of deposit profiles or specific profile + + Args: + deposit_profile_id (str): A unique identifier of the deposit profile. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of deposit profiles or specific profile. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if deposit_profile_id: + url += ("/" + str(deposit_profile_id)) + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.get(url, args, raw=raw) + + if deposit_profile_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__get_all__(url=url, args=args, raw=raw, + response=response, data_key='deposit_profile') + + +class SubClientConfigurationImport(Client): + """Handles the Import profiles endpoints of Configurations API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/md-import-profiles' + self.cnxn_params['api_uri_full'] += '/md-import-profiles' + + def read(self, profile_id=None, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieves list of import profiles or specific profile + + Args: + profile_id (str): A unique identifier of the import profile. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of import profiles or specific profile. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if profile_id: + url += ("/" + str(profile_id)) + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.get(url, args, raw=raw) + + if profile_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__get_all__(url=url, args=args, raw=raw, + response=response, data_key='import_profile') + return response + + +class SubClientConfigurationReminders(Client): + """Handles the Reminder endpoints of Configurations API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/reminders' + self.cnxn_params['api_uri_full'] += '/reminders' + + def read(self, reminder_id=None, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieves list of reminders or specific reminder. + + Args: + reminder_id (str): A unique identifier of the reminder. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of reminders or specific reminder. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if reminder_id: + url += ("/" + str(reminder_id)) + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.get(url, args, raw=raw) + + if reminder_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__get_all__(url=url, args=args, raw=raw, + response=response, data_key='reminder') + return response diff --git a/almapipy/courses.py b/almapipy/courses.py index 43420ec..3b48719 100644 --- a/almapipy/courses.py +++ b/almapipy/courses.py @@ -1,231 +1,233 @@ -from .client import Client -from . import utils - - -class SubClientCourses(Client): - """ - The Courses API allows access to courses and reading lists related information. - These Web services can be used by external systems such as Courses Management - Systems to retrieve or update courses and reading lists related data. - For more info: https://developers.exlibrisgroup.com/alma/apis/courses - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to API. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/courses" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/courses" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/25ede018-da5d-4780-8fda-a8e5d103faba.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - #self.cnxn_params['xml_ns']['report'] = 'urn:schemas-microsoft-com:xml-analysis:rowset' - - # Hook in subclients of api - self.reading_lists = SubClientCoursesReadingLists(self.cnxn_params) - self.citations = SubClientCoursesCitations(self.cnxn_params) - self.owners = SubClientCoursesOwners(self.cnxn_params) - self.tags = SubClientCoursesTags(self.cnxn_params) - - def get(self, course_id=None, query={}, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a courses list or a single course. - - Args: - course_id (str): The identifier of a single course. - Gets more detailed information. - query (dict): Search query for filtering a course list. Optional. - Searching for words from fields: [code, section, name, notes, - instructors, searchable_ids, year, academic_department, - all]. Only AND operator is supported for multiple filters. - Format {'field': 'value', 'field2', 'value2'}. - e.g. query = {'code': 'ECN'} returns a list of econ classes - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of courses or a specific course resource. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if course_id: - url += ("/" + str(course_id)) - else: - # include paramets specific to course list - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - # add search query if specified in desired format - if query: - args['q'] = self.__format_query__(query) - - response = self.read(url, args, raw=raw) - if course_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='course') - return response - - -class SubClientCoursesReadingLists(Client): - """Handles the reading list endpoints of Courses API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/' - self.cnxn_params['api_uri_full'] += '/' - - def get(self, course_id, reading_list_id=None, view='brief', q_params={}, raw=False): - """Retrieves all Reading Lists, or a specific list, for a Course. - - Args: - course_id (str): The identifier of the Course. - reading_list_id (str): The identifier of the Reading List. - view (str): 'brief' or 'full' view of reading list. - Only applies when retrieving a single record. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of reading lists or single list - for a given course ID. - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += str(course_id) - url += '/reading-lists' - if reading_list_id: - url += ('/' + str(reading_list_id)) - if view not in ['brief', 'full']: - message = "Valid view arguments are 'brief' or 'full'" - raise utils.ArgError(message) - args['view'] = view - - return self.read(url, args, raw=raw) - - -class SubClientCoursesCitations(Client): - """Handles the citations endpoints of Courses API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/' - self.cnxn_params['api_uri_full'] += '/' - - def get(self, course_id, reading_list_id, citation_id=None, q_params={}, raw=False): - """Retrieves all citations, or a specific citation, for a reading list. - - Args: - course_id (str): The identifier of the Course. - reading_list_id (str): The identifier of the Reading List. - citation_id (str): The identifier of the citation. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of citations or single citation - for a given reading list ID. - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += str(course_id) - url += '/reading-lists' - url += ('/' + str(reading_list_id)) - url += '/citations' - - if citation_id: - url += ('/' + str(citation_id)) - - return self.read(url, args, raw=raw) - - -class SubClientCoursesOwners(Client): - """Handles the owners endpoints of Courses API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/' - self.cnxn_params['api_uri_full'] += '/' - - def get(self, course_id, reading_list_id, owner_id=None, q_params={}, raw=False): - """Retrieves all owners, or a specific owner, for a reading list. - - Args: - course_id (str): The identifier of the Course. - reading_list_id (str): The identifier of the Reading List. - owner_id (str): The primary identifier of the user (primary_id). - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of owner or single owner - for a given reading list ID. - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += str(course_id) - url += '/reading-lists' - url += ('/' + str(reading_list_id)) - url += '/owners' - - if owner_id: - url += ('/' + str(owner_id)) - - return self.read(url, args, raw=raw) - - -class SubClientCoursesTags(Client): - """Handles the citation tags endpoints of Courses API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/' - self.cnxn_params['api_uri_full'] += '/' - - def get(self, course_id, reading_list_id, citation_id, q_params={}, raw=False): - """Retrieves a citation's tag list. - - Args: - course_id (str): The identifier of the Course. - reading_list_id (str): The identifier of the Reading List. - citation_id (str): The identifier of the citation. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of a citation's tags in for a given reading list ID. - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += str(course_id) - url += '/reading-lists' - url += ('/' + str(reading_list_id)) - url += '/citations' - url += ('/' + str(citation_id) + "/tags") - - return self.read(url, args, raw=raw) +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils + + +class SubClientCourses(Client): + """ + The Courses API allows access to courses and reading lists related information. + These Web services can be used by external systems such as Courses Management + Systems to retrieve or update courses and reading lists related data. + For more info: https://developers.exlibrisgroup.com/alma/apis/courses + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to API. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/courses" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/courses" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/25ede018-da5d-4780-8fda-a8e5d103faba.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + #self.cnxn_params['xml_ns']['report'] = 'urn:schemas-microsoft-com:xml-analysis:rowset' + + # Hook in subclients of api + self.reading_lists = SubClientCoursesReadingLists(self.cnxn_params) + self.citations = SubClientCoursesCitations(self.cnxn_params) + self.owners = SubClientCoursesOwners(self.cnxn_params) + self.tags = SubClientCoursesTags(self.cnxn_params) + + def get(self, course_id=None, query={}, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve a courses list or a single course. + + Args: + course_id (str): The identifier of a single course. + Gets more detailed information. + query (dict): Search query for filtering a course list. Optional. + Searching for words from fields: [code, section, name, notes, + instructors, searchable_ids, year, academic_department, + all]. Only AND operator is supported for multiple filters. + Format {'field': 'value', 'field2', 'value2'}. + e.g. query = {'code': 'ECN'} returns a list of econ classes + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of courses or a specific course resource. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if course_id: + url += ("/" + str(course_id)) + else: + # include paramets specific to course list + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + # add search query if specified in desired format + if query: + args['q'] = self.__format_query__(query) + + response = self.read(url, args, raw=raw) + if course_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='course') + return response + + +class SubClientCoursesReadingLists(Client): + """Handles the reading list endpoints of Courses API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/' + self.cnxn_params['api_uri_full'] += '/' + + def get(self, course_id, reading_list_id=None, view='brief', q_params={}, raw=False): + """Retrieves all Reading Lists, or a specific list, for a Course. + + Args: + course_id (str): The identifier of the Course. + reading_list_id (str): The identifier of the Reading List. + view (str): 'brief' or 'full' view of reading list. + Only applies when retrieving a single record. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of reading lists or single list + for a given course ID. + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += str(course_id) + url += '/reading-lists' + if reading_list_id: + url += ('/' + str(reading_list_id)) + if view not in ['brief', 'full']: + message = "Valid view arguments are 'brief' or 'full'" + raise utils.ArgError(message) + args['view'] = view + + return self.read(url, args, raw=raw) + + +class SubClientCoursesCitations(Client): + """Handles the citations endpoints of Courses API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/' + self.cnxn_params['api_uri_full'] += '/' + + def get(self, course_id, reading_list_id, citation_id=None, q_params={}, raw=False): + """Retrieves all citations, or a specific citation, for a reading list. + + Args: + course_id (str): The identifier of the Course. + reading_list_id (str): The identifier of the Reading List. + citation_id (str): The identifier of the citation. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of citations or single citation + for a given reading list ID. + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += str(course_id) + url += '/reading-lists' + url += ('/' + str(reading_list_id)) + url += '/citations' + + if citation_id: + url += ('/' + str(citation_id)) + + return self.read(url, args, raw=raw) + + +class SubClientCoursesOwners(Client): + """Handles the owners endpoints of Courses API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/' + self.cnxn_params['api_uri_full'] += '/' + + def get(self, course_id, reading_list_id, owner_id=None, q_params={}, raw=False): + """Retrieves all owners, or a specific owner, for a reading list. + + Args: + course_id (str): The identifier of the Course. + reading_list_id (str): The identifier of the Reading List. + owner_id (str): The primary identifier of the user (primary_id). + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of owner or single owner + for a given reading list ID. + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += str(course_id) + url += '/reading-lists' + url += ('/' + str(reading_list_id)) + url += '/owners' + + if owner_id: + url += ('/' + str(owner_id)) + + return self.read(url, args, raw=raw) + + +class SubClientCoursesTags(Client): + """Handles the citation tags endpoints of Courses API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/' + self.cnxn_params['api_uri_full'] += '/' + + def get(self, course_id, reading_list_id, citation_id, q_params={}, raw=False): + """Retrieves a citation's tag list. + + Args: + course_id (str): The identifier of the Course. + reading_list_id (str): The identifier of the Reading List. + citation_id (str): The identifier of the citation. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of a citation's tags in for a given reading list ID. + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += str(course_id) + url += '/reading-lists' + url += ('/' + str(reading_list_id)) + url += '/citations' + url += ('/' + str(citation_id) + "/tags") + + return self.read(url, args, raw=raw) diff --git a/almapipy/electronic.py b/almapipy/electronic.py index ea68d53..85ab8b7 100644 --- a/almapipy/electronic.py +++ b/almapipy/electronic.py @@ -1,157 +1,159 @@ -from .client import Client -from . import utils - - -class SubClientElectronic(Client): - """ - Alma provides a set of Web services for handling electronic information, - enabling you to quickly and easily manipulate electronic details. - These Web services can be used by external systems in order to retrieve or - update electronic data. - For more info: https://developers.exlibrisgroup.com/alma/apis/electronic - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to API. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/electronic" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/electronic" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/e7cf39e9-adce-4be1-aeb9-a31f452960da.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - - # Hook in subclients of api - self.collections = SubClientElectronicCollections(self.cnxn_params) - self.services = SubClientElectronicServices(self.cnxn_params) - self.portfolios = SubClientElectronicPortfolios(self.cnxn_params) - - -class SubClientElectronicCollections(Client): - """Handles the e-collections endpoints of Electronic API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/e-collections' - self.cnxn_params['api_uri_full'] += '/e-collections' - - def get(self, collection_id=None, query={}, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a list of electronic collections. - - Args: - collection_id (str): Unique ID of the electronic collection. - query (dict): Search query for filtering a course list. Optional. - Searching for words from fields: [interface_name, keywords, - name, po_line_id]. Only AND operator is supported for multiple filters. - Format {'field': 'value', 'field2', 'value2'}. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of electronic collections or specific collection. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if collection_id: - url += ("/" + str(collection_id)) - else: - # include paramets specific to course list - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - # add search query if specified in desired format - if query: - args['q'] = self.__format_query__(query) - - response = self.read(url, args, raw=raw) - if collection_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='electronic_collection') - return response - - -class SubClientElectronicServices(Client): - """Handles the e-services endpoints of Electronic API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/e-collections/' - self.cnxn_params['api_uri_full'] += '/e-collections/' - - def get(self, collection_id, service_id=None, q_params={}, raw=False): - """Returns a list of electronic services for a given electronic collection. - - Args: - collection_id (str): Unique ID of the electronic collection. - service_id (str): Unique ID of the electronic service. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of electronic services for a given electronic collection - or a specific e-service. - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += str(collection_id) - url += '/e-services' - if service_id: - url += ('/' + str(service_id)) - - return self.read(url, args, raw=raw) - - -class SubClientElectronicPortfolios(Client): - """Handles the e-services endpoints of Electronic API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/e-collections/' - self.cnxn_params['api_uri_full'] += '/e-collections/' - - def get(self, collection_id, service_id, portfolio_id=None, q_params={}, raw=False): - """Returns a list of portfolios for an electronic services for a given electronic collection. - - Args: - collection_id (str): Unique ID of the electronic collection. - service_id (str): Unique ID of the electronic service. - portfolio_id (str): Unique ID of the portfolio. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of portfolios for an electronic services for a given electronic collection - or a specific portfolio. - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += str(collection_id) - url += '/e-services' - url += ('/' + str(service_id)) - url += "/portfolios" - if portfolio_id: - url += ('/' + str(portfolio_id)) - return self.read(url, args, raw=raw) +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils + + +class SubClientElectronic(Client): + """ + Alma provides a set of Web services for handling electronic information, + enabling you to quickly and easily manipulate electronic details. + These Web services can be used by external systems in order to retrieve or + update electronic data. + For more info: https://developers.exlibrisgroup.com/alma/apis/electronic + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to API. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/electronic" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/electronic" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/e7cf39e9-adce-4be1-aeb9-a31f452960da.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + + # Hook in subclients of api + self.collections = SubClientElectronicCollections(self.cnxn_params) + self.services = SubClientElectronicServices(self.cnxn_params) + self.portfolios = SubClientElectronicPortfolios(self.cnxn_params) + + +class SubClientElectronicCollections(Client): + """Handles the e-collections endpoints of Electronic API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/e-collections' + self.cnxn_params['api_uri_full'] += '/e-collections' + + def get(self, collection_id=None, query={}, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve a list of electronic collections. + + Args: + collection_id (str): Unique ID of the electronic collection. + query (dict): Search query for filtering a course list. Optional. + Searching for words from fields: [interface_name, keywords, + name, po_line_id]. Only AND operator is supported for multiple filters. + Format {'field': 'value', 'field2', 'value2'}. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of electronic collections or specific collection. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if collection_id: + url += ("/" + str(collection_id)) + else: + # include paramets specific to course list + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + # add search query if specified in desired format + if query: + args['q'] = self.__format_query__(query) + + response = self.read(url, args, raw=raw) + if collection_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='electronic_collection') + return response + + +class SubClientElectronicServices(Client): + """Handles the e-services endpoints of Electronic API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/e-collections/' + self.cnxn_params['api_uri_full'] += '/e-collections/' + + def get(self, collection_id, service_id=None, q_params={}, raw=False): + """Returns a list of electronic services for a given electronic collection. + + Args: + collection_id (str): Unique ID of the electronic collection. + service_id (str): Unique ID of the electronic service. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of electronic services for a given electronic collection + or a specific e-service. + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += str(collection_id) + url += '/e-services' + if service_id: + url += ('/' + str(service_id)) + + return self.read(url, args, raw=raw) + + +class SubClientElectronicPortfolios(Client): + """Handles the e-services endpoints of Electronic API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/e-collections/' + self.cnxn_params['api_uri_full'] += '/e-collections/' + + def get(self, collection_id, service_id, portfolio_id=None, q_params={}, raw=False): + """Returns a list of portfolios for an electronic services for a given electronic collection. + + Args: + collection_id (str): Unique ID of the electronic collection. + service_id (str): Unique ID of the electronic service. + portfolio_id (str): Unique ID of the portfolio. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of portfolios for an electronic services for a given electronic collection + or a specific portfolio. + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += str(collection_id) + url += '/e-services' + url += ('/' + str(service_id)) + url += "/portfolios" + if portfolio_id: + url += ('/' + str(portfolio_id)) + return self.read(url, args, raw=raw) diff --git a/almapipy/partners.py b/almapipy/partners.py index b0491e2..8223df3 100644 --- a/almapipy/partners.py +++ b/almapipy/partners.py @@ -1,100 +1,102 @@ -from .client import Client -from . import utils - - -class SubClientPartners(Client): - """ - Alma provides a set of Web services for handling Resource Sharing Partner - information, enabling you to quickly and easily manipulate partner details. - These Web services can be used by external systems to retrieve or update - partner data. - For more info: https://developers.exlibrisgroup.com/alma/apis/partners - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to API. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/partners" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/partners" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/8883ef41-c3b8-4792-9ff8-cb6b729d6e07.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - - # Hook in subclients of api - self.lending_requests = SubClientPartnersLending(self.cnxn_params) - - def get(self, partner_id=None, limit=10, offset=0, all_records=False, - q_params={}, raw=False): - """Retrieves a list of Resource Sharing Partners or specific partner. - - Args: - partner_id (str): The code of the Resource Sharing Partner (partner_code). - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of partners or specific partner - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if partner_id: - url += ("/" + str(partner_id)) - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - - if partner_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='partner') - return response - - -class SubClientPartnersLending(Client): - """Handles the Lending Request endpoints of Resource Sharing Partners API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - - def get(self, partner_id, request_id, q_params={}, raw=False): - """Retrieve a lending request from a specific partner. - - Args: - partner_id (str): The code of the Resource Sharing Partner (partner_code). - request_id (str): The ID of the requested lending request. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - Lending request from a specific partner. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += ("/" + str(partner_id) + "/lending-requests") - url += ("/" + str(request_id)) - - response = self.read(url, args, raw=raw) - return response +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils + + +class SubClientPartners(Client): + """ + Alma provides a set of Web services for handling Resource Sharing Partner + information, enabling you to quickly and easily manipulate partner details. + These Web services can be used by external systems to retrieve or update + partner data. + For more info: https://developers.exlibrisgroup.com/alma/apis/partners + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to API. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/partners" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/partners" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/8883ef41-c3b8-4792-9ff8-cb6b729d6e07.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + + # Hook in subclients of api + self.lending_requests = SubClientPartnersLending(self.cnxn_params) + + def get(self, partner_id=None, limit=10, offset=0, all_records=False, + q_params={}, raw=False): + """Retrieves a list of Resource Sharing Partners or specific partner. + + Args: + partner_id (str): The code of the Resource Sharing Partner (partner_code). + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of partners or specific partner + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + if partner_id: + url += ("/" + str(partner_id)) + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.read(url, args, raw=raw) + + if partner_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, raw=raw, + response=response, data_key='partner') + return response + + +class SubClientPartnersLending(Client): + """Handles the Lending Request endpoints of Resource Sharing Partners API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + + def get(self, partner_id, request_id, q_params={}, raw=False): + """Retrieve a lending request from a specific partner. + + Args: + partner_id (str): The code of the Resource Sharing Partner (partner_code). + request_id (str): The ID of the requested lending request. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + Lending request from a specific partner. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += ("/" + str(partner_id) + "/lending-requests") + url += ("/" + str(request_id)) + + response = self.read(url, args, raw=raw) + return response diff --git a/almapipy/task_lists.py b/almapipy/task_lists.py index 1e31b69..984c364 100644 --- a/almapipy/task_lists.py +++ b/almapipy/task_lists.py @@ -1,112 +1,114 @@ -from .client import Client -from . import utils - - -class SubClientTaskList(Client): - """ - Alma provides a set of Web services for handling task lists information, - enabling you to quickly and easily manipulate their details. - These Web services can be used by external systems. - For more info: https://developers.exlibrisgroup.com/alma/apis/taskslists - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to API. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/task-lists" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/taskslists" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/d48a1a58-d90c-4eb2-b69f-c17f7a016fd3.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - - # Hook in subclients of api - self.resources = SubClientTaskListResources(self.cnxn_params) - self.lending = SubClientTaskListLending(self.cnxn_params) - - -class SubClientTaskListResources(Client): - """Handles the requested resources endpoints of Task List API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/requested-resources' - self.cnxn_params['api_uri_full'] += '/requested-resources' - - def get(self, library_id, circ_desk, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a list of requested resources to be picked from the shelf in Alma - for a specific library/circ_desk. - - Args: - library_id (str): The library of the given circulation desk or department where the resources are located. - Use conf.units.get_libraries() to retrieve valid arguments. - circ_desk (str): The circulation desk where the action is being performed. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of requested resources. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - args['library'] = str(library_id) - args['circ_desk'] = str(circ_desk) - - response = self.read(url, args, raw=raw) - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, - raw=raw, response=response, - data_key='requested_resource') - return response - - -class SubClientTaskListLending(Client): - """Handles the requested resources endpoints of Task List API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/rs/lending-requests' - self.cnxn_params['api_uri_full'] += '/rs/lending-requests' - - def get(self, library_id, q_params={}, raw=False): - """Retrieve list of lending requests in Alma. - - Args: - library_id (str): The library of the given circulation desk or department where the resources are located. - Use conf.units.get_libraries() to retrieve valid arguments. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of lending requests. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - args['library'] = str(library_id) - - url = self.cnxn_params['api_uri_full'] - - response = self.read(url, args, raw=raw) - - return response +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils + + +class SubClientTaskList(Client): + """ + Alma provides a set of Web services for handling task lists information, + enabling you to quickly and easily manipulate their details. + These Web services can be used by external systems. + For more info: https://developers.exlibrisgroup.com/alma/apis/taskslists + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to API. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/task-lists" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/taskslists" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/d48a1a58-d90c-4eb2-b69f-c17f7a016fd3.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + + # Hook in subclients of api + self.resources = SubClientTaskListResources(self.cnxn_params) + self.lending = SubClientTaskListLending(self.cnxn_params) + + +class SubClientTaskListResources(Client): + """Handles the requested resources endpoints of Task List API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/requested-resources' + self.cnxn_params['api_uri_full'] += '/requested-resources' + + def get(self, library_id, circ_desk, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve a list of requested resources to be picked from the shelf in Alma + for a specific library/circ_desk. + + Args: + library_id (str): The library of the given circulation desk or department where the resources are located. + Use conf.units.get_libraries() to retrieve valid arguments. + circ_desk (str): The circulation desk where the action is being performed. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of requested resources. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + args['library'] = str(library_id) + args['circ_desk'] = str(circ_desk) + + response = self.read(url, args, raw=raw) + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, + raw=raw, response=response, + data_key='requested_resource') + return response + + +class SubClientTaskListLending(Client): + """Handles the requested resources endpoints of Task List API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/rs/lending-requests' + self.cnxn_params['api_uri_full'] += '/rs/lending-requests' + + def get(self, library_id, q_params={}, raw=False): + """Retrieve list of lending requests in Alma. + + Args: + library_id (str): The library of the given circulation desk or department where the resources are located. + Use conf.units.get_libraries() to retrieve valid arguments. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of lending requests. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + args['library'] = str(library_id) + + url = self.cnxn_params['api_uri_full'] + + response = self.read(url, args, raw=raw) + + return response diff --git a/almapipy/users.py b/almapipy/users.py index bced946..eb5802f 100644 --- a/almapipy/users.py +++ b/almapipy/users.py @@ -1,286 +1,476 @@ -from .client import Client -from . import utils - - -class SubClientUsers(Client): - """ - Alma provides a set of Web services for handling user information, - enabling you to quickly and easily manipulate user details. - These Web services can be used by external systems - such as student information systems (SIS)—to retrieve or update user data. - For more info: https://developers.exlibrisgroup.com/alma/apis/users - """ - - def __init__(self, cnxn_params={}): - - # Copy cnnection parameters and add info specific to API. - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] = "/almaws/v1/users" - self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/users" - self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/0aa8d36f-53d6-48ff-8996-485b90b103e4.wadl" - self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] - self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] - - # Hook in subclients of api - self.loans = SubClientUsersLoans(self.cnxn_params) - self.requests = SubClientUsersRequests(self.cnxn_params) - self.fees = SubClientUsersFees(self.cnxn_params) - self.deposits = SubClientUsersDeposits(self.cnxn_params) - - def get(self, user_id=None, query={}, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a user list or a single user. - - Args: - user_id (str): A unique identifier for the user. - Gets more detailed information. - query (dict): Search query for filtering a user list. Optional. - Searching for words from fields: [primary_id, first_name, - last_name, middle_name, email, job_category, identifiers, - general_info and ALL.]. - Only AND operator is supported for multiple filters. - Format {'field': 'value', 'field2', 'value2'}. - e.g. query = {'first_name': 'Sterling', 'last_name': 'Archer'} - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of user or a specific user's details. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - if user_id: - url += ("/" + str(user_id)) - else: - # include paramets specific to user list - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - # add search query if specified in desired format - if query: - args['q'] = self.__format_query__(query) - - response = self.read(url, args, raw=raw) - if user_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='user') - return response - - -class SubClientUsersLoans(Client): - """Handles the Loans endpoints of Users API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/' - self.cnxn_params['api_uri_full'] += '/' - - def get(self, user_id, loan_id=None, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a list of loans for a user. - - Args: - user_id (str): A unique identifier for the user. - loan_id (str): A unique identifier for the loan. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of loans or a specific loan for a given user. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += (str(user_id) + "/loans") - - if loan_id: - url += ('/' + str(loan_id)) - else: - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - if loan_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='item_loan') - return response - - -class SubClientUsersRequests(Client): - """Handles the Requests endpoints of Users API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/' - self.cnxn_params['api_uri_full'] += '/' - - def get(self, user_id, request_id=None, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a list of requests for a user. - - Args: - user_id (str): A unique identifier for the user. - request_id (str): A unique identifier for the request. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of requests or a specific request for a given user. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += (str(user_id) + "/requests") - - if request_id: - url += ('/' + str(request_id)) - else: - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - if request_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='user_request') - return response - - -class SubClientUsersFees(Client): - """Handles the Fines and Fees endpoints of Users API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/' - self.cnxn_params['api_uri_full'] += '/' - - def get(self, user_id, fee_id=None, q_params={}, raw=False): - """Retrieve a list of fines and fees for a user. - - Args: - user_id (str): A unique identifier for the user. - fee_id (str): A unique identifier for the fee. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of fines/fees or a specific fine/fee for a given user. - - """ - url = self.cnxn_params['api_uri_full'] - url += (str(user_id)) - url += '/fees' - if fee_id: - url += ('/' + str(fee_id)) - - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - return self.read(url, args, raw=raw) - - -class SubClientUsersDeposits(Client): - """Handles the Deposits endpoints of Users API""" - - def __init__(self, cnxn_params={}): - self.cnxn_params = cnxn_params.copy() - self.cnxn_params['api_uri'] += '/' - self.cnxn_params['api_uri_full'] += '/' - - def get(self, user_id, deposit_id=None, limit=10, offset=0, - all_records=False, q_params={}, raw=False): - """Retrieve a list of deposits for a user. - - Args: - user_id (str): A unique identifier for the user. - deposit_id (str): A unique identifier for the deposit. - limit (int): Limits the number of results. - Valid values are 0-100. - offset (int): The row number to start with. - all_records (bool): Return all rows returned by query. - Otherwise returns number specified by limit. - q_params (dict): Any additional query parameters. - raw (bool): If true, returns raw requests object. - - Returns: - List of deposits or a specific deposit for a given user. - - """ - args = q_params.copy() - args['apikey'] = self.cnxn_params['api_key'] - - url = self.cnxn_params['api_uri_full'] - url += (str(user_id) + "/deposits") - - if deposit_id: - url += ('/' + str(deposit_id)) - else: - if int(limit) > 100: - limit = 100 - elif int(limit) < 1: - limit = 1 - else: - limit = int(limit) - args['limit'] = limit - args['offset'] = int(offset) - - response = self.read(url, args, raw=raw) - if deposit_id: - return response - - # make multiple api calls until all records are retrieved - if all_records: - response = self.__read_all__(url=url, args=args, raw=raw, - response=response, data_key='user_deposit') - return response +# -*- coding: utf-8 -*- + +from .client import Client +from . import utils + +#from json import loads + +class SubClientUsers(Client): + """ + Alma provides a set of Web services for handling user information, + enabling you to quickly and easily manipulate user details. + These Web services can be used by external systems + such as student information systems (SIS) to retrieve or update user data. + For more info: https://developers.exlibrisgroup.com/alma/apis/users + """ + + def __init__(self, cnxn_params={}): + + # Copy cnnection parameters and add info specific to API. + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] = "/almaws/v1/users" + self.cnxn_params['web_doc'] = "https://developers.exlibrisgroup.com/alma/apis/users" + self.cnxn_params['wadl_url'] = "https://developers.exlibrisgroup.com/resources/wadl/0aa8d36f-53d6-48ff-8996-485b90b103e4.wadl" + self.cnxn_params['api_uri_full'] = self.cnxn_params['base_uri'] + self.cnxn_params['api_uri_full'] += self.cnxn_params['api_uri'] + + # Hook in subclients of api + self.loans = SubClientUsersLoans(self.cnxn_params) + self.requests = SubClientUsersRequests(self.cnxn_params) + self.fees = SubClientUsersFees(self.cnxn_params) + self.deposits = SubClientUsersDeposits(self.cnxn_params) + + # Returned values by functions on success/failure (not much "creativity" + # for now) + self.SUCCESS = True + self.FAILURE = False + + def create(self, identifier, id_type, user_data, raw=False): + """Create a single user if it does not exist yet in Alma + + Args: + identifier (str): The identifier itself for the user. + See: + id_type (str): The identifier type for the user + Values: from the code-table: UserIdentifierTypes + + See: + user_data (dict): Data for user enrollment. + Setting words for fields: [first_name, last_name, + middle_name, email, user_group, ...]. + Format {'field': 'value', 'field2', 'value2'}. + Values(user_group): code-table: UserGroups. + + See + raw (bool): If true, returns raw requests object. + + Returns: (?) + The user (at Alma) if a new user is created. + "FAILURE" if the 'identifier' was already set. + + """ + + headers = {'Authorization': 'apikey {}'.format(self.cnxn_params['api_key'])} + + data=user_data.copy() + + # Avoid sending 'primary_id' as 'id_type' + args = {} + query = {} + if id_type != 'primary_id': + args['id_type'] = id_type + query['identifiers'] = identifier +# TODO: add 'user_identifier' stuff into 'user_data' + else: + query['primary_id'] = identifier + data['primary_id'] = identifier + + args['q'] = self.__format_query__(query) + + url = self.cnxn_params['api_uri_full'] + + # Search for a user with this 'user_identifier' + response = self.Get(url, args=args, headers=headers, raw=raw) + +# TODO: ¿what happens when no response? Parse 'requests.models.Response' + if response['total_record_count'] == 0: + # No user exists with this 'identifier': Let's create it. + + """ + # 'user_identifier' chunk + aux_dict = {} + aux_dict['value'] = identifier + aux_dict['id_type'] = {} + aux_dict['id_type']['value'] = id_type + aux_dict['status'] = 'ACTIVE' + aux_dict['segment_type'] = 'External' + data['user_identifier'] = [ aux_dict ] + """ + """ + aux_dict = "{ 'user_identifier': [{ 'value': '" + identifier + "', 'id_type': { 'value': '" + id_type + "' }, 'status': 'ACTIVE', 'segment_type': 'External' }] }" + data = loads(aux_dict.replace("'", "\"")) + """ + + args.pop('q', None) + + # Send request + response = self.Post(url, data=data, args=args, headers=headers, raw=raw) + + else: + # User already exist in Alma. + response = self.FAILURE + + return response + + def read(self, user_id=None, query={}, limit=10, offset=0, all_records=False, q_params={}, raw=False): + """Retrieve a user list or a single user. + + Args: + user_id (str): A unique identifier for the user. + Gets more detailed information. + query (dict): Search query for filtering a user list. Optional. + Searching for words from fields: [primary_id, first_name, + last_name, middle_name, email, job_category, identifiers, + general_info and ALL.]. + Only AND operator is supported for multiple filters. + Format {'field': 'value', 'field2', 'value2'}. + e.g. query = {'first_name': 'Sterling', 'last_name': 'Archer'} + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of user or a specific user's details. + + """ + + args = q_params.copy() + + # Set 'API Key' as 'Authorization' at the Header + headers = {'Authorization': 'apikey {}'.format(self.cnxn_params['api_key'])} + + url = self.cnxn_params['api_uri_full'] + + if user_id: + url += ("/" + str(user_id)) + else: + # include paramets specific to user list + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + # add search query if specified in desired format + if query: + args['q'] = self.__format_query__(query) + + response = self.Get(url, args=args, headers=headers, raw=raw) + if user_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__read_all__(url=url, args=args, headers=headers, raw=raw, + response=response, data_key='user') + return response + + def update(self, primary_id, user_data, raw=False): + """Update a single user if it does exist yet in Alma + + WARNING: This function is only for Alma Sorcerer's Apprentices. + Advanced users' data structure knowledge is required. + + Args: + primary_id (str): The primary ididentifier of the user. + See: + user_data (dict): Data for user enrollment. + Setting words for fields: [first_name, last_name, + middle_name, email, user_group, ...]. + Format {'field': 'value', 'field2', 'value2'}. + Values(user_group): code-table: UserGroups. + + See + raw (bool): If true, returns raw requests object. + + Returns: (?) + "SUCCESS" if the user was updated successfully. + "FAILURE" if there were any trouble. + + """ + + # Set 'API Key' as 'Authorization' at the Header + headers = {'Authorization': 'apikey {}'.format(self.cnxn_params['api_key'])} + + url = self.cnxn_params['api_uri_full'] + "/" + str(primary_id) + + # Search for a user with this 'primary_id' included in the URL + response = self.Get(url, args={}, headers=headers, raw=raw) + +# TODO: ¿what happens when no response? Parse 'requests.models.Response' + if response['primary_id']: + # Found an user with this 'primary_id': Let's update it. + + # Send request + self.Put(url, data=user_data, headers=headers, raw=raw) + response = self.SUCCESS + + else: + # (a single) User not found in Alma. + response = self.FAILURE + + return response + + def delete(self, identifier, id_type, raw=False): + """Remove a single user if it does exist in Alma + + Args: + identifier (str): The identifier itself for the user. + See: + id_type (str): The identifier type for the user + Values: from the code-table: UserIdentifierTypes + + See: + raw (bool): If true, returns raw requests object. + + Returns: (?) + "SUCCESS" if the user has been successfully removed. + "FAILURE" if the 'identifier' was not present in Alma. + + """ + + headers = {'Authorization': 'apikey {}'.format(self.cnxn_params['api_key'])} + + # Avoid sending 'primary_id' as 'id_type' + args = {} + query = {} + if id_type != 'primary_id': + args['id_type'] = id_type + query['identifiers'] = identifier +# TODO: add 'user_identifier' stuff into 'user_data' + else: + query['primary_id'] = identifier + + args['q'] = self.__format_query__(query) + + url = self.cnxn_params['api_uri_full'] + + # Search for a user with this 'user_identifier' + response = self.Get(url, args=args, headers=headers, raw=raw) + +# TODO: ¿what happens when no response? Parse 'requests.models.Response' + if response['total_record_count'] == 1: + # A single user exists with this 'identifier': Let's remove it. + args.clear() + args['primary_id'] = query['primary_id'] + url += (str('/' + args['primary_id'])) + # Send request +# SEE: "TODO: Eval exception 204" at the end of Delete 'function' in 'client.py' +# response = self.Delete(url, args=args, headers=headers, raw=raw) + self.Delete(url, args=args, headers=headers, raw=raw) + response = self.SUCCESS + else: + # (a single) User not found in Alma. + response = self.FAILURE + + return response + + +class SubClientUsersLoans(Client): + """Handles the Loans endpoints of Users API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/' + self.cnxn_params['api_uri_full'] += '/' + + def read(self, user_id, loan_id=None, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve a list of loans for a user. + + Args: + user_id (str): A unique identifier for the user. + loan_id (str): A unique identifier for the loan. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of loans or a specific loan for a given user. + + """ + + headers = {'Authorization': 'apikey {}'.format(self.cnxn_params['api_key'])} + + args = q_params.copy() + + url = self.cnxn_params['api_uri_full'] + url += (str(user_id) + "/loans") + + if loan_id: + url += ('/' + str(loan_id)) + else: + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.Get(url, args=args, headers=headers, raw=raw) + if loan_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__Get_all__(url=url, args=args, headers=headers, raw=raw, + response=response, data_key='item_loan') + return response + + +class SubClientUsersRequests(Client): + """Handles the Requests endpoints of Users API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/' + self.cnxn_params['api_uri_full'] += '/' + + def read(self, user_id, request_id=None, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve a list of requests for a user. + + Args: + user_id (str): A unique identifier for the user. + request_id (str): A unique identifier for the request. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of requests or a specific request for a given user. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += (str(user_id) + "/requests") + + if request_id: + url += ('/' + str(request_id)) + else: + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.Get(url, args, raw=raw) + if request_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__Get_all__(url=url, args=args, raw=raw, + response=response, data_key='user_request') + return response + + +class SubClientUsersFees(Client): + """Handles the Fines and Fees endpoints of Users API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/' + self.cnxn_params['api_uri_full'] += '/' + + def read(self, user_id, fee_id=None, q_params={}, raw=False): + """Retrieve a list of fines and fees for a user. + + Args: + user_id (str): A unique identifier for the user. + fee_id (str): A unique identifier for the fee. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of fines/fees or a specific fine/fee for a given user. + + """ + url = self.cnxn_params['api_uri_full'] + url += (str(user_id)) + url += '/fees' + if fee_id: + url += ('/' + str(fee_id)) + + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + return self.Get(url, args, raw=raw) + + +class SubClientUsersDeposits(Client): + """Handles the Deposits endpoints of Users API""" + + def __init__(self, cnxn_params={}): + self.cnxn_params = cnxn_params.copy() + self.cnxn_params['api_uri'] += '/' + self.cnxn_params['api_uri_full'] += '/' + + def read(self, user_id, deposit_id=None, limit=10, offset=0, + all_records=False, q_params={}, raw=False): + """Retrieve a list of deposits for a user. + + Args: + user_id (str): A unique identifier for the user. + deposit_id (str): A unique identifier for the deposit. + limit (int): Limits the number of results. + Valid values are 0-100. + offset (int): The row number to start with. + all_records (bool): Return all rows returned by query. + Otherwise returns number specified by limit. + q_params (dict): Any additional query parameters. + raw (bool): If true, returns raw requests object. + + Returns: + List of deposits or a specific deposit for a given user. + + """ + args = q_params.copy() + args['apikey'] = self.cnxn_params['api_key'] + + url = self.cnxn_params['api_uri_full'] + url += (str(user_id) + "/deposits") + + if deposit_id: + url += ('/' + str(deposit_id)) + else: + if int(limit) > 100: + limit = 100 + elif int(limit) < 1: + limit = 1 + else: + limit = int(limit) + args['limit'] = limit + args['offset'] = int(offset) + + response = self.Get(url, args, raw=raw) + if deposit_id: + return response + + # make multiple api calls until all records are retrieved + if all_records: + response = self.__Get_all__(url=url, args=args, raw=raw, + response=response, data_key='user_deposit') + return response diff --git a/almapipy/utils.py b/almapipy/utils.py index d0ba87c..09fa040 100644 --- a/almapipy/utils.py +++ b/almapipy/utils.py @@ -1,26 +1,28 @@ -""" -Error classes and other helpful functions -""" - - -class Error(Exception): - """Base class for exceptions""" - pass - - -class AlmaError(Error): - """ - Base Exception class for Alma API calls - """ - - def __init__(self, message, response=None, url=None): - super(AlmaError, self).__init__(message) - self.message = message - self.response = response - self.url = url - - -class ArgError(Error): - def __init__(self, message): - super(ArgError, self).__init__(message) - self.message = "Invalid Argument: " + message +# -*- coding: utf-8 -*- + +""" +Error classes and other helpful functions +""" + + +class Error(Exception): + """Base class for exceptions""" + pass + + +class AlmaError(Error): + """ + Base Exception class for Alma API calls + """ + + def __init__(self, message, response=None, url=None): + super(AlmaError, self).__init__(message) + self.message = message + self.response = response + self.url = url + + +class ArgError(Error): + def __init__(self, message): + super(ArgError, self).__init__(message) + self.message = "Invalid Argument: " + message diff --git a/setup.py b/setup.py index 280b474..9f0117b 100644 --- a/setup.py +++ b/setup.py @@ -1,39 +1,40 @@ -from setuptools import setup -#from distutils.core import setup -try: - from pypandoc import convert - - def read_md(f): return convert(f, 'rst') -except ImportError: - print("warning: pypandoc module not found, could not convert Markdown to RST") - - def read_md(f): return open(f, 'r').read() - - -VERSION = "1.0.0" - -setup( - name="almapipy", - packages=['almapipy'], - version=VERSION, - description="Python requests wrapper for the Ex Libris Alma API", - license='MIT' - long_description=read_md('README.md'), - author="Steve Pelkey", - author_email="spelkey@ucdavis.edu", - url='https://github.com/UCDavisLibrary/almapipy', - install_requires=['requests'], - keywords='alma exlibris exlibrisgroup api bibliographic' - classifiers=[ - "Intended Audience :: Developers", - "Intended Audience :: Education", - "Intended Audience :: Science/Research", - "License :: OSI Approved :: MIT License", - "Natural Language :: English", - "Operating System :: OS Independent", - "Programming Language :: Python", - "Topic :: Software Development :: Libraries :: Python Modules", - - 'Programming Language :: Python :: 3.6' - ] -) +# -*- coding: utf-8 -*- + +from setuptools import setup +from setuptools import find_packages + +with open("README.md", "r") as fh: + long_description = fh.read() + +from almapipy.__init__ import __version__ as version +from almapipy.__init__ import __author__ as author +from almapipy.__init__ import __author_email__ as author_email +from almapipy.__init__ import __project_url__ as url +from almapipy.__init__ import __project_description__ as description +from almapipy.__init__ import __license__ as license +from almapipy.__init__ import __project_name__ as name + +setup( + name = name, + version = version, + author = author, + author_email = author_email, + url = url, + description = description, + long_description = long_description, +# long_description_content_type = "text/markdown", + install_requires = ['requests'], + keywords = 'alma exlibris exlibrisgroup api bibliographic', + packages=find_packages(), + classifiers = [ + "Programming Language :: Python :: 3" + "License :: OSI Approved :: MIT License", + "Operating System :: OS Independent", + "Intended Audience :: Developers", + "Intended Audience :: Education", + "Intended Audience :: Science/Research", + "Natural Language :: English", + "Topic :: Software Development :: Libraries :: Python Modules", + ], + license = license, +)