Skip to content

Commit 3e527cd

Browse files
committed
Added run status modal
1 parent 9267275 commit 3e527cd

File tree

5 files changed

+258
-38
lines changed

5 files changed

+258
-38
lines changed

components/src/lib/types/api.d.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -298,6 +298,13 @@ export type HistoryItemV2 = {
298298
task_group_dump: TaskGroupV2;
299299
};
300300

301+
export type HistoryUnit = {
302+
id: number;
303+
status: JobStatus;
304+
zarr_urls: string[];
305+
logfile: string | null;
306+
};
307+
301308
export type HistoryRunAggregated = {
302309
id: number;
303310
workflowtask_dump: WorkflowTaskV2;

src/lib/components/jobs/ImagesStatus.svelte

Lines changed: 9 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -28,12 +28,12 @@
2828
{#if status.num_done_images === 0 && status.num_failed_images === 0 && status.num_submitted_images === 0}
2929
<span class="d-flex">
3030
{#if status.status === 'done'}
31-
<i class="image-status-icon bi bi-check text-success pe-1" />
31+
<i class="status-icon bi bi-check text-success pe-1" />
3232
{:else if status.status === 'failed'}
33-
<i class="image-status-icon bi bi-x text-danger pe-1" />
33+
<i class="status-icon bi bi-x text-danger pe-1" />
3434
{:else if status.status === 'submitted'}
3535
<div
36-
class="mt-1 pe-1 spinner-border spinner-border-sm text-primary image-status"
36+
class="mt-1 pe-1 spinner-border spinner-border-sm text-primary status-wrapper"
3737
role="status"
3838
>
3939
<span class="visually-hidden">Loading...</span>
@@ -50,11 +50,11 @@
5050
imagesStatusModal.open(projectId, datasetId, workflowTask.id)}
5151
>
5252
<span class="d-flex">
53-
<span class="pe-1 image-status text-primary">
53+
<span class="pe-1 status-wrapper text-primary">
5454
{status.num_submitted_images}
5555
</span>
5656
<div
57-
class="mt-1 pe-1 spinner-border spinner-border-sm text-primary image-status"
57+
class="mt-1 pe-1 spinner-border spinner-border-sm text-primary status-wrapper"
5858
role="status"
5959
>
6060
<span class="visually-hidden">Loading...</span>
@@ -69,10 +69,10 @@
6969
on:click={() => imagesStatusModal.open(projectId, datasetId, workflowTask.id)}
7070
>
7171
<span class="d-flex">
72-
<span class="image-status text-success ps-1">
72+
<span class="status-wrapper text-success ps-1">
7373
{status.num_done_images}
7474
</span>
75-
<i class="image-status-icon bi bi-check text-success pe-1" />
75+
<i class="status-icon bi bi-check text-success pe-1" />
7676
</span>
7777
</button>
7878
{/if}
@@ -86,10 +86,10 @@
8686
on:click={() => imagesStatusModal.open(projectId, datasetId, workflowTask.id)}
8787
>
8888
<span class="d-flex">
89-
<span class="image-status text-danger ps-1">
89+
<span class="status-wrapper text-danger ps-1">
9090
{status.num_failed_images}
9191
</span>
92-
<i class="image-status-icon bi bi-x text-danger pe-1" />
92+
<i class="status-icon bi bi-x text-danger pe-1" />
9393
</span>
9494
</button>
9595
{/if}
@@ -103,21 +103,3 @@
103103
{/if}
104104
{/if}
105105

106-
<style>
107-
:global(.image-status-icon) {
108-
font-size: 160%;
109-
font-weight: bold;
110-
margin: 0 -5px -5px -5px;
111-
line-height: 0;
112-
display: block;
113-
}
114-
115-
:global(.active .image-status),
116-
:global(.active .image-status-icon) {
117-
color: #fff !important;
118-
}
119-
120-
.status-modal-btn:hover span {
121-
text-decoration: underline;
122-
}
123-
</style>

src/lib/components/jobs/RunStatus.svelte

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,20 @@
11
<script>
2+
/** @type {number} */
3+
export let projectId;
4+
/** @type {number} */
5+
export let datasetId;
6+
/** @type {number} */
7+
export let workflowTaskId;
28
/** @type {import('fractal-components/types/api').HistoryRunAggregated} */
39
export let run;
10+
/** @type {number} */
11+
export let index;
12+
/** @type {import('./RunStatusModal.svelte').default} */
13+
export let runStatusModal;
14+
15+
function openModal() {
16+
runStatusModal.open(projectId, run.id, datasetId, workflowTaskId, index);
17+
}
418
</script>
519

620
<span class="d-flex">
@@ -9,14 +23,14 @@
923
<button
1024
aria-label="Submitted images"
1125
class="status-modal-btn btn btn-link text-decoration-none p-0"
12-
on:click={() => {}}
26+
on:click={openModal}
1327
>
1428
<span class="d-flex">
15-
<span class="pe-1 image-status text-primary">
29+
<span class="pe-1 status-wrapper text-primary">
1630
{run.num_submitted_units}
1731
</span>
1832
<div
19-
class="mt-1 pe-1 spinner-border spinner-border-sm text-primary image-status"
33+
class="mt-1 pe-1 spinner-border spinner-border-sm text-primary status-wrapper"
2034
role="status"
2135
>
2236
<span class="visually-hidden">Loading...</span>
@@ -28,13 +42,13 @@
2842
<button
2943
aria-label="Done images"
3044
class="status-modal-btn btn btn-link text-decoration-none p-0"
31-
on:click={() => {}}
45+
on:click={openModal}
3246
>
3347
<span class="d-flex">
34-
<span class="image-status text-success ps-1">
48+
<span class="status-wrapper text-success ps-1">
3549
{run.num_done_units}
3650
</span>
37-
<i class="image-status-icon bi bi-check text-success pe-1" />
51+
<i class="status-icon bi bi-check text-success pe-1" />
3852
</span>
3953
</button>
4054
{/if}
@@ -45,13 +59,13 @@
4559
<button
4660
aria-label="Failed images"
4761
class="status-modal-btn btn btn-link text-decoration-none p-0"
48-
on:click={() => {}}
62+
on:click={openModal}
4963
>
5064
<span class="d-flex">
51-
<span class="image-status text-danger ps-1">
65+
<span class="status-wrapper text-danger ps-1">
5266
{run.num_failed_units}
5367
</span>
54-
<i class="image-status-icon bi bi-x text-danger pe-1" />
68+
<i class="status-icon bi bi-x text-danger pe-1" />
5569
</span>
5670
</button>
5771
{/if}
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
<script>
2+
import { getAlertErrorFromResponse } from '$lib/common/errors';
3+
import { extractJobErrorParts } from '$lib/common/job_utilities';
4+
import ExpandableLog from '../common/ExpandableLog.svelte';
5+
import Modal from '../common/Modal.svelte';
6+
import Paginator from '../common/Paginator.svelte';
7+
8+
let loading = false;
9+
let page = 1;
10+
let pageSize = 10;
11+
let totalCount = 0;
12+
13+
/** @type {number} */
14+
let projectId;
15+
/** @type {number} */
16+
let historyRunId;
17+
/** @type {number} */
18+
let datasetId;
19+
/** @type {number} */
20+
let workflowTaskId;
21+
/** @type {number} */
22+
let historyRunIndex;
23+
24+
/** @type {Modal} */
25+
let modal;
26+
27+
/** @type {(import('fractal-components/types/api').Pagination<import('fractal-components/types/api').HistoryUnit>)|undefined} */
28+
let data = undefined;
29+
30+
let loadingLogs = false;
31+
/** @type {number|undefined} */
32+
let selectedLogUnit = undefined;
33+
/** @type {Array<{text: string, highlight: boolean}>} */
34+
let logParts = [];
35+
let loadedLogsStatus = '';
36+
37+
/**
38+
* @param {number} _projectId
39+
* @param {number} _historyRunId
40+
* @param {number} _datasetId
41+
* @param {number} _workflowTaskId
42+
* @param {number} _historyRunIndex
43+
*/
44+
export async function open(
45+
_projectId,
46+
_historyRunId,
47+
_datasetId,
48+
_workflowTaskId,
49+
_historyRunIndex
50+
) {
51+
loading = true;
52+
projectId = _projectId;
53+
historyRunId = _historyRunId;
54+
datasetId = _datasetId;
55+
workflowTaskId = _workflowTaskId;
56+
historyRunIndex = _historyRunIndex;
57+
data = undefined;
58+
page = 1;
59+
pageSize = 10;
60+
modal.show();
61+
await loadRun(page, pageSize);
62+
}
63+
64+
function onClose() {
65+
data = undefined;
66+
}
67+
68+
/**
69+
* @param {number} currentPage
70+
* @param {number} selectedPageSize
71+
*/
72+
async function loadRun(currentPage, selectedPageSize) {
73+
loading = true;
74+
const url = `/api/v2/project/${projectId}/status/run/${historyRunId}/units?workflowtask_id=${workflowTaskId}&dataset_id=${datasetId}&page=${currentPage}&page_size=${selectedPageSize}`;
75+
const response = await fetch(url);
76+
if (!response.ok) {
77+
loading = false;
78+
modal.displayErrorAlert(await getAlertErrorFromResponse(response));
79+
return;
80+
}
81+
data = await response.json();
82+
if (data) {
83+
page = data.current_page;
84+
pageSize = data.page_size;
85+
totalCount = data.total_count;
86+
}
87+
loading = false;
88+
}
89+
90+
/**
91+
* @param {number} unitId
92+
* @param {string} status
93+
*/
94+
async function loadLogs(unitId, status) {
95+
loadingLogs = true;
96+
const response = await fetch(
97+
`/api/v2/project/${projectId}/status/unit-log?history_run_id=${historyRunId}&history_unit_id=${unitId}&workflowtask_id=${workflowTaskId}&dataset_id=${datasetId}`,
98+
{
99+
method: 'GET'
100+
}
101+
);
102+
if (!response.ok) {
103+
modal.displayErrorAlert(await getAlertErrorFromResponse(response));
104+
loadingLogs = false;
105+
return;
106+
}
107+
const log = await response.json();
108+
loadedLogsStatus = status;
109+
if (status === 'failed') {
110+
logParts = extractJobErrorParts(log, false, true);
111+
} else {
112+
logParts = [{ text: log, highlight: false }];
113+
}
114+
selectedLogUnit = unitId;
115+
loadingLogs = false;
116+
}
117+
</script>
118+
119+
<Modal id="runStatusModal" bind:this={modal} fullscreen={true} bodyCss="p-0" {onClose}>
120+
<svelte:fragment slot="header">
121+
<h1 class="modal-title fs-5">
122+
{#if selectedLogUnit && !loadingLogs}
123+
Run {historyRunIndex} - Logs for unit #{selectedLogUnit}
124+
{:else}
125+
Run {historyRunIndex}
126+
{/if}
127+
</h1>
128+
</svelte:fragment>
129+
<svelte:fragment slot="body">
130+
<div id="errorAlert-runStatusModal" class="mb-2" />
131+
{#if loading && !data}
132+
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true" />
133+
{:else if selectedLogUnit && !loadingLogs}
134+
<div class="row">
135+
<div class="col">
136+
<ExpandableLog bind:logParts highlight={loadedLogsStatus === 'failed'} />
137+
</div>
138+
</div>
139+
<div class="row">
140+
<div class="col">
141+
<button class="m-2 ms-3 btn btn-primary" on:click={() => (selectedLogUnit = undefined)}>
142+
Back
143+
</button>
144+
</div>
145+
</div>
146+
{:else if data}
147+
{#if data.items.length > 0}
148+
<table class="table table-striped">
149+
<thead>
150+
<tr>
151+
<th>Unit id</th>
152+
<th>Status</th>
153+
<th />
154+
</tr>
155+
</thead>
156+
<tbody>
157+
{#each data.items as run}
158+
<tr>
159+
<td>{run.id}</td>
160+
<td>{run.status || '-'}</td>
161+
<td>
162+
<button class="btn btn-light" on:click={() => loadLogs(run.id, run.status)}>
163+
{#if loadingLogs}
164+
<span
165+
class="spinner-border spinner-border-sm"
166+
role="status"
167+
aria-hidden="true"
168+
/>
169+
{/if}
170+
<i class="bi-list-columns-reverse" /> Logs
171+
</button>
172+
</td>
173+
</tr>
174+
{/each}
175+
</tbody>
176+
</table>
177+
<Paginator
178+
currentPage={page}
179+
{pageSize}
180+
{totalCount}
181+
onPageChange={(currentPage, pageSize) => loadRun(currentPage, pageSize)}
182+
/>
183+
{:else}
184+
<p class="ps-3">No images</p>
185+
{/if}
186+
{/if}
187+
</svelte:fragment>
188+
</Modal>

0 commit comments

Comments
 (0)