Skip to content
This repository has been archived by the owner on Oct 12, 2023. It is now read-only.

Commit

Permalink
Merge branch 'release/0.11.3'
Browse files Browse the repository at this point in the history
  • Loading branch information
ufl-taeber committed Sep 23, 2014
2 parents 01afebe + d4a3770 commit 712bfd6
Show file tree
Hide file tree
Showing 59 changed files with 10,833 additions and 553 deletions.
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
*.pyc
*.pyo
REDI.egg-info/
config-example/vagrant-data/redcap.zip
formData.xml
rawData.xml
translationalData.xml
Expand All @@ -14,6 +15,10 @@ rawDataWithFormStatus.xml
!log/.dummy
log/
config/
config-uf/
config-surgery/
config-upenn/
config-vcu/
.project
*.swp
*.swo
Expand All @@ -23,6 +28,9 @@ person_form_event_tree_with_data.xml
.figleaf
build/
dist/
vagrant/Makefile.ini
vagrant/projectDataBootstrap.sql
vagrant/out.csv
vagrant/.vagrant/
vagrant/.vimrc
vagrant/report.html
Expand All @@ -32,8 +40,13 @@ vagrant/redcap-backup-*.sql
vagrant/REDI-*.egg

vagrant/redcap.zip
vagrant/projectDataBootstrap.sql
vagrant/redcap_database.sql
vagrant/sqlPatches
vagrant/data/
vagrant/demographic_test_data.csv
vagrant/demographic_test_data.json
vagrant/redi.db
.idea/
data/
redi.db
report.html
31 changes: 31 additions & 0 deletions CHANGELOG
Original file line number Diff line number Diff line change
@@ -1,3 +1,34 @@
2014-09-23 v0.11.3

* Summary: Update config-example to work with new sample project

* Fixed #73: Change use of redcap_server and redcap_uri settings
* Fixed #72: Remove circular dependency redi <--> redi_lib
* Fixed #68: Sample REDCap project is tied to a specific REDCap version number
* Fixed #64: Create a RED-I config to match the example REDCap project.
* Fixed #63: Add documentation within config-example/settings.ini to explain the use of each parameter
* Fixed #62: Regroup settings within settings.ini
* Fixed #60: Make a test data set of CBC and Chemistry data
* Fixed #59: Revise report sending code to note the locations of reports/email bodies in the console output
* Fixed #58: Remove project specific details from vagrant/Makefile
* Fixed #49: "Exceeded event list... Event count of 11 exceeds maximum of 11"
* Fixed #47: Do not hardcode smtp host/port
* Fixed #27: Update /vagrant/README.md to match new features
* Fixed #4: Create a working example REDCap Project suitable for RED-I Demonstration
* Fixed #3: No config-example folder

* Rename README_Travis_CI_Setup.md to `setup_travis_ci.md` Also move the related images to `images/setup_travis_ci`
* Cut the section about adding a new REDCap project from vagrant/README.md This resulted in creation of a new document: doc/add_new_redcap_project.md and associated files in dedicated folder: `images/add_new_redcap_project/`
* Remove deprecated document: `doc/README_test_against_redcap.md`
* Delete empty file: `vagrant/README-projects.md`

* Added scripts/compare_settings.sh to help find differences Example usage: ./compare_settings.sh ../config/settings.ini ../config-vcu/settings.ini
* Add clinical-commit-to-loinc.xml helper scripts
* Add synthetic_data tool, makefakedata.R
* Improve `bin/utils/redcap_records.py` - now works with json/xml

* Log information about rules loading

2014-09-10 v0.11.2

* Summary: performance improvements to speed up run time.
Expand Down
1 change: 0 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -45,5 +45,4 @@ clean:
rm -f person_form_event_tree.xml
rm -f person_form_event_tree_with_data.xml
rm -rf vagrant/data/
rm -f vagrant/demographic_test_data.csv
rm -f vagrant/redi.db
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,12 @@ The program will terminate if they are missing or do not have a value in **setti

