From d38c2ae36df471bfd26d055efd073af3ea58cac7 Mon Sep 17 00:00:00 2001
From: szh2425 <33664901+szh2425@users.noreply.github.com>
Date: Fri, 3 Aug 2018 00:08:41 -0400
Subject: [PATCH] Issue166 redcap form status (#43)
* adding completion field to metadata
* added a bunch of print statements
* convert metadata to string then back to json to add field
* metadata from json to string, add field, back to json
* fixed json format
* reading which forms are completed in record set
* adding field to meta in form builder, adding findcompletedforms function to driver
* colors show up
* find form completion for nonlongtudinal studies added
* fixed spacing and added comments
* moved function calls back to top of class due to 'before assignment' error
* deleting methods that were created to find completed forms but are now obsolete
* method find completed forms longitudinal is reformualted for quicker runtime
* method for finding completed forms on longitudinal studies was redone
* removed extra spacing and unneccesary comments and function
* fixed spacing
* fixed spacing
* fixed spacing
* fixed spacing
* modified find completed forms nonlongitudinal to improve runtime
* separated find redcap completion form methods into a new method outside of subrecordgenerationform
* fixed spacing
* removing unnecessary changes
* fix spacing
* removing unnecessary arguement in subrecordselectionform
* reorder arguements to match unit tests
* removed unnecessary arguement from formbuilder constructform
* seperated out add completion field in formbuilder to new function
* adding assert to construct form to assert that completion field was added to metadata
* added 3 unit tests to test button colors, and finding completion fields for long and nonlong
* added payload for testing find_completion_codes_nonlong and modified completion field in one of the response payloads to indicate unverified or complete
* spacing at top
* using .format() for coloring of buttons, and adding icons for color blind
* renaming i,j,l for creating table
* combining codes for nonlong and long studies for method find_completed_form
* deprecated function updated
* rename function
* finding the field for study id in driver config instead of hardcoding
* created more generic add field to form function that can be used beyond this project
* adding json obj to meta instead of string
* removing unecessary arguements
* moving find completion codes to brp
* spacing
* spacing
* new test for method add_new_field_to_form
* reverting changes to original file because values are no longer needed
* added assertions and took away unit tests because methods were moved to brp
* spacing
* removing unused libraries
* using join instead of lambda
---
ehb_datasources/drivers/redcap/driver.py | 101 ++++++++++--------
.../drivers/redcap/formBuilderJson.py | 38 +++++++
.../tests/unit_tests/test_form_builder.py | 7 ++
.../tests/unit_tests/test_redcap_driver.py | 22 ++++
4 files changed, 122 insertions(+), 46 deletions(-)
diff --git a/ehb_datasources/drivers/redcap/driver.py b/ehb_datasources/drivers/redcap/driver.py
index 50c8861..9de7b84 100644
--- a/ehb_datasources/drivers/redcap/driver.py
+++ b/ehb_datasources/drivers/redcap/driver.py
@@ -604,7 +604,9 @@ def configure(self, driver_configuration='', *args, **kwargs):
self.form_event_data = kwargs.pop('form_event_data', None)
self.form_names = kwargs.pop('form_names', None)
- def subRecordSelectionForm(self, form_url='', *args, **kwargs):
+
+ def subRecordSelectionForm(self, form_url='', redcap_form_complete_codes={}, *args, **kwargs):
+
'''
Generates the REDCap data entry table.
@@ -629,27 +631,68 @@ def counter(start):
yield start
start += 1
+ # method to identify button color and icon
+ # based on form completion status
+ def get_button_icon(key):
+ all_form_status = redcap_form_complete_codes
+ button_icon=[]
+ try:
+ if all_form_status[key] == 1: # form is unverified
+ button_icon.append('btn-warning')
+ button_icon.append('fa-adjust')
+ return button_icon
+ elif all_form_status[key] == 2: # form is complete
+ button_icon.append('btn-success')
+ button_icon.append('fa-circle')
+ return button_icon
+ except: # form is incomplete
+ button_icon.append('btn-primary')
+ button_icon.append('fa-circle-o')
+ return button_icon
+
if self.form_names:
# The project is not longitudinal
- def makeRow(fn, i):
- row = '
'
return form
@@ -712,7 +722,6 @@ def subRecordForm(self, external_record, form_spec='', *args, **kwargs):
The form and event numbers are mapped to form names and event names in
the order they were provided in the call to configure
-
If the REDCap project is not longitudinal (i.e. Survey or Data Forms
Classic) the event number is not required and will be ignored if
included
diff --git a/ehb_datasources/drivers/redcap/formBuilderJson.py b/ehb_datasources/drivers/redcap/formBuilderJson.py
index 24ca844..613d9da 100644
--- a/ehb_datasources/drivers/redcap/formBuilderJson.py
+++ b/ehb_datasources/drivers/redcap/formBuilderJson.py
@@ -20,6 +20,36 @@ class redcapTemplate(Template):
class FormBuilderJson(object):
+ # this method can be used to add a field not already defined in meta data to all forms
+ def add_new_field_to_form (self, meta, field_name="", form_name="", field_type="", field_label="", select_choices_or_calculations="",
+ section_header="", field_note="", text_validation_type_or_show_slider_number="",
+ text_validation_min="",text_validation_max="",identifier="", branching_logic="",
+ required_field="", custom_alignment="", question_number="", matrix_group_name="",
+ matrix_ranking="", field_annotation=""):
+ new_field = {}
+ new_field["field_name"] = field_name
+ new_field["form_name"] = form_name
+ new_field["section_header"] = section_header
+ new_field["field_type"] = field_type
+ new_field ["field_label"] = field_label
+ new_field["select_choices_or_calculations"]= select_choices_or_calculations
+ new_field["field_note"] = field_note
+ new_field["text_validation_type_or_show_slider_number"] = text_validation_type_or_show_slider_number
+ new_field["text_validation_min"] = text_validation_min
+ new_field["text_validation_max"] = text_validation_max
+ new_field["identifier"] = identifier
+ new_field["branching_logic"] = branching_logic
+ new_field["required_field"] = required_field
+ new_field["custom_alignment"] = custom_alignment
+ new_field["question_number"] = question_number
+ new_field["matrix_group_name"] = matrix_group_name
+ new_field["matrix_ranking"]= matrix_ranking
+ new_field["field_annotation"] = field_annotation
+ new_field = json.dumps (new_field)
+ new_field = json.loads(new_field)
+ meta.append(new_field)
+ return meta
+
def construct_form(self, meta, record_set, form_name, record_id,
event_num=None, unique_event_names=None,
event_labels=None, session=None, record_id_field=None):
@@ -64,6 +94,14 @@ def construct_form(self, meta, record_set, form_name, record_id,
return '''
There was an error retrieving this record from REDCap
'''
+
+ # construct the field name for adding completion status to redcap forms
+ completion_field_name = form_name + "_complete"
+ # add completion field to all redcap forms
+ meta = self.add_new_field_to_form (meta, field_name=completion_field_name, form_name=form_name,
+ field_type="dropdown", field_label="Form Completion Status", select_choices_or_calculations="0, Incomplete | 1, Unverified | 2, Complete",
+ section_header="Form Status", required_field="y")
+
form_fields = [
item for item in meta if item.get("form_name") == form_name
]
diff --git a/ehb_datasources/tests/unit_tests/test_form_builder.py b/ehb_datasources/tests/unit_tests/test_form_builder.py
index 6d701dc..101bc13 100644
--- a/ehb_datasources/tests/unit_tests/test_form_builder.py
+++ b/ehb_datasources/tests/unit_tests/test_form_builder.py
@@ -20,6 +20,7 @@ def test_construct_form(form_builder, redcap_metadata_json, redcap_record_json):
assert '' in form
assert '' in form
assert '' in form
+ assert 'Form Completion Status' in form
def test_construct_form2_branch_logic_functions(form_builder, redcap_metadata_json2, redcap_record_json2):
form = form_builder.construct_form(
@@ -84,3 +85,9 @@ def test_construct_form_bad_redcap_record(form_builder, redcap_metadata_json2):
'study_id'
)
assert 'There was an error retrieving this record from REDCap' in form
+
+def test_add_new_field_to_form(form_builder, redcap_metadata_json):
+ metadata_json = json.loads(redcap_metadata_json.decode('utf-8'))
+ new_field = form_builder.add_new_field_to_form(metadata_json, field_name="test_new_field")
+ last_index = len(new_field)-1
+ assert 'test_new_field' in new_field[last_index]['field_name']
diff --git a/ehb_datasources/tests/unit_tests/test_redcap_driver.py b/ehb_datasources/tests/unit_tests/test_redcap_driver.py
index 5e1ef77..687b840 100644
--- a/ehb_datasources/tests/unit_tests/test_redcap_driver.py
+++ b/ehb_datasources/tests/unit_tests/test_redcap_driver.py
@@ -788,3 +788,25 @@ def test_write_records_badresp(mocker, driver, redcap_payload):
{'Content-Type': 'application/x-www-form-urlencoded', 'Accept': 'text/xml'},
'content=record&data=%3Crecords%3E%3Citem%3E%3Cstudy_id%3E%3C%21%5BCDATA%5B0GUQDBCDE0EAWN9Q%3A8LAG76CHO%5D%5D%3E%3C%2Fstudy_id%3E%3Credcap_event_name%3E%3C%21%5BCDATA%5Bvisit_arm_1%5D%5D%3E%3C%2Fredcap_event_name%3E%3Ccolonoscopy_date%3E%3C%21%5BCDATA%5B2016-08-31%5D%5D%3E%3C%2Fcolonoscopy_date%3E%3Cgeneral_ibd%3E%3C%21%5BCDATA%5B2016-08-31%5D%5D%3E%3C%2Fgeneral_ibd%3E%3Ctransferrin_b%3E%3C%21%5BCDATA%5B102%5D%5D%3E%3C%2Ftransferrin_b%3E%3Cmeds___2%3E%3C%21%5BCDATA%5B0%5D%5D%3E%3C%2Fmeds___2%3E%3Cmeal_date%3E%3C%21%5BCDATA%5B2016-08-31%5D%5D%3E%3C%2Fmeal_date%3E%3Culcerative_colitis%3E%3C%21%5BCDATA%5B2016-08-31%5D%5D%3E%3C%2Fulcerative_colitis%3E%3Cmeds___1%3E%3C%21%5BCDATA%5B1%5D%5D%3E%3C%2Fmeds___1%3E%3Ccomments%3E%3C%21%5BCDATA%5BTest+Data%5D%5D%3E%3C%2Fcomments%3E%3Cweight%3E%3C%21%5BCDATA%5B20%5D%5D%3E%3C%2Fweight%3E%3Cchrons%3E%3C%21%5BCDATA%5B2016-08-31%5D%5D%3E%3C%2Fchrons%3E%3Cchol_b%3E%3C%21%5BCDATA%5B101%5D%5D%3E%3C%2Fchol_b%3E%3Ccolonoscopy%3E%3C%21%5BCDATA%5B0%5D%5D%3E%3C%2Fcolonoscopy%3E%3Cprealb_b%3E%3C%21%5BCDATA%5B19%5D%5D%3E%3C%2Fprealb_b%3E%3Cheight%3E%3C%21%5BCDATA%5B100%5D%5D%3E%3C%2Fheight%3E%3Ccreat_b%3E%3C%21%5BCDATA%5B0.6%5D%5D%3E%3C%2Fcreat_b%3E%3Cibd_flag%3E%3C%21%5BCDATA%5B1%5D%5D%3E%3C%2Fibd_flag%3E%3Cmeds___5%3E%3C%21%5BCDATA%5B0%5D%5D%3E%3C%2Fmeds___5%3E%3Cmeds___4%3E%3C%21%5BCDATA%5B0%5D%5D%3E%3C%2Fmeds___4%3E%3Cmeds___3%3E%3C%21%5BCDATA%5B0%5D%5D%3E%3C%2Fmeds___3%3E%3C%2Fitem%3E%3C%2Frecords%3E&format=xml&overwriteBehavior=overwrite&token=foo&type=flat'
)
+
+
+def test_srsf_redcap_completion_codes(mocker, driver, driver_configuration_long, redcap_metadata_json, redcap_record_json):
+ # Mocks
+ # Metadata request
+ driver.meta = mocker.MagicMock(return_value=redcap_metadata_json)
+ driver.configure(driver_configuration_long)
+ # Record Request
+ MockREDCapResponse = mocker.MagicMock(
+ spec=HTTPResponse,
+ status=200)
+ MockREDCapResponse.read = mocker.MagicMock(return_value=redcap_record_json)
+ driver.POST = mocker.MagicMock(return_value=MockREDCapResponse)
+
+ redcap_form_complete_codes = {}
+ redcap_form_complete_codes[(str(1)+"_"+str(3))] = 2
+ redcap_form_complete_codes[(str(0)+"_"+str(0))] = 1
+ form = driver.subRecordSelectionForm(form_url='/test/', redcap_form_complete_codes=redcap_form_complete_codes)
+ assert 'btn-success' in form
+ assert 'fa-circle' in form
+ assert 'btn-warning' in form
+ assert 'fa-adjust' in form