Skip to content

Commit

Permalink
Changes to show redcap error messages (#39)
Browse files Browse the repository at this point in the history
* added error message to the recordcreationerror exception

* raise exception to read error message

* processResponse method changed to process RedCap's error message

* checking to see if prior redcap error messages exist so they don't get printed again

* create the error message to be displayed

* changed unit test

* created method fieldValidation and added redcap validation map

* showing the validation notes on the table

* changing Y->y to show isRequired note on table

* is required y  OR Y

* created separate process response that is redcap specific

* processresponse moved to driver.py in redcap

* added class to header

* getting rid of stub

* spacing

* ehb_datasources/drivers/Base.py

* spacing

* changed description for time mm:ss and zipcode
  • Loading branch information
stxphcodes authored Dec 22, 2017
1 parent fb176e5 commit ce6bffc
Show file tree
Hide file tree
Showing 4 changed files with 129 additions and 19 deletions.
2 changes: 0 additions & 2 deletions ehb_datasources/drivers/Base.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,10 @@
import logging
import re
import urllib.request, urllib.parse, urllib.error

import xml.dom.minidom as xml

log = logging.getLogger('ehb_datasources')


class Driver(object, metaclass=ABCMeta):
'''
Abstract electronic honest broker (ehb) datasource driver class
Expand Down
65 changes: 60 additions & 5 deletions ehb_datasources/drivers/redcap/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
from jinja2 import Template

from ehb_datasources.drivers.exceptions import PageNotFound,\
ImproperArguments
ImproperArguments, ServerError
from ehb_datasources.drivers.Base import Driver, RequestHandler
from ehb_datasources.drivers.exceptions import RecordDoesNotExist,\
RecordCreationError
Expand Down Expand Up @@ -258,6 +258,59 @@ def read_metadata(self, _format=FORMAT_JSON, headers=STANDARD_HEADER,
else:
return self.transformResponse(_format, response)

# overriding method from Base.py in order to
# clean and parse redcap error message
def processResponse (self, response, path =''):
status = response.status
if status == 200:
return self.readAndClose(response)
elif status == 201:
return self.readAndClose(response)
elif status == 400:
#this means the data being imported isn't formatted correctly
#redcap api will return a message
msg = response.read()
#xml to string
msg = msg.decode ("utf-8")
#for more than one error, errors are separated by \n
if "\n" in msg:
msg=msg.split('\n')
msgShow =''
for error in msg:
error=error.split(',')
#there are 3 parts to error message returned by redcap
#[0] -> user's name
#[1] -> field name
#[2] -> user input
#[3] -> error message
msgShow += "You entered " + error[2] + " for the field " + error[1]+ ". " + error [3] + "<br><br>"
self.closeConnection()
raise Exception (msgShow)
else:
#there is only one error message
msg = msg.split(',')
#there are 3 parts to error message returned by redcap
#[0] -> user's name
#[1] -> field name
#[2] -> user input
#[3] -> error message
self.closeConnection()
raise Exception("You entered " + msg[2] + " for the field " + msg[1]+ ". " + msg [3])
elif status == 406:
msg = "The data being imported was formatted incorrectly"
self.closeConnection()
raise Exception(msg)
elif status == 404:
self.closeConnection()
raise PageNotFound(path=path)
elif status == 500:
self.closeConnection()
raise ServerError
else:
self.closeConnection()
msg = 'Unknown response code from server: {}'.format(status)
raise Exception(msg)


class ehbDriver(Driver, GenericDriver):

Expand Down Expand Up @@ -468,7 +521,10 @@ def validate_id(pid):
data=xml.parseString(record),
overwrite=overwrite
):
raise RecordCreationError(self.url, self.path, study_id, 'Unknown')
errors = self.write_records(
data=xml.parseString(record),
overwrite=overwrite)
raise RecordCreationError(self.url, self.path, study_id, errors)
else:
return study_id

Expand Down Expand Up @@ -939,6 +995,5 @@ def make_data_entry_from_session(field_name, field_dict):
):
return ['Unknown error. REDCap reports multiple records were' +
'updated, should have only been 1.']
except Exception:
return ['Parse error. REDCap response is an unknown format.' +
' Please contact system administrator.']
except Exception as error:
return repr(error)
78 changes: 68 additions & 10 deletions ehb_datasources/drivers/redcap/formBuilderJson.py
Original file line number Diff line number Diff line change
Expand Up @@ -224,7 +224,22 @@ def construct_form(self, meta, record_set, form_name, record_id,
success: function(data){
if(data.status == 'error'){
$("#pleaseWaitModal").modal('hide');
// Check to see if error messages exist prior
// to remove them
var elementExists = document.getElementById("errorMsg");
if (elementExists) {
var element = document.getElementById("errorMsg");
element.parentNode.removeChild(element);
}
else {
}
$("#errorModal").modal('show');
$("#errorClass").append("<div id='errorMsg'>" + "<p style='color:#F08080'> <br>[ REDCAP ERROR MESSAGE ] <br>" + data.errors + "</p>" + "</div>");
}
else{
if(next_form_url != ""){
Expand Down Expand Up @@ -359,13 +374,55 @@ def table_rows(self, meta, record, form_name, master_dep_map, apriori_branch_eva

def make_tr_for(self, field, record, master_dep_map, apriori_branch_evals):
def isRequired():
if field.get('required_field') and field.get('required_field')=='Y': return '* must provide value'
if field.get('required_field') and field.get('required_field')=='y': return '* must provide value'
elif field.get('required_field') and field.get('required_field')=='Y': return '* must provide value'
else: return ''

def fieldNote():
if field.get('field_note'): return field.get('field_note')
else: return ''

def fieldValidation():
redcap_field_validation_map = {
'date_dmy': 'YYYY-MM-DD.',
'date_mdy': 'YYYY-MM-DD. ',
'date_ymd': 'YYYY-MM-DD.',
'datetime_dmy': 'YYYY-MM-DD HH:MM (time is in military format)',
'datetime_ymd': 'YYYY-MM-DD HH:MM (time is in military format)',
'datetime_mdy': 'YYYY-MM-DD HH:MM (time is in military format)',
'datetime_seconds_mdy': 'YYYY-MM-DD HH:MM:SS (time is in military format)',
'datetime_seconds_ymd': 'YYYY-MM-DD HH:MM:SS (time is in military format) ',
'datetime_seconds_dmy': 'YYYY-MM-DD HH:MM:SS (time is in military format) ',
'email': '[email protected]',
'integer': 'whole number (no letters or decimal)',
'alpha_only': 'letters (no numbers)',
'mrn_8d': 'medical record number (8 digits)',
'number': 'number (no letters)',
'number_1dp': 'number with 1 decimal place #.#',
'number_2dp': 'number with 2 decimal place #.##',
'number_3dp': 'number with 3 decimal place #.###',
'number_4dp': 'number with 4 decimal place #.####',
'phone': '10 digit phone number (###)###-####',
'ssn': '9 digit social security number ###-##-####',
'text_0_150': 'characters (max. 150)',
'text_0_200': 'characters (max. 200)',
'time': 'time HH:MM (military format)',
'time_mm_ss': 'time MM:SS',
'zipcode': '5 or 9 digit zipcode (#####-####)'
}

#if validation note exists for the field
if field.get('text_validation_type_or_show_slider_number'):
#loop through the validation map to find the correct validation msg to show
for validation_key, validation_message in redcap_field_validation_map.items():
if field.get('text_validation_type_or_show_slider_number') == str(validation_key):
return "field value expected is " + str(validation_message)
#validation doesn't match with any of the keys above.
#print the validation note anyways
return "field value expected is: " + field.get('text_validation_type_or_show_slider_number')
#no validation note exists
else: return ''

section_header = field.get('section_header')
header = ''
radio_reset = ''
Expand All @@ -379,16 +436,17 @@ def fieldNote():
return """{0}
<tr id="{5}" {6}>
<td><div>{1}</div><div style="color:red; font-size:12px;">{2}</div></td>
<td><div>{3}</div><div style="color:blue; font-size:12px;">{4}</div>{7}</td>
<td><div>{3}</div><div style="color:blue; font-size:12px;">{4}</div><div style="color:grey; font-size:10px;">{8}</div>{7}</td>
</tr>
""".format(header,
field.get('field_label'),
isRequired(),
self.build_field(field, record, master_dep_map),
fieldNote(),
field.get('field_name'),
dis,
radio_reset
""".format(header, #{0}
field.get('field_label'), #{1}
isRequired(), #{2}
self.build_field(field, record, master_dep_map), #{3}
fieldNote(), #{4}
field.get('field_name'), #{5}
dis,#{6}
radio_reset,#{7}
fieldValidation() #{8}
)

def build_fld_on_change_function(self, fld_name, master_dep_map):
Expand Down
3 changes: 1 addition & 2 deletions ehb_datasources/tests/unit_tests/test_redcap_driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -549,8 +549,7 @@ def test_process_form_badrcresponse(mocker, driver, driver_configuration_long, r

driver.write_records = mocker.MagicMock(side_effect=ServerError)
errors = driver.processForm(request, external_record, form_spec='0_0')
assert 'Parse error. REDCap response is an unknown format. Please contact system administrator.' in errors

assert 'ServerError()' in errors

def test_process_form_bad_metadata_norecordid(mocker, driver, driver_configuration_long, redcap_metadata_xml, redcap_form_datastring):
# Mocks
Expand Down

0 comments on commit ce6bffc

Please sign in to comment.