mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Merge pull request #7755 from jmchilton/job_error_reporting
Report a couple common errors in dataset/job report.
This commit is contained in:
@@ -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) {
|
||||
|
||||
@@ -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):
|
||||
"""
|
||||
|
||||
@@ -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',
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user