From bc41ce9e043e876dcf396028c38c02984d2cb1ba Mon Sep 17 00:00:00 2001 From: Oleg Zharkov Date: Fri, 2 Oct 2020 18:37:32 +0200 Subject: [PATCH] vuefy job information --- client/src/bundleEntries.js | 1 + .../JobInformation/JobInformation.vue | 130 ++++++++++++++++++ client/src/components/JobInformation/index.js | 3 + client/src/components/JobInformation/mount.js | 13 ++ client/src/store/index.js | 2 + client/src/store/jobInformationStore.js | 33 +++++ lib/galaxy/managers/jobs.py | 21 +++ lib/galaxy/webapps/galaxy/api/jobs.py | 17 +++ lib/galaxy/webapps/galaxy/buildapp.py | 1 + templates/show_params.mako | 68 +-------- 10 files changed, 225 insertions(+), 64 deletions(-) create mode 100644 client/src/components/JobInformation/JobInformation.vue create mode 100644 client/src/components/JobInformation/index.js create mode 100644 client/src/components/JobInformation/mount.js create mode 100644 client/src/store/jobInformationStore.js diff --git a/client/src/bundleEntries.js b/client/src/bundleEntries.js index 19f6a629f5a..a4bfdcd9dcd 100644 --- a/client/src/bundleEntries.js +++ b/client/src/bundleEntries.js @@ -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"; diff --git a/client/src/components/JobInformation/JobInformation.vue b/client/src/components/JobInformation/JobInformation.vue new file mode 100644 index 00000000000..dae49573684 --- /dev/null +++ b/client/src/components/JobInformation/JobInformation.vue @@ -0,0 +1,130 @@ + + + diff --git a/client/src/components/JobInformation/index.js b/client/src/components/JobInformation/index.js new file mode 100644 index 00000000000..7e2949fd7d9 --- /dev/null +++ b/client/src/components/JobInformation/index.js @@ -0,0 +1,3 @@ +export { default as JobInformation } from "./JobInformation"; + +export { mountJobInformation } from "./mount"; diff --git a/client/src/components/JobInformation/mount.js b/client/src/components/JobInformation/mount.js new file mode 100644 index 00000000000..de7b30d5d33 --- /dev/null +++ b/client/src/components/JobInformation/mount.js @@ -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); + }); +}; diff --git a/client/src/store/index.js b/client/src/store/index.js index 4a51630d465..ef5ac982cff 100644 --- a/client/src/store/index.js +++ b/client/src/store/index.js @@ -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, }, }); } diff --git a/client/src/store/jobInformationStore.js b/client/src/store/jobInformationStore.js new file mode 100644 index 00000000000..88f747b8096 --- /dev/null +++ b/client/src/store/jobInformationStore.js @@ -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, +}; diff --git a/lib/galaxy/managers/jobs.py b/lib/galaxy/managers/jobs.py index 560be6ddcb4..5306a980789 100644 --- a/lib/galaxy/managers/jobs.py +++ b/lib/galaxy/managers/jobs.py @@ -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 diff --git a/lib/galaxy/webapps/galaxy/api/jobs.py b/lib/galaxy/webapps/galaxy/api/jobs.py index 1f7c8043359..b71fd90ec47 100644 --- a/lib/galaxy/webapps/galaxy/api/jobs.py +++ b/lib/galaxy/webapps/galaxy/api/jobs.py @@ -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): """ diff --git a/lib/galaxy/webapps/galaxy/buildapp.py b/lib/galaxy/webapps/galaxy/buildapp.py index c18c421e95e..7dccdca768f 100644 --- a/lib/galaxy/webapps/galaxy/buildapp.py +++ b/lib/galaxy/webapps/galaxy/buildapp.py @@ -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'])) diff --git a/templates/show_params.mako b/templates/show_params.mako index d58b62d4082..2ef09fa8bce 100644 --- a/templates/show_params.mako +++ b/templates/show_params.mako @@ -25,65 +25,7 @@ encoded_history_id = trans.security.encode_id( hda.history_id ) %>
- -

Job Information

- - - %if job: - - - %endif - - - - %if job: - - %if job.job_messages: - - %endif - %endif - - - %if job: - - - %if job.copied_from_job_id: - - - %endif - %endif - - - %if hda.dataset.uuid: - - %endif - %if trans.user_is_admin or trans.app.config.expose_dataset_path: - %if not hda.purged: - - %endif - %endif - -
Galaxy Tool ID:${ job.tool_id | h }
Galaxy Tool Version:${ job.tool_version | h }
Tool Version:${hda.tool_version | h}
Tool Standard Output:stdout
Tool Standard Error:stderr
Tool Exit Code:${ job.exit_code | h }
Job Messages
    - %for job_message in job.job_messages: -
  • ${ job_message['desc'] |h }
  • - %endfor -
    History Content API ID:${encoded_hda_id} - %if trans.user_is_admin: - (${hda.id}) - %endif -
    Job API ID:${trans.security.encode_id( job.id )} - %if trans.user_is_admin: - (${job.id}) - %endif -
    Copied from Job API ID:${trans.security.encode_id( job.copied_from_job_id )} - %if trans.user_is_admin: - (${job.copied_from_job_id}) - %endif -
    History API ID:${encoded_history_id} - %if trans.user_is_admin: - (${hda.history_id}) - %endif -
    UUID:${hda.dataset.uuid}
    Full Path:${hda.file_name | h}
    +
    @@ -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]()) });