Loading client/src/components/JobInformation/JobInformation.vue +23 −2 Original line number Diff line number Diff line <template> <div> <job-details-provider auto-refresh :jobId="job_id" @update:result="updateJob" /> <job-details-provider auto-refresh :jobId="job_id" :stdout_start=stdout_position :stdout_char_count=stdout_char_count @update:result="updateJob" /> <h3>Job Information</h3> <table id="job-information" class="tabletip info_data_table"> <tbody> Loading Loading @@ -37,7 +37,7 @@ </td> </tr> <code-row v-if="job" id="command-line" :code-label="'Command Line'" :code-item="job.command_line" /> <code-row v-if="job" id="stdout" :code-label="'Tool Standard Output'" :code-item="job.tool_stdout" /> <code-row v-if="job" id="stdout" :code-label="'Tool Standard Output'" :code-item="stdout_text" /> <code-row v-if="job" id="stderr" :code-label="'Tool Standard Error'" :code-item="job.tool_stderr" /> <code-row v-if="job && job.traceback" Loading Loading @@ -103,6 +103,9 @@ export default { data() { return { job: null, stdout_position: 0, stdout_char_count: 50000, stdout_text: "", }; }, computed: { Loading @@ -115,12 +118,30 @@ export default { return this.job && !JOB_STATES_MODEL.NON_TERMINAL_STATES.includes(this.job.state); }, }, updated() { try { const stdout_block = document.querySelector("#stdout").querySelector(".code"); // if user is scrolled above the bottom of the code element, then no need to update the stdout if (stdout_block.scrollTop <= stdout_block.scrollHeight - 3000) { this.stdout_char_count = 0; } else { this.stdout_char_count = 50000; } } catch(exception) { console.log(exception); } }, methods: { getAppRoot() { return getAppRoot(); }, updateJob(job) { this.job = job; // Keep stdout in memory and only fetch new text via JobProvider if (job.tool_stdout != null) { this.stdout_text += job.tool_stdout; this.stdout_position += job.tool_stdout.length; } }, }, }; Loading client/src/components/providers/JobProvider.js +2 −2 Original line number Diff line number Diff line Loading @@ -5,8 +5,8 @@ import { rethrowSimple } from "utils/simple-error"; import { stateIsTerminal } from "./utils"; import { cleanPaginationParameters } from "./utils"; async function jobDetails({ jobId }) { const url = `${getAppRoot()}api/jobs/${jobId}?full=True`; async function jobDetails({ jobId, stdout_start = 0, stdout_char_count = 0 }) { const url = `${getAppRoot()}api/jobs/${jobId}?full=True&stdout_start_pos=${stdout_start}&stdout_count=${stdout_char_count}`; try { const { data } = await axios.get(url); return data; Loading lib/galaxy/jobs/runners/pulsar.py +5 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import logging import os import re import subprocess from pathlib import Path from time import sleep import packaging.version Loading Loading @@ -644,6 +645,10 @@ class PulsarJobRunner(AsynchronousJobRunner): run_results = client.full_status() remote_metadata_directory = run_results.get("metadata_directory", None) stdout = run_results.get("stdout", "") if stdout == "": stdout_path = Path(job_wrapper.working_directory) / "outputs" / "tool_stdout" stdout_file = open(stdout_path, "r") stdout = stdout_file.read() stderr = run_results.get("stderr", "") exit_code = run_results.get("returncode", None) pulsar_outputs = PulsarOutputs.from_status_response(run_results) Loading lib/galaxy/managers/jobs.py +15 −2 Original line number Diff line number Diff line import json import logging import typing from pathlib import Path from datetime import ( date, datetime, Loading Loading @@ -56,7 +57,7 @@ from galaxy.util.search import ( parse_filters_structured, RawTextTerm, ) import traceback log = logging.getLogger(__name__) Loading Loading @@ -219,7 +220,7 @@ class JobManager: ) return self.job_lock() def get_accessible_job(self, trans, decoded_job_id): def get_accessible_job(self, trans, decoded_job_id, stdout_start_pos=-1, stdout_count=0): job = trans.sa_session.query(trans.app.model.Job).filter(trans.app.model.Job.id == decoded_job_id).first() if job is None: raise ObjectNotFound() Loading @@ -236,6 +237,18 @@ class JobManager: if not self.dataset_manager.is_accessible(data_assoc.dataset.dataset, trans.user): raise ItemAccessibilityException("You are not allowed to rerun this job.") trans.sa_session.refresh(job) # If stdout_count and stdout_start_pos are good values, then load standard out and add it to status if job.state == job.states.RUNNING and stdout_count > 0 and stdout_start_pos > -1: try: working_directory = trans.app.object_store.get_filename(job, base_dir="job_work", dir_only=True, obj_dir=True) stdout_path = Path(working_directory) / "outputs" / "tool_stdout" stdout_file = open(stdout_path, "r") stdout_file.seek(stdout_start_pos) job.job_stdout = stdout_file.read(stdout_count) job.tool_stdout = job.job_stdout except Exception as e: log.error("Could not read STDOUT: %s", e) return job def stop(self, job, message=None): Loading lib/galaxy/webapps/galaxy/api/job_files.py +5 −1 Original line number Diff line number Diff line Loading @@ -104,6 +104,10 @@ class JobFilesAPIController(BaseGalaxyAPIController): target_dir = os.path.dirname(path) util.safe_makedirs(target_dir) try: if os.path.exists(path): with open(path, 'ab') as dest: shutil.copyfileobj(open(input_file.name, 'rb'), dest) else: shutil.move(input_file.name, path) finally: try: Loading Loading
client/src/components/JobInformation/JobInformation.vue +23 −2 Original line number Diff line number Diff line <template> <div> <job-details-provider auto-refresh :jobId="job_id" @update:result="updateJob" /> <job-details-provider auto-refresh :jobId="job_id" :stdout_start=stdout_position :stdout_char_count=stdout_char_count @update:result="updateJob" /> <h3>Job Information</h3> <table id="job-information" class="tabletip info_data_table"> <tbody> Loading Loading @@ -37,7 +37,7 @@ </td> </tr> <code-row v-if="job" id="command-line" :code-label="'Command Line'" :code-item="job.command_line" /> <code-row v-if="job" id="stdout" :code-label="'Tool Standard Output'" :code-item="job.tool_stdout" /> <code-row v-if="job" id="stdout" :code-label="'Tool Standard Output'" :code-item="stdout_text" /> <code-row v-if="job" id="stderr" :code-label="'Tool Standard Error'" :code-item="job.tool_stderr" /> <code-row v-if="job && job.traceback" Loading Loading @@ -103,6 +103,9 @@ export default { data() { return { job: null, stdout_position: 0, stdout_char_count: 50000, stdout_text: "", }; }, computed: { Loading @@ -115,12 +118,30 @@ export default { return this.job && !JOB_STATES_MODEL.NON_TERMINAL_STATES.includes(this.job.state); }, }, updated() { try { const stdout_block = document.querySelector("#stdout").querySelector(".code"); // if user is scrolled above the bottom of the code element, then no need to update the stdout if (stdout_block.scrollTop <= stdout_block.scrollHeight - 3000) { this.stdout_char_count = 0; } else { this.stdout_char_count = 50000; } } catch(exception) { console.log(exception); } }, methods: { getAppRoot() { return getAppRoot(); }, updateJob(job) { this.job = job; // Keep stdout in memory and only fetch new text via JobProvider if (job.tool_stdout != null) { this.stdout_text += job.tool_stdout; this.stdout_position += job.tool_stdout.length; } }, }, }; Loading
client/src/components/providers/JobProvider.js +2 −2 Original line number Diff line number Diff line Loading @@ -5,8 +5,8 @@ import { rethrowSimple } from "utils/simple-error"; import { stateIsTerminal } from "./utils"; import { cleanPaginationParameters } from "./utils"; async function jobDetails({ jobId }) { const url = `${getAppRoot()}api/jobs/${jobId}?full=True`; async function jobDetails({ jobId, stdout_start = 0, stdout_char_count = 0 }) { const url = `${getAppRoot()}api/jobs/${jobId}?full=True&stdout_start_pos=${stdout_start}&stdout_count=${stdout_char_count}`; try { const { data } = await axios.get(url); return data; Loading
lib/galaxy/jobs/runners/pulsar.py +5 −0 Original line number Diff line number Diff line Loading @@ -8,6 +8,7 @@ import logging import os import re import subprocess from pathlib import Path from time import sleep import packaging.version Loading Loading @@ -644,6 +645,10 @@ class PulsarJobRunner(AsynchronousJobRunner): run_results = client.full_status() remote_metadata_directory = run_results.get("metadata_directory", None) stdout = run_results.get("stdout", "") if stdout == "": stdout_path = Path(job_wrapper.working_directory) / "outputs" / "tool_stdout" stdout_file = open(stdout_path, "r") stdout = stdout_file.read() stderr = run_results.get("stderr", "") exit_code = run_results.get("returncode", None) pulsar_outputs = PulsarOutputs.from_status_response(run_results) Loading
lib/galaxy/managers/jobs.py +15 −2 Original line number Diff line number Diff line import json import logging import typing from pathlib import Path from datetime import ( date, datetime, Loading Loading @@ -56,7 +57,7 @@ from galaxy.util.search import ( parse_filters_structured, RawTextTerm, ) import traceback log = logging.getLogger(__name__) Loading Loading @@ -219,7 +220,7 @@ class JobManager: ) return self.job_lock() def get_accessible_job(self, trans, decoded_job_id): def get_accessible_job(self, trans, decoded_job_id, stdout_start_pos=-1, stdout_count=0): job = trans.sa_session.query(trans.app.model.Job).filter(trans.app.model.Job.id == decoded_job_id).first() if job is None: raise ObjectNotFound() Loading @@ -236,6 +237,18 @@ class JobManager: if not self.dataset_manager.is_accessible(data_assoc.dataset.dataset, trans.user): raise ItemAccessibilityException("You are not allowed to rerun this job.") trans.sa_session.refresh(job) # If stdout_count and stdout_start_pos are good values, then load standard out and add it to status if job.state == job.states.RUNNING and stdout_count > 0 and stdout_start_pos > -1: try: working_directory = trans.app.object_store.get_filename(job, base_dir="job_work", dir_only=True, obj_dir=True) stdout_path = Path(working_directory) / "outputs" / "tool_stdout" stdout_file = open(stdout_path, "r") stdout_file.seek(stdout_start_pos) job.job_stdout = stdout_file.read(stdout_count) job.tool_stdout = job.job_stdout except Exception as e: log.error("Could not read STDOUT: %s", e) return job def stop(self, job, message=None): Loading
lib/galaxy/webapps/galaxy/api/job_files.py +5 −1 Original line number Diff line number Diff line Loading @@ -104,6 +104,10 @@ class JobFilesAPIController(BaseGalaxyAPIController): target_dir = os.path.dirname(path) util.safe_makedirs(target_dir) try: if os.path.exists(path): with open(path, 'ab') as dest: shutil.copyfileobj(open(input_file.name, 'rb'), dest) else: shutil.move(input_file.name, path) finally: try: Loading