From 22ea23c72301e690e6d3f35ccd73931b255468a6 Mon Sep 17 00:00:00 2001 From: Tyler Rivera Date: Wed, 8 Apr 2015 10:33:18 -0400 Subject: [PATCH 1/2] Add option to lazily initialize Project objects There are situations where I'd like to make programmatic calls to a project ad-hoc but not have to pull metadata for the project on every initialization. A proposed solution would be to provide an additional keyword argument on the Project object lazy which would default to False (to preserve current behavior). Setting lazy to True would result in the Project being initialized without the metadata being pulled. Users can still export/import records and metadata, it would just be done on demand. Signed off by: Tyler Rivera tyler.rivera@gmail.com Fixes #52 --- docs/deep.rst | 2 ++ redcap/project.py | 41 +++++++++++++++++++++++++++++------------ redcap/request.py | 2 +- 3 files changed, 32 insertions(+), 13 deletions(-) diff --git a/docs/deep.rst b/docs/deep.rst index 6865f25..47cc1b9 100644 --- a/docs/deep.rst +++ b/docs/deep.rst @@ -48,6 +48,8 @@ When creating a ``Project`` object, PyCap goes ahead and makes some useful API c For non-longitudinal projects, ``events``, ``arm_nums``, and ``arm_names`` are empty tuples. +:note: To disable calls to the API on initialization you can set ``lazy=True``. + Metadata ^^^^^^^^ diff --git a/redcap/project.py b/redcap/project.py index 89ca83b..96aba41 100644 --- a/redcap/project.py +++ b/redcap/project.py @@ -14,7 +14,7 @@ class Project(object): """Main class for interacting with REDCap projects""" - def __init__(self, url, token, name='', verify_ssl=True): + def __init__(self, url, token, name='', verify_ssl=True, lazy=False): """ Parameters ---------- @@ -32,6 +32,22 @@ def __init__(self, url, token, name='', verify_ssl=True): self.name = name self.url = url self.verify = verify_ssl + self.metadata = None + self.redcap_version = None + self.field_names = None + # We'll use the first field as the default id for each row + self.def_field = None + self.field_labels = None + self.forms = None + self.events = None + self.arm_nums = None + self.arm_names = None + self.configured = False + + if not lazy: + self.configure() + + def configure(self): try: self.metadata = self.__md() except RequestException: @@ -59,6 +75,7 @@ def __init__(self, url, token, name='', verify_ssl=True): self.events = events self.arm_nums = arm_nums self.arm_names = arm_names + self.configured = True def __md(self): """Return the project's metadata structure""" @@ -212,10 +229,10 @@ def export_metadata(self, fields=None, forms=None, format='json', return read_csv(StringIO(response), **df_kwargs) def export_records(self, records=None, fields=None, forms=None, - events=None, raw_or_label='raw', event_name='label', - format='json', export_survey_fields=False, - export_data_access_groups=False, df_kwargs=None, - export_checkbox_labels=False): + events=None, raw_or_label='raw', event_name='label', + format='json', export_survey_fields=False, + export_data_access_groups=False, df_kwargs=None, + export_checkbox_labels=False): """ Export data from the REDCap project. @@ -279,11 +296,11 @@ def export_records(self, records=None, fields=None, forms=None, pl = self.__basepl('record', format=ret_format) fields = self.backfill_fields(fields, forms) keys_to_add = (records, fields, forms, events, - raw_or_label, event_name, export_survey_fields, - export_data_access_groups, export_checkbox_labels) + raw_or_label, event_name, export_survey_fields, + export_data_access_groups, export_checkbox_labels) str_keys = ('records', 'fields', 'forms', 'events', 'rawOrLabel', - 'eventName', 'exportSurveyFields', 'exportDataAccessGroups', - 'exportCheckboxLabel') + 'eventName', 'exportSurveyFields', 'exportDataAccessGroups', + 'exportCheckboxLabel') for key, data in zip(str_keys, keys_to_add): if data: # Make a url-ok string @@ -307,7 +324,7 @@ def export_records(self, records=None, fields=None, forms=None, return df def metadata_type(self, field_name): - """If the given field_name is validated by REDCap, return its type""" + """If the given field_name is validated by REDCap, return it's type""" return self.__meta_metadata(field_name, 'text_validation_type_or_show_slider_number') @@ -393,7 +410,7 @@ def names_labels(self, do_print=False): return self.field_names, self.field_labels def import_records(self, to_import, overwrite='normal', format='json', - return_format='json', return_content='count', rec_type='flat', + return_format='json', return_content='count', date_format='YMD'): """ Import data into the RedCap Project @@ -433,7 +450,7 @@ def import_records(self, to_import, overwrite='normal', format='json', response : dict, str response from REDCap API, json-decoded if ``return_format`` == ``'json'`` """ - pl = self.__basepl('record', rec_type=rec_type) + pl = self.__basepl('record') if hasattr(to_import, 'to_csv'): # We'll assume it's a df from StringIO import StringIO diff --git a/redcap/request.py b/redcap/request.py index 8f40399..1ede59c 100644 --- a/redcap/request.py +++ b/redcap/request.py @@ -85,7 +85,7 @@ def validate(self): 'Exporting form-event mappings but content != formEventMapping'), 'exp_user': (['format'], 'user', 'Exporting users but content is not user'), - 'version': (['format'], 'version', + 'version': (['format'], 'version', 'Requesting version but content != version') } extra, req_content, err_msg = valid_data[self.type] From cf124c5f331923a43e8ac6832e3aca513dd93f8f Mon Sep 17 00:00:00 2001 From: Tyler Rivera Date: Thu, 9 Apr 2015 09:47:59 -0400 Subject: [PATCH 2/2] Additional clarification of lazy keyword arg in documentation --- docs/deep.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/deep.rst b/docs/deep.rst index 47cc1b9..ede5c1f 100644 --- a/docs/deep.rst +++ b/docs/deep.rst @@ -48,7 +48,7 @@ When creating a ``Project`` object, PyCap goes ahead and makes some useful API c For non-longitudinal projects, ``events``, ``arm_nums``, and ``arm_names`` are empty tuples. -:note: To disable calls to the API on initialization you can set ``lazy=True``. +:note: To disable calls to the API on initialization you can set ``lazy=True``. This prevents much of the metadata associated with a project from being requested at `Project` initialization. Accessing these values prior to `Project.configure()` being run will result in the return of Null values. This might be useful if you're implementing PyCap into a service-based architecture. For example, On each request to the service you might want to initialize a different Project with each request to the service and use PyCap's methods to pull records without having to initialize the project's entire metadata on each service request. Metadata ^^^^^^^^