vuefy job information

This commit is contained in:
Oleg Zharkov
2020-12-03 13:55:40 +01:00
parent 78b9b111d3
commit bc41ce9e04
10 changed files with 225 additions and 64 deletions
+1
View File
@@ -100,6 +100,7 @@ export { mountPageDisplay } from "components/PageDisplay";
export { mountDestinationParams } from "components/JobDestinationParams";
export { mountDatasetInformation } from "components/DatasetInformation";
export { mountDatasetStorage } from "components/Dataset/DatasetStorage";
export { mountJobInformation } from "components/JobInformation";
// Used in common.mako
export { default as store } from "storemodern";
@@ -0,0 +1,130 @@
<template>
<div>
<h3>Job Information</h3>
<table class="tabletip">
<tr v-if="jobInformation">
<td>Galaxy Tool ID:</td>
<td>{{ jobInformation.tool_id }}</td>
</tr>
<tr v-if="jobInformation">
<td>Galaxy Tool Version:</td>
<td>{{ jobInformation.tool_version }}</td>
</tr>
<tr>
<td>Tool Version:</td>
<td>{{ dataset.tool_version }}</td>
</tr>
<tr>
<td>Tool Standard Output:</td>
<td><a :href="getOutUrl()">stdout</a></td>
</tr>
<tr>
<td>Tool Standard Error:</td>
<td><a :href="getOutUrl(true)">stdout</a></td>
</tr>
<tr v-if="jobInformation">
<td>Tool Exit Code:</td>
<td>{{ jobInformation.exit_code }}</td>
</tr>
<tr v-if="jobInformation && jobInformation.job_messages">
<td>Job Messages</td>
<td>
<ul style="padding-left: 15px; margin-bottom: 0px;">
<li v-for="message in jobInformation.job_messages">{{ message }}</li>
</ul>
</td>
</tr>
<tr>
<td>History Content API ID:</td>
<td>
{{ dataset.id }}
<div v-if="dataset.dataset_id">({{ dataset.dataset_id }})</div>
</td>
</tr>
<tr v-if="jobInformation">
<td>Job API ID:</td>
<td>
{{ jobInformation.encoded_id }}
<div v-if="jobInformation.id">{{ jobInformation.id }}</div>
</td>
</tr>
<tr v-if="jobInformation && jobInformation.encoded_copied_from_job_id">
<td>Copied from Job API ID:</td>
<td>
{{ jobInformation.encoded_copied_from_job_id }}
<div v-if="jobInformation.copied_from_job_id">({{ jobInformation.copied_from_job_id }})</div>
</td>
</tr>
<tr v-if="jobInformation && jobInformation.encoded_copied_from_job_id">
<td>Copied from Job API ID:</td>
<td>
{{ jobInformation.encoded_copied_from_job_id }}
<div v-if="jobInformation.copied_from_job_id">({{ jobInformation.copied_from_job_id }})</div>
</td>
</tr>
<tr>
<td>History API ID:</td>
<td>
{{ dataset.id }}
<!-- TODO admin-->
<!-- %if trans.user_is_admin:-->
<!-- (${hda.history_id})-->
<!-- %endif-->
</td>
</tr>
<tr v-if="dataset.uuid">
<td>UUID:</td>
<td>{{ dataset.uuid }}</td>
</tr>
<tr v-if="dataset.file_name">
<td>Full Path:</td>
<td>{{ dataset.file_name }}</td>
</tr>
</table>
</div>
</template>
<script>
import { mapCacheActions } from "vuex-cache";
import { getAppRoot } from "onload/loadConfig";
export default {
props: {
hda_id: {
type: String,
required: true,
},
job_id: {
type: String,
required: true,
},
},
created: function () {
this.fetchDataset(this.hda_id);
this.fetchJobInformation(this.job_id);
},
computed: {
dataset: function () {
return this.$store.getters.dataset(this.hda_id);
},
jobInformation: function () {
return this.$store.getters.jobInformation(this.job_id);
},
},
methods: {
getOutUrl(isStderr = false) {
let url;
if (isStderr) url = `${getAppRoot()}datasets/${this.dataset.id}/stderr`;
else url = `${getAppRoot()}datasets/${this.dataset.id}/stdout`;
return url;
},
...mapCacheActions(["fetchJobInformation", "fetchDataset"]),
},
};
</script>
@@ -0,0 +1,3 @@
export { default as JobInformation } from "./JobInformation";
export { mountJobInformation } from "./mount";
@@ -0,0 +1,13 @@
/**
* Endpoint for mounting job metrics from non-Vue environment.
*/
import JobInformation from "./JobInformation";
import { mountVueComponent } from "utils/mountVueComponent";
export const mountJobInformation = (propsData = {}) => {
document.querySelectorAll(".job-information").forEach((element) => {
propsData.hda_id = element.getAttribute("hda_id");
propsData.job_id = element.getAttribute("job_id");
mountVueComponent(JobInformation)(propsData, element);
});
};
+2
View File
@@ -18,6 +18,7 @@ import { workflowStore } from "./workflowStore";
import { datasetPathDestinationStore } from "./datasetPathDestinationStore";
import { datasetExtFilesStore } from "./datasetExtFilesStore";
import { datasetsStore } from "./datasetsStore";
import { jobInformationStore } from "./jobInformationStore";
Vue.use(Vuex);
@@ -43,6 +44,7 @@ export function createStore() {
config: configStore,
workflows: workflowStore,
datasets: datasetsStore,
informationStore: jobInformationStore,
},
});
}
+33
View File
@@ -0,0 +1,33 @@
export const state = {
jobInformationJobId: {},
};
import Vue from "vue";
import { getAppRoot } from "onload/loadConfig";
import axios from "axios";
const getters = {
jobInformation: (state) => (jobId) => {
return state.jobInformationJobId[jobId] || [];
},
};
const actions = {
fetchJobInformation: async ({ commit }, jobId) => {
const { data } = await axios.get(`${getAppRoot()}api/jobs/${jobId}/job_information`);
commit("saveJobInformationForJobId", { jobId, jobInformation: data });
},
};
const mutations = {
saveJobInformationForJobId: (state, { jobId, jobInformation }) => {
Vue.set(state.jobInformationJobId, jobId, jobInformation);
},
};
export const jobInformationStore = {
state,
getters,
actions,
mutations,
};
+21
View File
@@ -662,3 +662,24 @@ def summarize_job_parameters(trans, job):
parameters = inputs_recursive(tool.inputs, params_objects, depth=1, upgrade_messages=upgrade_messages)
return {"parameters": parameters, "has_parameter_errors": has_parameter_errors}
def summarize_job_information(trans, job):
"""Produce a dict-ified version of job information
Precondition: the caller has verified the job is accessible to the user
represented by the trans parameter.
"""
job_information = {'tool_id': job.tool_id,
'tool_version': job.tool_version,
'exit_code': job.exit_code,
'job_messages': job.job_messages,
'encoded_id': trans.security.encode_id(job.id),
'encoded_copied_from_job_id': trans.security.encode_id(job.copied_from_job_id) if job.copied_from_job_id else "",
}
if trans.user_is_admin:
job_information['id'] = job.id
job_information['copied_from_job_id'] = job.copied_from_job_id if job.copied_from_job_id else "",
return job_information
+17
View File
@@ -18,6 +18,7 @@ from galaxy.managers.jobs import (
JobManager,
JobSearch,
summarize_destination_params,
summarize_job_information,
summarize_job_metrics,
summarize_job_parameters,
)
@@ -334,6 +335,22 @@ class JobController(BaseAPIController, UsesVisualizationMixin):
job = self.__get_job(trans, **kwd)
return summarize_job_parameters(trans, job)
@require_admin
@expose_api
def job_information(self, trans, **kwd):
"""
* GET /api/jobs/{job_id}/summarize_job_information
Return job information for specified job.
:type job_id: string
:param job_id: Encoded job id
:rtype: list
:returns: list containing job information
"""
job = self.__get_job(trans, **kwd)
return summarize_job_information(trans, job)
@expose_api_anonymous
def build_for_rerun(self, trans, id, **kwd):
"""
+1
View File
@@ -1022,6 +1022,7 @@ def populate_api_routes(webapp, app):
# Job metrics and parameters by job id or dataset id (for slightly different accessibility checking)
webapp.mapper.connect('metrics', '/api/jobs/{job_id}/metrics', controller='jobs', action='metrics', conditions=dict(method=['GET']))
webapp.mapper.connect('destination_params', '/api/jobs/{job_id}/destination_params', controller='jobs', action='destination_params', conditions=dict(method=['GET']))
webapp.mapper.connect('job_information', '/api/jobs/{job_id}/job_information', controller='jobs', action='job_information', conditions=dict(method=['GET']))
webapp.mapper.connect('show_job_lock', '/api/job_lock', controller='jobs', action='show_job_lock', conditions=dict(method=['GET']))
webapp.mapper.connect('update_job_lock', '/api/job_lock', controller='jobs', action='update_job_lock', conditions=dict(method=['PUT']))
webapp.mapper.connect('dataset_metrics', '/api/datasets/{dataset_id}/metrics', controller='jobs', action='metrics', conditions=dict(method=['GET']))
+4 -64
View File
@@ -25,65 +25,7 @@ encoded_history_id = trans.security.encode_id( hda.history_id )
%>
<div class="dataset-information" hda_id="${encoded_hda_id}"></div>
<h3>Job Information</h3>
<table class="tabletip">
<tbody>
%if job:
<tr><td>Galaxy Tool ID:</td><td>${ job.tool_id | h }</td></tr>
<tr><td>Galaxy Tool Version:</td><td>${ job.tool_version | h }</td></tr>
%endif
<tr><td>Tool Version:</td><td>${hda.tool_version | h}</td></tr>
<tr><td>Tool Standard Output:</td><td><a href="${h.url_for( controller='dataset', action='stdout', dataset_id=encoded_hda_id )}">stdout</a></td></tr>
<tr><td>Tool Standard Error:</td><td><a href="${h.url_for( controller='dataset', action='stderr', dataset_id=encoded_hda_id )}">stderr</a></td></tr>
%if job:
<tr><td>Tool Exit Code:</td><td>${ job.exit_code | h }</td></tr>
%if job.job_messages:
<tr><td>Job Messages</td><td><ul style="padding-left: 15px; margin-bottom: 0px">
%for job_message in job.job_messages:
<li>${ job_message['desc'] |h }</li>
%endfor
<ul></td></tr>
%endif
%endif
<tr><td>History Content API ID:</td>
<td>${encoded_hda_id}
%if trans.user_is_admin:
(${hda.id})
%endif
</td></tr>
%if job:
<tr><td>Job API ID:</td>
<td>${trans.security.encode_id( job.id )}
%if trans.user_is_admin:
(${job.id})
%endif
</td></tr>
%if job.copied_from_job_id:
<tr><td>Copied from Job API ID:</td>
<td>${trans.security.encode_id( job.copied_from_job_id )}
%if trans.user_is_admin:
(${job.copied_from_job_id})
%endif
</td></tr>
%endif
%endif
<tr><td>History API ID:</td>
<td>${encoded_history_id}
%if trans.user_is_admin:
(${hda.history_id})
%endif
</td></tr>
%if hda.dataset.uuid:
<tr><td>UUID:</td><td>${hda.dataset.uuid}</td></tr>
%endif
%if trans.user_is_admin or trans.app.config.expose_dataset_path:
%if not hda.purged:
<tr><td>Full Path:</td><td>${hda.file_name | h}</td></tr>
%endif
%endif
</tbody>
</table>
<div class="job-information" hda_id="${encoded_hda_id}" job_id="${trans.security.encode_id( job.id )}"></div>
<div class="dataset-storage" dataset_id="${encoded_hda_id}" dataset_type="hda">
</div>
@@ -174,10 +116,8 @@ $(function(){
window.parent.Galaxy.currHistoryPanel.scrollToId( 'dataset-' + $( this ).data( 'hda-id' ) );
}
})
window.bundleEntries.mountJobMetrics();
window.bundleEntries.mountJobParameters();
window.bundleEntries.mountDestinationParams();
window.bundleEntries.mountDatasetInformation();
window.bundleEntries.mountDatasetStorage();
const mountComponents = ["mountJobMetrics", "mountJobParameters", "mountDestinationParams","mountDatasetInformation","mountJobInformation", "mountDatasetStorage"]
mountComponents.forEach(component => window.bundleEntries[component]())
});
</script>