### Conditional Parameters

Whereas the aforementioned parameters are always required, the following parameters are only required to have a value in **settings.ini** when **redi** is not performing a dry run:
While the parameters mentioned above are always required, the following
parameters are only required to have a value in **settings.ini** when
**redi** is not performing a dry run:

- redcap_uri
- token
- redcap_server
- redcap_support_receiver_email
- redcap_support_sender_email
- smtp_host_for_outbound_mail
Expand Down
102 changes: 57 additions & 45 deletions bin/redi.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
__author__ = "Nicholas Rejack"
__copyright__ = "Copyright 2013, University of Florida"
__license__ = "BSD 2-Clause"
__version__ = "0.11.1"
__version__ = "0.11.3"
__email__ = "[email protected]"
__status__ = "Development"

Expand All @@ -21,7 +21,6 @@
from collections import defaultdict
from collections import Counter
import string
import smtplib
import xml.etree.ElementTree as ET
import sys
import imp
Expand Down Expand Up @@ -246,7 +245,7 @@ def _run(config_file, configuration_directory, do_keep_gen_files, dry_run,
report_parameters = {
'report_file_path': report_file_path,
'project': settings.project,
'redcap_server': settings.redcap_server}
'redcap_uri': settings.redcap_uri}

report_xsl = proj_root + "bin/utils/report.xsl"
send_email = settings.send_email
Expand Down Expand Up @@ -292,27 +291,17 @@ def _run(config_file, configuration_directory, do_keep_gen_files, dry_run,
xml_report_tree = create_summary_report(report_parameters,
report_data, alert_summary,
collection_date_summary_dict)
# print ElementTree.tostring(xml_report_tree)

# print etree.tostring(xml_report_tree)
report_xsl = proj_root + "bin/utils/report.xsl"
xslt = etree.parse(report_xsl)
transform = etree.XSLT(xslt)
html_report = transform(xml_report_tree)
html_str = etree.tostring(html_report, method='html', pretty_print=True)

# send report via email
if settings.send_email:
sender = settings.sender_email
receiver = settings.receiver_email.split()
send_report(sender, receiver, html_str)
deliver_report_as_email(email_settings, html_str)
else:
logger.info("Email will not be sent as 'send_email' parameter"\
" in {0} is set to 'N'".format(config_file))
try:
report_file = open(settings.report_file_path2, 'w')
except IOError:
logger.exception('could not open file %s' % settings.report_file_path2)
raise
report_file.write(html_str)
deliver_report_as_file(settings.report_file_path2, html_str)

if batch:
# Update the batch row
Expand All @@ -327,6 +316,46 @@ def _run(config_file, configuration_directory, do_keep_gen_files, dry_run,
if not do_keep_gen_files:
redi_lib.delete_temporary_folder(data_folder)

def deliver_report_as_file(html_report_path, html):
"""
Deliver the summary report by writing it to a file
or logging it to the console if writing the file fails
:html_report_path the path where the report will be stored
:html the actual report content
"""
problem_found = False
try:
report_file = open(html_report_path, 'w')
except (IOError, OSError) as e:
logger.exception('Could not open file: %s' % html_report_path)
problem_found = True
else:
try:
report_file.write(html)
logger.info("==> You can review the summary report by opening: {}"\
" in your browser".format(html_report_path))
except IOError:
logger.exception('Could not write file: %s' % html_report_path)
problem_found = True
finally:
report_file.close()
if problem_found:
logger.info("== Summary report ==" + html)

def deliver_report_as_email(email_settings, html):
"""
Deliver summary report as an email
:email_settings dictinary with email parameters
:html the actual report content
"""
try:
redi_email.send_email_data_import_completed(email_settings, html)
logger.info("Summary report was emailed: parameter 'send_email = Y'")
except Exception as e:
logger.error("Unable to deliver the summary report due error: %s" % e)
deliver_report_as_file("report.html", html)

def _create_person_form_event_tree_with_data(config_file, \
configuration_directory, email_settings, form_events_file, raw_xml_file,\
Expand Down Expand Up @@ -998,7 +1027,7 @@ def update_event_name(data, lookup_data, undefined):
# issue a warning

if old_form_name is not "dummy" and \
event_index >= len(lookup_table[old_form_name]):
event_index > len(lookup_table[old_form_name]):
max_event_alert.append("Exceeded event list for record "\
"group with Subject ID.: " + last_study_id + " and "\
"Form Name: " + last_form_name + ". Event count "\
Expand Down Expand Up @@ -1212,28 +1241,6 @@ def configure_logging(data_folder, verbose=False):
return logger


def send_report(sender, receiver, body):
"""Function to email the report of the redi run."""
from email.MIMEMultipart import MIMEMultipart
from email.MIMEText import MIMEText
msg = MIMEMultipart()
msg['From'] = sender
msg['To'] = ",".join(receiver)
msg['Subject'] = "Data Import Report"
msg.attach(MIMEText(body, 'html'))

"""
Sending email
"""

try:
smtpObj = smtplib.SMTP('smtp.ufl.edu', 25)
smtpObj.sendmail(sender, receiver, msg.as_string())
logger.info("Successfully sent email to: " + str(receiver))
except Exception:
logger.info("Error: unable to send report email to: " + str(receiver))


def create_summary_report(report_parameters, report_data, alert_summary, \
collection_date_summary_dict):
root = etree.Element("report")
Expand All @@ -1255,13 +1262,14 @@ def create_summary_report(report_parameters, report_data, alert_summary, \


def updateReportHeader(root, report_parameters):
""" Update the passed `root` element tree with date, project name and url"""
header = root[0]
project = etree.SubElement(header, "project")
project.text = report_parameters.get('project')
date = etree.SubElement(header, "date")
date.text = time.strftime("%m/%d/%Y")
redcapServerAddress = etree.SubElement(header, "redcapServerAddress")
redcapServerAddress.text = report_parameters.get('redcap_server')
redcapServerAddress.text = report_parameters.get('redcap_uri')


def updateReportSummary(root, report_data):
Expand Down Expand Up @@ -1914,6 +1922,7 @@ def run_rules(data):

loaded_rules[rule] = module

logger.info("Loaded %s post-processing rule(s)" % len(loaded_rules))
return loaded_rules


Expand Down Expand Up @@ -1969,7 +1978,7 @@ def verify_and_correct_collection_date(data, input_date_format):
continue
subject.remove(result_date_element)
if collection_date_summary_dict['blank'] > 0:
logger.info("There were {0} out of {1} blank specimen taken times "\
logger.debug("There were {0} out of {1} blank specimen taken times "\
"in this run.".format(collection_date_summary_dict['blank'],
collection_date_summary_dict['total']))
return data, collection_date_summary_dict
Expand All @@ -1981,14 +1990,17 @@ def get_email_settings(settings):
"""
email_settings = {}
email_settings['smtp_host_for_outbound_mail'] = settings.smtp_host_for_outbound_mail
email_settings['smtp_port_for_outbound_mail'] = settings.smtp_port_for_outbound_mail
email_settings['redcap_support_sender_email'] = settings.redcap_support_sender_email
email_settings['redcap_support_receiving_list'] = \
settings.redcap_support_receiver_email.split() if settings.redcap_support_receiver_email else []
email_settings['redcap_uri'] = settings.redcap_uri
email_settings['smtp_port_for_outbound_mail'] = settings.smtp_port_for_outbound_mail
email_settings['redcap_support_receiver_email'] = settings.redcap_support_receiver_email
email_settings['batch_warning_days'] = settings.batch_warning_days
email_settings['batch_report_sender_email'] = settings.sender_email
email_settings['batch_report_receiving_list'] = \
settings.receiver_email.split() if settings.receiver_email else []
return email_settings


def get_redcap_settings(settings):
"""
Helper function for grouping redcap connection properties
Expand Down
6 changes: 1 addition & 5 deletions bin/redi_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,8 @@
from redcap import RedcapError
import tempfile
import sqlite3 as lite
import md5
from datetime import date
import hashlib
import redi
import utils.redi_email as redi_email
from utils.redcapClient import redcapClient
from requests import RequestException
Expand All @@ -27,7 +26,6 @@
import sys
logger = logging.getLogger(__name__)
logger.addHandler(logging.NullHandler())
proj_root = redi.get_proj_root()

DEFAULT_DATA_DIRECTORY = os.getcwd()

Expand All @@ -47,7 +45,6 @@
def create_import_data_json(
import_data_dict,
event_tree):
# redi.configure_logger(system_log_file_full_path)

root = event_tree

Expand Down Expand Up @@ -103,7 +100,6 @@ def get_child_text_safely(etree, ele):


def generate_output(person_tree, redcap_settings, email_settings, data_repository, skip_blanks=False):
# redi.configure_logger(system_log_file_full_path)

# the global dictionary to be returned
report_data = {
Expand Down
16 changes: 8 additions & 8 deletions bin/utils/SimpleConfigParser.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
required_server_parameters_list = [
'redcap_uri',
'token',
'redcap_server',
'redcap_support_receiver_email',
'smtp_host_for_outbound_mail',
'smtp_port_for_outbound_mail',
Expand All @@ -113,6 +112,7 @@
"rate_limiter_value_in_redcap": 600,
"batch_info_database": "redi.db",
"send_email": 'N',
"receiver_email": "[email protected]",
"verify_ssl": True,
"replace_fields_in_raw_data_xml": None,
"include_rule_errors_in_report": False,
Expand Down Expand Up @@ -172,7 +172,7 @@ def set_attributes(self):
if not self.getoptionslist():
message = "ERROR: Configuration file '{0}' is empty! Program "\
"will now terminate...".format(self.filename)
logging.error(message)
logger.error(message)
sys.exit()

else:
Expand All @@ -190,7 +190,7 @@ def check_parameters(self):
message = DEFAULT_MESSAGE_NO_VALUE.format(option, \
self.filename) + required_files_dict[option] +\
DEFAULT_MESSAGE
logging.error(message)
logger.error(message)
sys.exit()
else:
setattr(self, option, self.getoption(option))
Expand All @@ -200,9 +200,11 @@ def check_parameters(self):
if not self.hasoption(option) or self.getoption(option) == "":
message = DEFAULT_MESSAGE_NO_VALUE.format(option, \
self.filename) + DEFAULT_MESSAGE
logging.error(message)
logger.error(message)
sys.exit()
else:
logger.debug("Setting required parameter {} = {} "\
.format(option, self.getoption(option)))
setattr(self, option, self.getoption(option))

# check for receiver email if send_email = 'Y'
Expand All @@ -211,15 +213,13 @@ def check_parameters(self):
self.getoption('receiver_email') == "":
message = DEFAULT_MESSAGE_NO_VALUE.format(option, \
self.filename) + DEFAULT_MESSAGE
logging.error(message)
logger.error(message)
sys.exit()
else:
setattr(self, 'receiver_email', self.getoption('receiver_email'))

# set optional parameters with default values if missing
for option in optional_parameters_dict:
if not self.hasoption(option) or self.getoption(option) == "":
logging.warn("Parameter '{0}' in {1} does not have"\
logger.warn("Parameter '{0}' in {1} does not have"\
" a value. Default value '{2}' applied.".format(option, \
self.filename, optional_parameters_dict[option]))
setattr(self, option, optional_parameters_dict[option])
Expand Down
Loading

0 comments on commit 712bfd6

Please sign in to comment.