Merge pull request #7755 from jmchilton/job_error_reporting

Report a couple common errors in dataset/job report.
This commit is contained in:
Martin Cech
2019-04-16 13:30:12 -04:00
committed by GitHub
4 changed files with 121 additions and 3 deletions
@@ -32,6 +32,7 @@ var View = Backbone.View.extend({
url: job_url,
success: job => {
this.render_error_page(dataset, job);
this.find_common_problems(job);
},
error: response => {
var error_response = {
@@ -56,15 +57,53 @@ var View = Backbone.View.extend({
});
},
find_common_problems: function(job) {
var job_url = `${getAppRoot()}api/jobs/${job.id}/common_problems`;
Utils.get({
url: job_url,
success: common_problems => {
this.render_common_problems(job, common_problems);
},
error: response => {
console.log("error");
console.log(response);
}
});
return;
},
render_common_problems: function(job, common_problems) {
const has_duplicate_inputs = common_problems.has_duplicate_inputs;
const has_empty_inputs = common_problems.has_empty_inputs;
if (has_duplicate_inputs || has_empty_inputs) {
const reportEl = this.$el.find(".common_problems");
reportEl.text("Detected Common Potential Problems");
if (has_empty_inputs) {
reportEl.after(`
<p>
The tool was executed with one or more empty input datasets, this isn't
always a problem and may not have been but frequently this is a source
of error.
<p>`);
}
if (has_duplicate_inputs) {
reportEl.after(`
<p>
The tool was executed with one or more duplicate input datasets, this isn't
always a problem and may not have been but frequently this is a source
of error.
<p>`);
}
}
},
/** Render the view */
render_error_page: function(dataset, job) {
this.$el.empty().append(`
${this._templateHeader()}
<h2>Dataset Error</h2>
<p>An error occurred while running the tool <b>${job.tool_id}</b>.</p>
<p>Tool execution generated the following messages:</p>
<pre class="code">${_.escape(job.stderr)}</pre>
${this.job_summary(job)}
<h3>Troubleshoot This Error</h3>
<p>
There are a number of help resources to self diagnose and
@@ -85,6 +124,32 @@ var View = Backbone.View.extend({
this.$el.append(this._getBugFormTemplate(dataset, job));
},
job_summary: function(job) {
const tool_stderr = job.tool_stderr;
const job_stderr = job.job_stderr;
const job_messages = job.job_messages;
if (!tool_stderr && !job_stderr && !job_messages) {
return '<h3 class="common_problems"></h3>';
}
var message = "<h3>Error Details</h3>";
if (job_messages) {
message += "<p>Execution resulted in the following messages:</p>";
for (let job_message of job_messages) {
message += `<p><pre>${_.escape(job_message["desc"])}</pre></p>`;
}
}
if (tool_stderr) {
message += "<p>Tool generated the following standard error:</p>";
message += `<pre class="code">${_.escape(tool_stderr)}</pre>`;
}
if (job_stderr) {
message += "<p>Galaxy job runner generated the following standard error:</p>";
message += `<pre class="code">${_.escape(job_stderr)}</pre>`;
}
message += `<h3 class="common_problems"></h3>`;
return message;
},
/** Display actions messages */
display_message: function(response, $el, doNotClear, safe) {
if (!safe) {
+24
View File
@@ -169,6 +169,30 @@ class JobController(BaseAPIController, UsesLibraryMixinItems):
job_dict['job_metrics'] = [metric_to_dict(metric) for metric in job.metrics]
return job_dict
@expose_api
def common_problems(self, trans, id, **kwd):
"""
* GET /api/jobs/{id}/common_problems
check inputs and job for common potential problems to aid in error reporting
"""
job = self.__get_job(trans, id)
seen_ids = set()
has_empty_inputs = False
has_duplicate_inputs = False
for job_input_assoc in job.input_datasets:
input_dataset_instance = job_input_assoc.dataset
if input_dataset_instance.get_total_size() == 0:
has_empty_inputs = True
input_instance_id = input_dataset_instance.id
if input_instance_id in seen_ids:
has_duplicate_inputs = True
else:
seen_ids.add(input_instance_id)
# TODO: check percent of failing jobs around a window on job.update_time for handler - report if high.
# TODO: check percent of failing jobs around a window on job.update_time for destination_id - report if high.
# TODO: sniff inputs (add flag to allow checking files?)
return {"has_empty_inputs": has_empty_inputs, "has_duplicate_inputs": has_duplicate_inputs}
@expose_api
def inputs(self, trans, id, **kwd):
"""
+1
View File
@@ -888,6 +888,7 @@ def populate_api_routes(webapp, app):
webapp.mapper.connect('build_for_rerun', '/api/jobs/{id}/build_for_rerun', controller='jobs', action='build_for_rerun', conditions=dict(method=['GET']))
webapp.mapper.connect('resume', '/api/jobs/{id}/resume', controller='jobs', action='resume', conditions=dict(method=['PUT']))
webapp.mapper.connect('job_error', '/api/jobs/{id}/error', controller='jobs', action='error', conditions=dict(method=['POST']))
webapp.mapper.connect('common_problems', '/api/jobs/{id}/common_problems', controller='jobs', action='common_problems', conditions=dict(method=['GET']))
# Job files controllers. Only for consumption by remote job runners.
webapp.mapper.resource('file',
+28
View File
@@ -190,6 +190,34 @@ class JobsApiTestCase(api.ApiTestCase):
assert_ok=False)
assert not dataset['visible']
@skip_without_tool('empty_output')
def test_common_problems(self):
with self.dataset_populator.test_history() as history_id:
empty_run_response = self.dataset_populator.run_tool(
tool_id='empty_output',
inputs={},
history_id=history_id,
)
empty_hda = empty_run_response["outputs"][0]
cat_empty_twice_run_response = self.dataset_populator.run_tool(
tool_id='cat1',
inputs={
'input1': {'src': 'hda', 'id': empty_hda['id']},
'queries_0|input2': {'src': 'hda', 'id': empty_hda['id']}
},
history_id=history_id,
)
empty_output_job = empty_run_response["jobs"][0]
cat_empty_job = cat_empty_twice_run_response["jobs"][0]
empty_output_common_problems_response = self._get('jobs/%s/common_problems' % empty_output_job["id"]).json()
cat_empty_common_problems_response = self._get('jobs/%s/common_problems' % cat_empty_job["id"]).json()
self._assert_has_keys(empty_output_common_problems_response, "has_empty_inputs", "has_duplicate_inputs")
self._assert_has_keys(cat_empty_common_problems_response, "has_empty_inputs", "has_duplicate_inputs")
assert not empty_output_common_problems_response["has_empty_inputs"]
assert cat_empty_common_problems_response["has_empty_inputs"]
assert not empty_output_common_problems_response["has_duplicate_inputs"]
assert cat_empty_common_problems_response["has_duplicate_inputs"]
@skip_without_tool('detect_errors_aggressive')
def test_report_error(self):
with self.dataset_populator.test_history() as history_id: