mirror of
https://github.com/galaxyproject/galaxy.git
synced 2026-09-24 16:30:27 +08:00
Report a couple common errors in dataset/job report.
Report if empty inputs were used or if the same input was used more than once, these were identified as common sources of potential errors across many tools. Neither of these are definite problems, but both could be depending on the tool and both happen frequently. Outline a few more ideas as TODOs in the code.
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,6 +57,46 @@ 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(`
|
||||
@@ -88,23 +129,24 @@ var View = Backbone.View.extend({
|
||||
const job_stderr = job.job_stderr;
|
||||
const job_messages = job.job_messages;
|
||||
if (!tool_stderr && !job_stderr && !job_messages) {
|
||||
return '';
|
||||
return '<h3 class="common_problems"></h3>';
|
||||
}
|
||||
var message = '<h3>Error Details</h3>';
|
||||
var message = "<h3>Error Details</h3>";
|
||||
if (job_messages) {
|
||||
message += '<p>Execution resulted in the following messages:</p>';
|
||||
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>`;
|
||||
message += `<p><pre>${_.escape(job_message["desc"])}</pre></p>`;
|
||||
}
|
||||
}
|
||||
if (tool_stderr) {
|
||||
message += '<p>Tool generated the following standard error:</p>';
|
||||
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 += "<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;
|
||||
},
|
||||
|
||||
|
||||
@@ -167,6 +167,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