Skip to content

Commit 513fce3

Browse files
authored
Enabled outputs for failed jobs (#270)
* Enabled outputs for failed jobs * Removed new status, enabled outputs for failed jobs * Ran eslint * Removed redundant value
1 parent 6358012 commit 513fce3

File tree

4 files changed

+69
-26
lines changed

4 files changed

+69
-26
lines changed

jupyter_scheduler/executors.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ def process(self):
5656
self.before_start()
5757
try:
5858
self.execute()
59+
except CellExecutionError as e:
60+
self.on_failure(e)
5961
except Exception as e:
6062
self.on_failure(e)
6163
else:
@@ -135,7 +137,7 @@ def execute(self):
135137
try:
136138
ep.preprocess(nb)
137139
except CellExecutionError as e:
138-
pass
140+
raise e
139141
finally:
140142
for output_format in job.output_formats:
141143
cls = nbconvert.get_exporter(output_format)

jupyter_scheduler/job_files_manager.py

Lines changed: 12 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from jupyter_server.utils import ensure_async
88

99
from jupyter_scheduler.scheduler import BaseScheduler
10-
from jupyter_scheduler.utils import resolve_path
1110

1211

1312
class JobFilesManager:
@@ -71,9 +70,12 @@ def download_tar(self, archive_format: str = "tar"):
7170
with tarfile.open(fileobj=f, mode=read_mode) as tar:
7271
filepaths = self.generate_filepaths()
7372
for input_filepath, output_filepath in filepaths:
74-
input_file = tar.extractfile(member=input_filepath)
75-
with fsspec.open(output_filepath, mode="wb") as output_file:
76-
output_file.write(input_file.read())
73+
try:
74+
input_file = tar.extractfile(member=input_filepath)
75+
with fsspec.open(output_filepath, mode="wb") as output_file:
76+
output_file.write(input_file.read())
77+
except Exception as e:
78+
pass
7779

7880
def download(self):
7981
if not self.staging_paths:
@@ -86,6 +88,9 @@ def download(self):
8688
else:
8789
filepaths = self.generate_filepaths()
8890
for input_filepath, output_filepath in filepaths:
89-
with fsspec.open(input_filepath) as input_file:
90-
with fsspec.open(output_filepath, mode="wb") as output_file:
91-
output_file.write(input_file.read())
91+
try:
92+
with fsspec.open(input_filepath) as input_file:
93+
with fsspec.open(output_filepath, mode="wb") as output_file:
94+
output_file.write(input_file.read())
95+
except Exception as e:
96+
pass

src/components/job-row.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ function JobFiles(props: {
5151
job: Scheduler.IDescribeJob;
5252
app: JupyterFrontEnd;
5353
}): JSX.Element | null {
54-
if (props.job.status !== 'COMPLETED') {
54+
if (!(props.job.status === 'COMPLETED' || props.job.status === 'FAILED')) {
5555
return null;
5656
}
5757

@@ -111,7 +111,12 @@ export function buildJobRow(
111111
);
112112

113113
const cellContents: React.ReactNode[] = [
114-
<Link onClick={() => showDetailView(job.job_id)}>{job.name}</Link>,
114+
<Link
115+
onClick={() => showDetailView(job.job_id)}
116+
title={`Open detail view for "${job.name}"`}
117+
>
118+
{job.name}
119+
</Link>,
115120
inputFile ? (
116121
<JobFileLink app={app} jobFile={inputFile}>
117122
{job.input_filename}
@@ -120,9 +125,10 @@ export function buildJobRow(
120125
job.input_filename
121126
),
122127
<>
123-
{!job.downloaded && job.status === 'COMPLETED' && (
124-
<DownloadFilesButton app={app} job={job} reload={reload} />
125-
)}
128+
{!job.downloaded &&
129+
(job.status === 'COMPLETED' || job.status === 'FAILED') && (
130+
<DownloadFilesButton app={app} job={job} reload={reload} />
131+
)}
126132
<JobFiles job={job} app={app} />
127133
</>,
128134
<Timestamp job={job} />,

src/mainviews/detail-view/job-detail.tsx

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import React, { useState } from 'react';
1+
import React, { useCallback, useState } from 'react';
22

33
import { JupyterFrontEnd } from '@jupyterlab/application';
44

@@ -7,7 +7,7 @@ import {
77
ConfirmDialogStopButton
88
} from '../../components/confirm-dialog-buttons';
99
import { JobFileLink } from '../../components/job-file-link';
10-
import { SchedulerService } from '../../handler';
10+
import { Scheduler, SchedulerService } from '../../handler';
1111
import { useTranslator } from '../../hooks';
1212
import { ICreateJobModel, IJobDetailModel, JobsView } from '../../model';
1313
import { Scheduler as SchedulerTokens } from '../../tokens';
@@ -65,6 +65,31 @@ export function JobDetail(props: IJobDetailProps): JSX.Element {
6565

6666
const ss = new SchedulerService({});
6767

68+
const translateStatus = useCallback(
69+
(status: Scheduler.Status) => {
70+
// This may look inefficient, but it's intended to call the `trans` function
71+
// with distinct, static values, so that code analyzers can pick up all the
72+
// needed source strings.
73+
switch (status) {
74+
case 'CREATED':
75+
return trans.__('Created');
76+
case 'QUEUED':
77+
return trans.__('Queued');
78+
case 'COMPLETED':
79+
return trans.__('Completed');
80+
case 'FAILED':
81+
return trans.__('Failed');
82+
case 'IN_PROGRESS':
83+
return trans.__('In progress');
84+
case 'STOPPED':
85+
return trans.__('Stopped');
86+
case 'STOPPING':
87+
return trans.__('Stopping');
88+
}
89+
},
90+
[trans]
91+
);
92+
6893
const handleDeleteJob = async () => {
6994
await ss.deleteJob(props.model.jobId ?? '');
7095
props.setJobsView(JobsView.ListJobs);
@@ -90,15 +115,17 @@ export function JobDetail(props: IJobDetailProps): JSX.Element {
90115

91116
const ButtonBar = (
92117
<Stack direction="row" gap={2} justifyContent="flex-end" flexWrap={'wrap'}>
93-
{props.model.downloaded === false && props.model.status === 'COMPLETED' && (
94-
<Button
95-
variant="outlined"
96-
onClick={downloadFiles}
97-
disabled={downloading}
98-
>
99-
{trans.__('Download Job Files')}
100-
</Button>
101-
)}
118+
{props.model.downloaded === false &&
119+
(props.model.status === 'COMPLETED' ||
120+
props.model.status === 'FAILED') && (
121+
<Button
122+
variant="outlined"
123+
onClick={downloadFiles}
124+
disabled={downloading}
125+
>
126+
{trans.__('Download Job Files')}
127+
</Button>
128+
)}
102129
{props.model.status === 'IN_PROGRESS' && (
103130
<ConfirmDialogStopButton
104131
handleStop={handleStopJob}
@@ -140,7 +167,10 @@ export function JobDetail(props: IJobDetailProps): JSX.Element {
140167
}
141168
],
142169
[
143-
{ value: props.model.status ?? '', label: trans.__('Status') },
170+
{
171+
value: translateStatus(props.model.status!),
172+
label: trans.__('Status')
173+
},
144174
{
145175
value: timestampLocalize(props.model.createTime ?? ''),
146176
label: trans.__('Created at')
@@ -165,7 +195,7 @@ export function JobDetail(props: IJobDetailProps): JSX.Element {
165195
];
166196

167197
const hasOutputs =
168-
props.model.status === 'COMPLETED' &&
198+
(props.model.status === 'COMPLETED' || props.model.status === 'FAILED') &&
169199
props.model.job_files.some(
170200
jobFile => jobFile.file_format !== 'input' && jobFile.file_path
171201
);

0 commit comments

Comments
 (0)