# Contributors:
# Christopher P. Barnes <senrabc@gmail.com>
# Andrei Sura: github.com/indera
# Mohan Das Katragadda <mohan.das142@gmail.com>
# Philip Chase <philipbchase@gmail.com>
# Ruchi Vivek Desai <ruchivdesai@gmail.com>
# Taeber Rapczak <taeber@ufl.edu>
# Nicholas Rejack <nrejack@ufl.edu>
# Josh Hanna <josh@hanna.io>
# Copyright (c) 2015, University of Florida
# All rights reserved.
#
# Distributed under the BSD 3-Clause License
# For full text of the BSD 3-Clause License see http://opensource.org/licenses/BSD-3-Clause
import logging
import time
import sys
from redcap import Project, RedcapError
from requests import RequestException
from requests.packages.urllib3.exceptions import MaxRetryError
# Configure module's logger
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
[docs]class RedcapClient(object):
""" Client for a REDCap server.
:param redcap_uri: URI for to REDCap server's API
:param token: API Token for a REDCap project.
:param verify_ssl: verify the SSL certificate? (default: True)
:raises RedcapError: if we failed to get the project's metadata
:raises RequestException: if some other network-related failure occurs
"""
def __init__(self, redcap_uri, token, verify_ssl=True):
self.redcap_uri = redcap_uri
msg = 'Initializing redcap interface for: ' + redcap_uri
logger.info(msg)
self.token = token
self.verify_ssl = verify_ssl
try:
self.project = Project(redcap_uri, token, "", verify_ssl)
logger.info("redcap interface initialzed")
except (RequestException, RedcapError) as e:
logger.exception(e.message)
raise
[docs] def get_data_from_redcap(
self,
records_to_fetch=None,
events_to_fetch=None,
fields_to_fetch=None,
forms_to_fetch=None,
return_format='xml'):
""" Exports REDCap records.
:param records_to_fetch: if specified, only includes records in this
list. Otherwise, includes all records.
:type records_to_fetch: list or None
:param events_to_fetch: if specified, only includes events in this list.
Otherwise, includes all events.
:type events_to_fetch: list or None
:param fields_to_fetch: if specified, only includes fields in this list.
Otherwise, includes all fields
:type fields_to_fetch: list or None
:param forms_to_fetch: if specified, only includes forms in this list.
Otherwise, includes all forms.
:type forms_to_fetch: list or None
:param return_format: specifies the format of the REDCap response
(default: xml)
:return: response
"""
logger.info('getting data from redcap')
try:
response = self.project.export_records(
records=records_to_fetch,
events=events_to_fetch,
fields=fields_to_fetch,
forms=forms_to_fetch,
format=return_format)
except RedcapError as e:
logger.debug(e.message)
return response
[docs] def send_data_to_redcap(self, data, max_retry_count, overwrite=False,
retry_count=0):
""" Sends records to REDCap.
:param list of dict objects data: records to send.
:param bool overwrite: treat blank values as intentional?
(default: False) When sending a record, if a field is blank, by
default REDCap will not overwrite any existing value with a blank.
:return: response
:raises RedcapError: if failed to send records for any reason.
:If MaxRetryError is caught, the function will try resending the same
data for a maximum of max_retry_count times before exitting. For each
attempt the wait time before sending is (the_attempt_no * 6)
"""
overwrite_value = 'overwrite' if overwrite else 'normal'
try:
# The following line simulates github issue #108:
# raise MaxRetryError('', 'localhost:8998', None)
response = self.project.import_records(data,
overwrite=overwrite_value)
return response
except MaxRetryError as e:
logger.debug(e.message + ", Attempt no.: " + str(retry_count))
if (retry_count == max_retry_count):
message = "Exiting since network connection timed out after"\
" reaching the maximum retry limit for resending data."
logger.debug(message)
sys.exit(message)
# wait for some time before resending data
time.sleep(retry_count*6)
self.send_data_to_redcap(data, max_retry_count, 'overwrite',
retry_count+1)
except RedcapError as e:
logger.debug(e.message)
raise