Package: rspace-client
rspace-client-pythonThis project contains a client which helps calling RSpace ELN and Inventory APIs. There are some example Python scripts.This client is easy to use in Jupyter notebooks - watch thisvideofor an example.To begin with, you'll need an account on an RSpace server and an API key which you can get from yourprofile page.
You can sign up for a free RSpace account athttps://community.researchspace.com. Alternatively, you can runRSpace locally in Docker.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 Python 3.7 onwards, based on analysis byverminAll the code listed here is in the project.For full details of our API specification, please see https://<YOUR_RSPACE_DOMAIN>/public/apiDocsFor example, if you are usinghttps://community.researchspace.com,
the API documentation is available athttps://community.researchspace.com/public/apiDocsSeeDEVELOPING.mdfor details of running tests.To install rspace-client and its dependencies, runpipinstallrspace-client==2.5.0Compatibility with RSpace server versionsELN API compatible with 1.69 or laterInventory API compatible with 1.73 - 1.76 inclusiveYou may need to installrequestsmodule:pip3installrequestsUsing the rspace_client library in your own codeYou'll need a running RSpace instance to send requests to. To use Inventory client you'll
need RSpace 1.70 or later/The simplest way to read in the URL and API key is from environment variables, e.g.On Linux/MacOS shellbash> export RSPACE_URL=https:/myrspace.com
bash> export RSPACE_API_KEY=abcdefgh...substituting in your own values.import os
from rspace_client.inv import inv
from rspace_client.eln import eln

inv_cli = inv.InventoryClient(os.getenv("RSPACE_URL"), os.getenv("RSPACE_API_KEY"))
eln_cli = eln.ELNClient(os.getenv("RSPACE_URL"), os.getenv("RSPACE_API_KEY"))

samples = inv_cli.list_samples()
print (f"There are {samples['totalHits']} samples")

