diff --git a/lib/galaxy/tools/errors.py b/lib/galaxy/tools/errors.py index 251e1e894db..dc4fce2eaff 100644 --- a/lib/galaxy/tools/errors.py +++ b/lib/galaxy/tools/errors.py @@ -3,6 +3,7 @@ Functionality for dealing with tool errors. """ import string from galaxy import model, util, web +import cgi error_report_template = """ GALAXY TOOL ERROR REPORT @@ -51,6 +52,91 @@ ${job_traceback} (This is an automated message). """ +error_report_template_html = """ + + + + +

Galaxy Tool Error Report

+from ${host} + +

Error Localization

+ + + + + + +
Dataset${dataset_id} (${dataset_id_encoded})
History${history_id} (${history_id_encoded})
Failed Job${hid}: ${history_item_name} (${hda_id_encoded})
+ +

User Provided Information

+ +The user ${email_str} provided the following information: + +
+${message}
+
+ + +

Detailed Job Information

+ +Job environment and execution information is available at the job Info Page. + + + + + + + + + +
Job ID${job_id} (${job_id_encoded})
Tool ID${job_tool_id}
Tool Version${tool_version}
Job PID or DRM id${job_runner_external_id}
Job Tool Version${job_tool_version}
+ +

Job Execution and Failure Information

+ +

Command Line

+
+${job_command_line}
+
+ +

stderr

+
+${job_stderr}
+
+ +

stdout

+
+${job_stdout}
+
+ +

Job Information

+
+${job_info}
+
+ +

Job Traceback

+
+${job_traceback}
+
+ +This is an automated message. Do not reply to this address. + +""" + class ErrorReporter( object ): def __init__( self, hda, app ): @@ -91,30 +177,40 @@ class ErrorReporter( object ): email_str = "'%s' (providing preferred contact email '%s')" % (user.email, email) else: email_str = "'%s'" % (email or 'anonymously') - self.report = string.Template( error_report_template ) \ - .safe_substitute( host=host, - dataset_id_encoded=self.app.security.encode_id( hda.dataset_id ), - dataset_id=hda.dataset_id, - history_id_encoded=history_id_encoded, - history_id=hda.history_id, - hda_id_encoded=hda_id_encoded, - hid=hda.hid, - history_item_name=hda.get_display_name(), - history_view_link=history_view_link, - hda_show_params_link=hda_show_params_link, - job_id_encoded=self.app.security.encode_id( job.id ), - job_id=job.id, - tool_version=job.tool_version, - job_tool_id=job.tool_id, - job_tool_version=hda.tool_version, - job_runner_external_id=job.job_runner_external_id, - job_command_line=job.command_line, - job_stderr=util.unicodify( job.stderr ), - job_stdout=util.unicodify( job.stdout ), - job_info=util.unicodify( job.info ), - job_traceback=util.unicodify( job.traceback ), - email_str=email_str, - message=util.unicodify( message ) ) + + report_variables = dict( + host=host, + dataset_id_encoded=self.app.security.encode_id( hda.dataset_id ), + dataset_id=hda.dataset_id, + history_id_encoded=history_id_encoded, + history_id=hda.history_id, + hda_id_encoded=hda_id_encoded, + hid=hda.hid, + history_item_name=hda.get_display_name(), + history_view_link=history_view_link, + hda_show_params_link=hda_show_params_link, + job_id_encoded=self.app.security.encode_id( job.id ), + job_id=job.id, + tool_version=job.tool_version, + job_tool_id=job.tool_id, + job_tool_version=hda.tool_version, + job_runner_external_id=job.job_runner_external_id, + job_command_line=job.command_line, + job_stderr=util.unicodify( job.stderr ), + job_stdout=util.unicodify( job.stdout ), + job_info=util.unicodify( job.info ), + job_traceback=util.unicodify( job.traceback ), + email_str=email_str, + message=util.unicodify( message ) + ) + + self.report = string.Template( error_report_template ).safe_substitute( report_variables ) + + # Escape all of the content for use in the HTML report + for parameter in report_variables.keys(): + report_variables[parameter] = cgi.escape(report_variables[parameter]) + + self.html_report = string.Template( error_report_template_html ).safe_substitute( report_variables ) def _send_report( self, user, email=None, message=None, **kwd ): return self.report @@ -146,5 +242,6 @@ class EmailErrorReporter( ErrorReporter ): subject = "%s (%s)" % ( subject, self.app.toolbox.get_tool( self.job.tool_id, self.job.tool_version ).old_id ) except Exception: pass + # Send it - return util.send_mail( frm, to, subject, self.report, self.app.config ) + return util.send_mail( frm, to, subject, self.report, self.app.config, html=self.html_report ) diff --git a/lib/galaxy/util/__init__.py b/lib/galaxy/util/__init__.py index 3478801d726..98c47941b60 100644 --- a/lib/galaxy/util/__init__.py +++ b/lib/galaxy/util/__init__.py @@ -28,7 +28,7 @@ from os.path import normpath, relpath from xml.etree import ElementInclude, ElementTree from six import binary_type, iteritems, PY3, string_types, text_type -from six.moves import email_mime_text, xrange, zip +from six.moves import email_mime_text, email_mime_multipart, xrange, zip from six.moves.urllib import parse as urlparse from six.moves.urllib import request as urlrequest @@ -1205,19 +1205,51 @@ def size_to_bytes( size ): return int( size ) -def send_mail( frm, to, subject, body, config ): +def send_mail( frm, to, subject, body, config, html=None ): """ Sends an email. + + :type frm: str + :param frm: from address + + :type to: str + :param to: to address + + :type subject: str + :param subject: Subject line + + :type body: str + :param body: Body text (should be plain text) + + :type config: object + :param config: Galaxy configuration object + + :type html: str + :param html: Alternative HTML representation of the body content. If + provided will convert the message to a MIMEMultipart. (Default 'None') """ + to = listify( to ) - msg = email_mime_text.MIMEText( body.encode( 'ascii', 'replace' ) ) + if html: + msg = email_mime_multipart.MIMEMultipart('alternative') + else: + msg = email_mime_text.MIMEText( body.encode( 'ascii', 'replace' ) ) + msg[ 'To' ] = ', '.join( to ) msg[ 'From' ] = frm msg[ 'Subject' ] = subject + if config.smtp_server is None: log.error( "Mail is not configured for this Galaxy instance." ) log.info( msg ) return + + if html: + mp_text = email_mime_text.MIMEText( body.encode( 'ascii', 'replace' ), 'plain' ) + mp_html = email_mime_text.MIMEText( html.encode( 'ascii', 'replace' ), 'html' ) + msg.attach(mp_text) + msg.attach(mp_html) + smtp_ssl = asbool( getattr(config, 'smtp_ssl', False ) ) if smtp_ssl: s = smtplib.SMTP_SSL()