Merge pull request #2143 from erasche/nicer-emails

Nicer bug report emails
This commit is contained in:
Nicola Soranzo
2016-04-14 12:48:42 +01:00
2 changed files with 157 additions and 28 deletions
+122 -25
View File
@@ -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 = """
<html><head></head>
<body>
<style type="text/css">
tr:nth-child(even) {background-color: #f2f2f2}
table{margin: 1em;}
.mono{font-family: monospace;}
pre {
white-space: pre-wrap;
white-space: -moz-pre-wrap;
white-space: -pre-wrap;
white-space: -o-pre-wrap;
word-wrap: break-word;
background: #eee;
border:1px solid black;
padding:10px;
}
</style>
<h1>Galaxy Tool Error Report</h1>
<span class="sub"><i>from</i> <span class="mono"><a href="${host}">${host}</a></span>
<h3>Error Localization</h3>
<table>
<tbody>
<tr><td>Dataset</td><td>${dataset_id} (${dataset_id_encoded})</td></tr>
<tr><td>History</td><td><a href="${history_view_link}">${history_id} (${history_id_encoded})</a></td></tr>
<tr><td>Failed Job</td><td>${hid}: ${history_item_name} (${hda_id_encoded})</td></tr>
</tbody>
</table>
<h3>User Provided Information</h3>
The user <a href="mailto:${email_str}"><span class="mono">${email_str}</span></a> provided the following information:
<pre>
${message}
</pre>
<h3>Detailed Job Information</h3>
Job environment and execution information is available at the job <a href="${hda_show_params_link}">Info Page</a>.
<table>
<tbody>
<tr><td>Job ID</td><td>${job_id} (${job_id_encoded})</td></tr>
<tr><td>Tool ID</td><td>${job_tool_id}</td></tr>
<tr><td>Tool Version</td><td>${tool_version}</td></tr>
<tr><td>Job PID or DRM id</td><td>${job_runner_external_id}</td></tr>
<tr><td>Job Tool Version</td><td>${job_tool_version}</td></tr>
</tbody>
</table>
<h3>Job Execution and Failure Information</h3>
<h4>Command Line</h4>
<pre>
${job_command_line}
</pre>
<h4>stderr</h4>
<pre>
${job_stderr}
</pre>
<h4>stdout</h4>
<pre>
${job_stdout}
</pre>
<h4>Job Information</h4>
<pre>
${job_info}
</pre>
<h4>Job Traceback</h4>
<pre>
${job_traceback}
</pre>
This is an automated message. Do not reply to this address.
</body></html>
"""
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 )
+35 -3
View File
@@ -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()