print(eln_cli.get_status())Example usageA basic query to list documentsFirst of all we'll get our URL and key from a command-line parameters.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:print(document['name'],document['id'],document['lastModified'])To run the example scripts in the examples folder, cd to that folder, then runpython3ExampleScript.py$RSPACE_URL$RSPACE_API_KEYreplacing ExampleScript.py with the name of the script you want to run.Iterating over pages of resultsThe JSON response also contains a_linksfield that uses HATEOAS conventions to provide links to related content. For document listings and searches, links toprevious,next,firstandlastpages are provided when needed.Using this approach we can iterate through pages of results, getting summary information for each document.whileclient.link_exists(response,'next'):print('Retrieving next page...')response=client.get_link_contents(response,'next')A complete example of this isexamples/paging_through_results.py.SearchingRSpace 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:response=client.get_documents(query='query_text')Here are some examples of advanced search constructs://searchbytag:search=json.dumps([terms:[[query:"ATag",queryType:"tag"]]])//bynamesearch=json.dumps([terms:[[query:"AName",queryType:"name"]]])//foritemscreatedonagivendateusingIS0-8601oryyyy-MM-ddformatsearch=json.dumps([terms:[[query:"2016-07-23",queryType:"created"]]])//foritemsmodifiedbetween2datesusingIS0-8601oryyyy-MM-ddformatsearch=json.dumps([terms:[[query:"2016-07-23;2016-08-23 ",queryType:"lastModified"]]])//foritemslastmodifiedoneitherof2dates:search=json.dumps([operator:"or",terms:[[query:"2015-07-06",queryType:"lastModified"],[query:"2015-07-07",queryType:"lastModified"]])//searchfordocumentscreatedfromagivenform:search=json.dumps([terms:[[query:"Basic Document",queryType:"form"]]])//searchfordocumentscreatedfromagivenformandaspecifictag:search=json.dumps([operator:"and",terms:[[query:"Basic Document",queryType:"form"],[query:"ATag",queryType:"tag"]]])or by using AdvancedQueryBuilder# Creation date (documents created between 2017-01-01 and 2017-12-01advanced_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 toget_get_documents_advanced_query:response=client.get_documents_advanced_query(advanced_query)fordocumentinresponse['documents']:print(document['name'],document['id'],document['lastModified'])Retrieving document contentContent can be retrieved from the endpoint/documents/{id}where {id} is a documentID.Here is an example retrieving a document in CSV format taken fromforms.pyscript: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:')fordocumentinresponse['documents']:print('Answer name:',document['name'])document_response=client.get_document_csv(document['id'])print(document_response)Getting attached filesHere's an example where we download file attachments associated with some documents. The code is indownload_attachments.py.try:response=client.get_document(doc_id=document_id)forfieldinresponse['fields']:forfileinfield['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)exceptValueError:print('Document with id%snot found'%str(document_id))Creating / editing a new documentA document can be created by sending a POST request to/documents. Document name, form from which the document is created, tags and field values can be specified. The example code is increate_document.py.# Creating a new Basic document in Api Inbox foldernew_document=client.create_document(name='Python API Example Basic Document',tags=['Python','API','example'],fields=[{'content':'Some example text'}])You can also supply theparentFolderIdof the workspace folder you want the document created in:# Creating a new Basic document in specified folder:new_document=client.create_document(name='Python API Example Basic Document',tags=['Python','API','example'],fields=[{'content':'Some example text'}],parent_folder_id=21);It is possible to edit a document by sending a PUT request to/documents/{id}, where {id} is a documentID. Document name, tags and field values can be edited.# Editing the document to link to the uploaded fileclient.update_document(document['id'],fields=[{'content':'Edited example text.'}])Uploading a file to galleryAny file that can be uploaded by using the UI can be uploaded by sending a POST request to/files. Also, it is possible to link to the file from any document as shown increate_document.pyexample.# Uploading a file to the gallerywithopen('resources/2017-05-10_1670091041_CNVts.csv','rb')asf:new_file=client.upload_file(f,caption='some caption')Linking to files and other RSpace documentsThere is a convenient syntax to link to either files or other RSpace documents.To include links to files in your document content, you can use the syntax <fileId=12345> where '12345' is the ID of a file uploaded through thefiles/endpoint.# Editing a document to link to an uploaded fileclient.update_document(new_document['id'],fields=[{'content':'Some example text. Link to the uploaded file: <fileId={}>'.format(new_file['id'])}])To include links to RSpace documents, folders or notebooks in your document content, you can use the syntax <docId=12345> where '12345' is the ID of an RSpace document,folder or notebook. E.g.# Editing a document to link to another RSpace documentclient.update_document(new_document['id'],fields=[{'content':'Some example text. Link to another document: <docId={}>'.format(anotherDocument['id'])}])ActivityAccess to the information that is available from the RSpace audit trail. This provides logged information on 'who did what, when’.For example, to get all activity for a particular document:response=client.get_activity(global_id=document_id)print('Activities for document{}:'.format(document_id))foractivityinresponse['activities']:print(activity)To get all activity related to documents being created or modified last week:date_from=date.today()-timedelta(days=7)response=client.get_activity(date_from=date_from,domains=['RECORD'],actions=['CREATE','WRITE'])print('All activity related to documents being created or modified from{}to now:'.format(date_from.isoformat()))foractivityinresponse['activities']:print(activity)Creating a Folder / NotebookA folder can be created by sending a POST request to/folders. All arguments are optional. Name, parent folder id and whether to create a notebook can be specified. For example, to create a folder named 'Testing Folder',create_foldermethod can be used:# Creating a folder named 'Testing Folder'new_folder=client.create_folder('Testing Folder')Notebooks can be created by settingnotebook=True. To create a new notebook inside the previously created folder:# Creating a notebook named 'Testing Notebook' inside the previously created folder:new_notebook=client.create_folder('Testing Notebook',parent_folder_id=new_folder['globalId'],notebook=True)There are some restrictions on where you can create folders and notebooks, which are required to maintain consistent behaviour with the web application.You can't create folders or notebooks inside notebooksYou can't create notebooks inside the Shared Folder; create them in a User folder first, then share. (Sharing is not yet supported in the API, but you can do this in the web application).Getting Information About a Folder / NotebookFolder or notebook information can be retrieved by sending a GET request to/folders/{folderId}where folder id is a numerical ID of a folder or a notebook. Python client accepts both numerical IDs and global IDs. Methodget_foldercan be used to get information about a folder:# Get information about a folderfolder_info=client.get_folder('FL123')# or client.get_folder(123)print(folder_info['globalId'],folder_info['name'])FormsPublished forms can be listed by sending a GET request to/forms. The results might be paginated if there are too many forms (seecreate_form.pyexample for a more in depth usage example).# Listing all published formsresponse=client.get_forms()forforminresponse['forms']:print(form['globalId'],form['name'])A new form can be created by sending a POST request to/forms. Name, tags (optionally) and fields can be specified. Currently, supported types of form fields are: 'String', 'Text', 'Number', 'Radio', 'Date'. More information about the available parameters can be found inAPI documentationor by looking atcreate_form.pysource code.# Creating a new formfields=[{"name":"A String Field","type":"String","defaultValue":"An optional default value"}]client.create_form('Test Form',tags=['testing','example'],fields=fields)Form information can be retrieved by sending a GET request to/forms/{formId}where formId is a numerical ID of a form. Python client accepts both numerical IDs and global IDs.# Getting information about a formresponse=client.get_form('FM3')# or client.get_form(3)print('Retrieved information about a form:',response['globalId'],response['name'])print('Fields:')forfieldinresponse['fields']:print(field['type'],field['name'])A newly created form is not available to create documents from until it has been published. Sending a POST request to/forms/{formId}/publishpublishes a form.# Publishing form FM123client.publish_form('FM123')# or client.publish_form(123)# Unpublish the formclient.unpublish_form('FM123')# or client.unpublish_form(123)It is possible to share a form with your groups. Once it is shared theaccessControl.groupPermissionTypeproperty isREAD.# Sharing form FM123client.share_form('FM123')# Unsharing the formclient.unshare_form('FM123')ExportYou can programmatically export your work in HTML or XML format. This might be useful if you want to make scheduled backups, for example. If you're an admin or PI you can export a particular user's work if you have permission.You can also export specific documents, notebooks or folders.Notethis requires RSpace 1.69.19 or later.Because export can be quite time-consuming, this is an asynchronous operation. On initial export you will receive a link to a job that you can query for progress updates. When the export has completed there will be a link to access the exported file - which may be very large.This Python API client provides an easy to use method that handles starting an export, polling the job's status and downloading the exported archive once it's ready. For example, to export current user's work in XML format:export_archive_file_path=client.download_export('xml','user',file_path='/tmp')or to export a group's work in HTML by id, passing an appendable file to record progress (requires >= 2.5):group_id=12345export_archive_file_path=client.download_export('html','group',uid=group_id,file_path='/tmp',progress_log="a-writeable-file.log")There arestart_export(self, format, scope, id=None)andget_job_status(self, job_id)functions to start the export and check its status as well. When a job is complete, the response contains a download link that can be accessed directly, without making an API call:{
 'id': 1595,
 'status': 'COMPLETED',
 'result': {'expiryDate': '2022-03-12T18:27:52.261Z',
  'size': 304694398,
  'checksum': '20038299',
  'algorithm': 'CRC32'},
 'percentComplete': 100.0,
 '_links': [{'link': 'https://community.researchspace.com/api/v1/export/RSpace-2022-06-11-18-28-html-123456.zip',
   'rel': 'enclosure'}]
}