PK#IJJzrspace_client/__init__.pyfrom .client import Client from .advanced_query_builder import AdvancedQueryBuilder __all__ = ['Client', 'AdvancedQueryBuilder'] PK'JJ[?*?*rspace_client/client.pyimport requests class Client: """Client for RSpace API v1. Most methods return a dictionary with fields described in the API documentation. The documentation can be found at https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). For authentication, an API key must be provided. It can be found by logging in and navigating to 'My Profile' page. """ class ConnectionError(Exception): pass class AuthenticationError(Exception): pass class NoSuchLinkRel(Exception): pass API_VERSION = 'v1' def __init__(self, rspace_url, api_key): """ Initializes RSpace client. :param rspace_url: RSpace server URL (for example, https://community.researchspace.com) :param api_key: RSpace API key of a user can be found on 'My Profile' page """ self.rspace_url = rspace_url self.api_key = api_key def _get_api_url(self): """ Returns an API server URL. :return: string URL """ return '%s/api/%s' % (self.rspace_url, Client.API_VERSION) def retrieve_api_results(self, url, params=None, content_type='application/json'): """ Makes the requested API call and returns either an exception or a parsed JSON response as a dictionary. Authentication header is automatically added. In most cases, a specialised method can be used instead. :param url: URL to retrieve :param params: GET parameters to be added to the URL :param content_type: content type :return: parsed JSON response as a dictionary """ headers = { 'apiKey': self.api_key, 'Accept': content_type } try: response = requests.get(url, params=params, headers=headers) # Check whether response includes UNAUTHORIZED response code if response.status_code == 401: raise Client.AuthenticationError(response.json()['message']) if response.status_code != 200: raise ValueError(next(iter(response.json()['errors'] or []), None) or response.json()['message']) if content_type == 'application/json': return response.json() else: return response.text except requests.exceptions.ConnectionError as e: raise Client.ConnectionError(e) @staticmethod def _get_links(response): """ Finds links part of the response. Most responses contain links section with URLs that might be useful to query for further information. :param response: response from the API server :return: links section of the response """ try: return response['_links'] except KeyError: raise Client.NoSuchLinkRel('There are no links!') def get_link_contents(self, response, link_rel): """ Finds a link with rel attribute equal to link_rel and retrieves its contents. :param response: response from the API server :param link_rel: rel attribute value to look for :return: parsed response from the found URL """ return self.retrieve_api_results(self.get_link(response, link_rel)) def get_link(self, response, link_rel): """ Finds a link with rel attribute equal to link_rel. :param response: response from the API server. :param link_rel: rel attribute value to look for :return: string URL """ for link in self._get_links(response): if link['rel'] == link_rel: return link['link'] raise Client.NoSuchLinkRel('Requested link rel "%s", available rel(s): %s' % (link_rel, ', '.join([x['rel'] for x in self._get_links(response)]))) def download_link_to_file(self, url, filename): """ Downloads a file from the API server. :param url: URL of the file to be downloaded :param filename: file path to save the file to """ headers = { 'apiKey': self.api_key, 'Accept': 'application/octet-stream' } with open(filename, 'wb') as fd: for chunk in requests.get(url, headers=headers).iter_content(chunk_size=128): fd.write(chunk) @staticmethod def link_exists(response, link_rel): """ Checks whether there is a link with rel attribute equal to link_rel in the links section of the response. :param response: response from the API server :param link_rel: rel attribute value to look for :return: True, iff the link exists """ return link_rel in [x['rel'] for x in Client._get_links(response)] # Documents methods def get_documents(self, query=None, order_by='lastModified desc', page_number=0, page_size=20): """ The Documents endpoint returns a paginated list of summary information about Documents in the RSpace workspace. These can be individual documents or notebook entries. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). :param (optional) query: Global search for a term, works identically to the simple "All' search in RSpace Workspace. :param order_by: Sort order for documents. :param page_number: For paginated results, this is the number of the page requested, 0 based. :param page_size: The maximum number of items to retrieve. :return: parsed response as a dictionary """ params = { 'orderBy': order_by, 'pageSize': page_size, 'pageNumber': page_number } if query is not None: params['query'] = query return self.retrieve_api_results(self._get_api_url() + '/documents', params) def get_documents_advanced_query(self, advanced_query, order_by='lastModified desc', page_number=0, page_size=20): """ The Documents endpoint returns a paginated list of summary information about Documents in the RSpace workspace. These can be individual documents or notebook entries. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). :param advanced_query: JSON representation of a search query. This can be built using AdvancedQueryBuilder. :param order_by: Sort order for documents. :param page_number: For paginated results, this is the number of the page requested, 0 based. :param page_size: The maximum number of items to retrieve. :return: parsed response as a dictionary """ params = { 'advancedQuery': advanced_query, 'orderBy': order_by, 'pageSize': page_size, 'pageNumber': page_number } return self.retrieve_api_results(self._get_api_url() + '/documents', params) def get_document(self, doc_id): """ Gets information about a document. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). :param doc_id: numeric document ID (the same as Global ID, but just the numeric part) :return: a dictionary that includes: document metadata, field content, metadata about media items belonging to this document, links to download the content of media files """ return self.retrieve_api_results(self._get_api_url() + '/documents/' + str(doc_id)) def get_document_csv(self, doc_id): """ Gets information about a document. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). :param doc_id: numeric document ID (the same as Global ID, but just the numeric part) :return: CSV that includes: document metadata, field content, metadata about media items belonging to this document, links to download the content of media files """ return self.retrieve_api_results(self._get_api_url() + '/documents/' + str(doc_id), content_type='text/csv') # File methods def get_files(self, page_number=0, page_size=20, order_by="lastModified desc", media_type="image"): """ Lists media items - i.e. content shown in the Gallery in RSpace web application. Note that this does not include files linked from external file systems or 3rd party providers. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). :param page_number: For paginated results, this is the number of the page requested, 0 based. :param page_size: The maximum number of items to retrieve. :param order_by: Sort order for documents. :param media_type: can be 'image', 'av' (audio or video), 'document' (any other file) :return: parsed response as a dictionary """ params = { 'pageNumber': page_number, 'pageSize': page_size, 'orderBy': order_by, 'mediaType': media_type } return self.retrieve_api_results(self._get_api_url() + '/files', params) def get_file_info(self, file_id): """ Gets metadata of a single file by its id. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). :param file_id: numeric document ID (the same as Global ID, but just the numeric part) :return: parsed response as a dictionary """ return self.retrieve_api_results(self._get_api_url() + '/files/' + str(file_id)) def download_file(self, file_id, filename): """ Downloads file contents. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). :param file_id: numeric document ID (the same as Global ID, but just the numeric part) :param filename: file path to save the file to """ return self.download_link_to_file(self._get_api_url() + '/files/' + str(file_id) + '/file', filename) # Miscellaneous methods def get_status(self): """ Simple API call to check that API service is available. Throws an AuthenticationError if authentication fails. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). :return: parsed response as a dictionary (most important field is 'message' which is supposed to be 'OK') """ return self.retrieve_api_results(self._get_api_url() + '/status') PKsIJVZ!!'rspace_client/advanced_query_builder.pyfrom enum import Enum import json class AdvancedQueryBuilder: """ AdvancedQueryBuilder helps to build an advanced query for /documents API endpoint. """ class QueryType(Enum): """ Lists all query types available in /documents API endpoint. More information on https://community.researchspace.com/public/apiDocs (or your own instance's /public/apiDocs). """ GLOBAL = 'global' FULL_TEXT = 'fullText' TAG = 'tag' NAME = 'name' CREATED = 'created' LAST_MODIFIED = 'lastModified' FORM = 'form' ATTACHMENT = 'attachment' def __init__(self, operator='and'): """ :param operator: either 'and' or 'or' """ self.operand = operator self.terms = [] def add_term(self, query, query_type): """ Adds an additional search term to the query. :param query: query depending on the query_type can be either a text, date or Global ID :param query_type: query type from the QueryType enum :return: self """ if not isinstance(query_type, AdvancedQueryBuilder.QueryType): raise TypeError('query_type must be an instance of QueryType (for example, QueryType.GLOBAL)') self.terms.append({ 'query': query, 'queryType': query_type.value }) return self def get_advanced_query(self): """ Builds an advanced query. :return: JSON representation of the built advanced query """ return json.dumps({ 'operator': self.operand, 'terms': self.terms }) def __str__(self): """ :return: JSON representation of the built advanced query """ return self.get_advanced_query() PK*JJ+J-rspace_client-0.0.2.dist-info/DESCRIPTION.rst# rspace-client-python This project contains a client which helps calling RSpace APIs. There are some example Python scripts. To begin with you'll need an account on an RSpace server and an API key which you can get from your profile page. In these examples we'll be using the rspace_client package (code is in rspace_client folder) which provides an abstraction over lower-level libraries. It's compatible with both Python 2 and Python 3. All the code listed here is in the project. For full details of our API spec please see https://your.rspace.com/public/apiDocs To install rspace-client and its dependencies, run ```bash pip3 install rspace-client ``` To run the example scripts in the examples folder, cd to that folder, then run ```bash python3 ExampleScript.py https://your.rspace.com MyAPIKey ``` replacing MyAPIKey with your key, and ExampleScript.py with the name of the script you want to run. ### A basic query to list documents First of all we'll get our URL and key from a command-line parameters. ```python parser = argparse.ArgumentParser() parser.add_argument("server", help="RSpace server URL (for example, https://community.researchspace.com)", type=str) parser.add_argument("apiKey", help="RSpace API key can be found on 'My Profile'", type=str) args = parser.parse_args() client = rspace_client.Client(args.server, args.apiKey) documents = client.get_documents() ``` In the above example, the 'documents' variable is a dictionary that can easily be accessed for data: ```python print(document['name'], document['id'], document['lastModified']) ``` #### Iterating over pages of results The JSON response also contains a `_links` field that uses HATEOAS conventions to provide links to related content. For document listings and searches, links to `previous`, `next`, `first` and `last` pages are provided when needed. Using this approach we can iterate through pages of results, getting summary information for each document. ```python while client.link_exists(response, 'next'): print('Retrieving next page...') response = client.get_link_contents(response, 'next') ``` A complete example of this is `examples/paging_through_results.py`. ### Searching RSpace API provides two sorts of search - a basic search that searches all searchable fields, and an advanced search where more fine-grained queries can be made and combined with boolean operators. A simple search can be run by calling get_documents with a query parameter: ```python response = client.get_documents(query='query_text') ``` Here are some examples of advanced search constructs: ```python // search by tag: search = json.dumps([terms:[[query:"ATag", queryType:"tag"]]]) // by name search = json.dumps([terms:[[query:"AName", queryType:"name"]]]) // for items created on a given date using IS0-8601 or yyyy-MM-dd format search = json.dumps([terms:[[query:"2016-07-23", queryType:"created"]]]) // for items modified between 2 dates using IS0-8601 or yyyy-MM-dd format search = json.dumps([terms:[[query:"2016-07-23;2016-08-23 ", queryType:"lastModified"]]]) // for items last modified on either of 2 dates: search = json.dumps([operator:"or",terms:[[query:"2015-07-06", queryType:"lastModified"], [query:"2015-07-07", queryType:"lastModified"] ]) // search for documents created from a given form: search = json.dumps([terms:[[query:"Basic Document", queryType:"form"]]]) // search for documents created from a given form and a specific tag: search = json.dumps([operator:"and", terms:[[query:"Basic Document", queryType:"form"], [query:"ATag", queryType:"tag"]]]) ``` or by using AdvancedQueryBuilder ```python # Creation date (documents created between 2017-01-01 and 2017-12-01 advanced_query = rspace_client.AdvancedQueryBuilder().\ add_term('2017-01-01;2017-12-01', rspace_client.AdvancedQueryBuilder.QueryType.CREATED).\ get_advanced_query() ``` To submit these queries pass them as a parameter to `get_get_documents_advanced_query`: ```python response = client.get_documents_advanced_query(advanced_query) for document in response['documents']: print(document['name'], document['id'], document['lastModified']) ``` ### Retrieving document content Content can be retrieved from the endpoint `/documents/{id}` where {id} is a documentID. Here is an example retrieving a document in CSV format taken from `forms.py` script: ```python advanced_query = rspace_client.AdvancedQueryBuilder(operator='and').\ add_term(form_id, rspace_client.AdvancedQueryBuilder.QueryType.FORM).\ get_advanced_query() response = client.get_documents_advanced_query(advanced_query) print('Found answers:') for document in response['documents']: print('Answer name:', document['name']) document_response = client.get_document_csv(document['id']) print(document_response) ``` ### Getting attached files Here's an example where we download file attachments associated with some documents. The code is in `download_attachments.py`. ```python try: response = client.get_document(doc_id=document_id) for field in response['fields']: for file in field['files']: download_metadata_link = client.get_link_contents(file, 'self') filename = '/tmp/' + download_metadata_link['name'] print('Downloading to file', filename) client.download_link_to_file(client.get_link(download_metadata_link, 'enclosure'), filename) except ValueError: print('Document with id %s not found' % str(document_id)) ``` PK*JJG x""+rspace_client-0.0.2.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "Topic :: Software Development :: Libraries", "License :: OSI Approved :: Apache Software License", "Programming Language :: Python :: 2", "Programming Language :: Python :: 2.6", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3.3", "Programming Language :: Python :: 3.4", "Programming Language :: Python :: 3.5"], "extensions": {"python.details": {"contacts": [{"email": "s1310787@sms.ed.ac.uk", "name": "Research Innovations Ltd", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/rspace-os/rspace-client-python"}}}, "extras": ["dev", "test"], "generator": "bdist_wheel (0.26.0)", "keywords": ["rspace", "api", "client"], "license": "Apache Software License", "metadata_version": "2.0", "name": "rspace-client", "run_requires": [{"requires": ["requests"]}], "summary": "A client which helps calling RSpace APIs", "version": "0.0.2"}PK*JJ t|+rspace_client-0.0.2.dist-info/top_level.txtrspace_client PK*JJndnn#rspace_client-0.0.2.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any Tag: py3-none-any PK*JJ &rspace_client-0.0.2.dist-info/METADATAMetadata-Version: 2.0 Name: rspace-client Version: 0.0.2 Summary: A client which helps calling RSpace APIs Home-page: https://github.com/rspace-os/rspace-client-python Author: Research Innovations Ltd Author-email: s1310787@sms.ed.ac.uk License: Apache Software License Keywords: rspace api client Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: Topic :: Software Development :: Libraries Classifier: License :: OSI Approved :: Apache Software License Classifier: Programming Language :: Python :: 2 Classifier: Programming Language :: Python :: 2.6 Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Programming Language :: Python :: 3.5 Requires-Dist: requests Provides-Extra: dev Provides-Extra: test # rspace-client-python This project contains a client which helps calling RSpace APIs. There are some example Python scripts. To begin with you'll need an account on an RSpace server and an API key which you can get from your profile page. In these examples we'll be using the rspace_client package (code is in rspace_client folder) which provides an abstraction over lower-level libraries. It's compatible with both Python 2 and Python 3. All the code listed here is in the project. For full details of our API spec please see https://your.rspace.com/public/apiDocs To install rspace-client and its dependencies, run ```bash pip3 install rspace-client ``` To run the example scripts in the examples folder, cd to that folder, then run ```bash python3 ExampleScript.py https://your.rspace.com MyAPIKey ``` replacing MyAPIKey with your key, and ExampleScript.py with the name of the script you want to run. ### A basic query to list documents First of all we'll get our URL and key from a command-line parameters. ```python parser = argparse.ArgumentParser() parser.add_argument("server", help="RSpace server URL (for example, https://community.researchspace.com)", type=str) parser.add_argument("apiKey", help="RSpace API key can be found on 'My Profile'", type=str) args = parser.parse_args() client = rspace_client.Client(args.server, args.apiKey) documents = client.get_documents() ``` In the above example, the 'documents' variable is a dictionary that can easily be accessed for data: ```python print(document['name'], document['id'], document['lastModified']) ``` #### Iterating over pages of results The JSON response also contains a `_links` field that uses HATEOAS conventions to provide links to related content. For document listings and searches, links to `previous`, `next`, `first` and `last` pages are provided when needed. Using this approach we can iterate through pages of results, getting summary information for each document. ```python while client.link_exists(response, 'next'): print('Retrieving next page...') response = client.get_link_contents(response, 'next') ``` A complete example of this is `examples/paging_through_results.py`. ### Searching RSpace API provides two sorts of search - a basic search that searches all searchable fields, and an advanced search where more fine-grained queries can be made and combined with boolean operators. A simple search can be run by calling get_documents with a query parameter: ```python response = client.get_documents(query='query_text') ``` Here are some examples of advanced search constructs: ```python // search by tag: search = json.dumps([terms:[[query:"ATag", queryType:"tag"]]]) // by name search = json.dumps([terms:[[query:"AName", queryType:"name"]]]) // for items created on a given date using IS0-8601 or yyyy-MM-dd format search = json.dumps([terms:[[query:"2016-07-23", queryType:"created"]]]) // for items modified between 2 dates using IS0-8601 or yyyy-MM-dd format search = json.dumps([terms:[[query:"2016-07-23;2016-08-23 ", queryType:"lastModified"]]]) // for items last modified on either of 2 dates: search = json.dumps([operator:"or",terms:[[query:"2015-07-06", queryType:"lastModified"], [query:"2015-07-07", queryType:"lastModified"] ]) // search for documents created from a given form: search = json.dumps([terms:[[query:"Basic Document", queryType:"form"]]]) // search for documents created from a given form and a specific tag: search = json.dumps([operator:"and", terms:[[query:"Basic Document", queryType:"form"], [query:"ATag", queryType:"tag"]]]) ``` or by using AdvancedQueryBuilder ```python # Creation date (documents created between 2017-01-01 and 2017-12-01 advanced_query = rspace_client.AdvancedQueryBuilder().\ add_term('2017-01-01;2017-12-01', rspace_client.AdvancedQueryBuilder.QueryType.CREATED).\ get_advanced_query() ``` To submit these queries pass them as a parameter to `get_get_documents_advanced_query`: ```python response = client.get_documents_advanced_query(advanced_query) for document in response['documents']: print(document['name'], document['id'], document['lastModified']) ``` ### Retrieving document content Content can be retrieved from the endpoint `/documents/{id}` where {id} is a documentID. Here is an example retrieving a document in CSV format taken from `forms.py` script: ```python advanced_query = rspace_client.AdvancedQueryBuilder(operator='and').\ add_term(form_id, rspace_client.AdvancedQueryBuilder.QueryType.FORM).\ get_advanced_query() response = client.get_documents_advanced_query(advanced_query) print('Found answers:') for document in response['documents']: print('Answer name:', document['name']) document_response = client.get_document_csv(document['id']) print(document_response) ``` ### Getting attached files Here's an example where we download file attachments associated with some documents. The code is in `download_attachments.py`. ```python try: response = client.get_document(doc_id=document_id) for field in response['fields']: for file in field['files']: download_metadata_link = client.get_link_contents(file, 'self') filename = '/tmp/' + download_metadata_link['name'] print('Downloading to file', filename) client.download_link_to_file(client.get_link(download_metadata_link, 'enclosure'), filename) except ValueError: print('Document with id %s not found' % str(document_id)) ``` PK*JJZ$rspace_client-0.0.2.dist-info/RECORDrspace_client/__init__.py,sha256=t5eNcWFnjKUiMj0KgtMeVlJUMhT4KvyizabXcgBnhuo,130 rspace_client/advanced_query_builder.py,sha256=6WrqMBq_P0HRMP_6OpKAmWDkgfIhETSXbMX49RaRRHU,1825 rspace_client/client.py,sha256=Pv1vy_ssAZ9dHYlGv3H0iuz0p_wTY4r7blw0I6BCTKs,10815 rspace_client-0.0.2.dist-info/DESCRIPTION.rst,sha256=I-mbbDvpVd85UET_PhDDNqGazNRNGcKJuvNjjMIGg8M,5630 rspace_client-0.0.2.dist-info/METADATA,sha256=u00SrDzWya0tTw14EDtPMLdT9tigsuDlqFLXOCZPSsQ,6563 rspace_client-0.0.2.dist-info/RECORD,, rspace_client-0.0.2.dist-info/WHEEL,sha256=GrqQvamwgBV4nLoJe0vhYRSWzWsx7xjlt74FT0SWYfE,110 rspace_client-0.0.2.dist-info/metadata.json,sha256=ROfpAE22ijJSaTuyZrwiOIPwrWELHrQ-zzwaaiVEs6I,1058 rspace_client-0.0.2.dist-info/top_level.txt,sha256=ew5M_eBc5bajD0w0CCfzI8U3ZlR6EnkNh8GehFPG8lQ,14 PK#IJJzrspace_client/__init__.pyPK'JJ[?*?*rspace_client/client.pyPKsIJVZ!!'-+rspace_client/advanced_query_builder.pyPK*JJ+J-2rspace_client-0.0.2.dist-info/DESCRIPTION.rstPK*JJG x""+Hrspace_client-0.0.2.dist-info/metadata.jsonPK*JJ t|+GMrspace_client-0.0.2.dist-info/top_level.txtPK*JJndnn#Mrspace_client-0.0.2.dist-info/WHEELPK*JJ &MNrspace_client-0.0.2.dist-info/METADATAPK*JJZ$4hrspace_client-0.0.2.dist-info/RECORDPK k