Skip to content

Commit cdd7bf7

Browse files
authored
Merge pull request #101 from fractal-analytics-platform/job-table-options
Job table options
2 parents bb6ca47 + a8a2eb1 commit cdd7bf7

File tree

5 files changed

+182
-1
lines changed

5 files changed

+182
-1
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { PUBLIC_FRACTAL_SERVER_HOST } from '$env/static/public'
2+
3+
export async function getJob(jobId) {
4+
5+
const response = await fetch(PUBLIC_FRACTAL_SERVER_HOST + `/api/v1/job/${jobId}`, {
6+
method: 'GET',
7+
credentials: 'include',
8+
mode: 'cors'
9+
})
10+
11+
if (response.ok) {
12+
return await response.json()
13+
}
14+
15+
throw new Error('The client was not able to retrieve the job')
16+
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
<script>
2+
import { afterUpdate, onMount } from 'svelte'
3+
import { page } from '$app/stores'
4+
import { loadProjectContext } from '$lib/components/projects/controller'
5+
import { contextProject } from '$lib/stores/projectStores'
6+
import { getJob } from '$lib/api/v1/monitoring/monitoring_api'
7+
import StatusBadge from '$lib/components/jobs/StatusBadge.svelte'
8+
9+
export let workflowJobId = undefined
10+
let job = undefined
11+
let projectName = undefined
12+
let datasets = undefined
13+
let workflows = undefined
14+
let jobWorkflowName = undefined
15+
let jobInputDatasetName = undefined
16+
let jobOutputDatasetName = undefined
17+
let jobStatus = undefined
18+
19+
contextProject.subscribe((value) => {
20+
projectName = value.project?.name
21+
datasets = value.datasets
22+
workflows = value.workflows
23+
})
24+
25+
onMount(async () => {
26+
await loadProjectContext($page.params.id)
27+
})
28+
29+
afterUpdate(async () => {
30+
// If workflowJobId is undefined, do nothing
31+
if (workflowJobId === undefined) return
32+
// Should fetch job info from server
33+
if (job === undefined) await fetchJob()
34+
// Should update jobWorkflowName
35+
jobWorkflowName = workflows.find((workflow) => workflow.id === job.workflow_id).name
36+
// Should update jobInputDatasetName
37+
jobInputDatasetName = datasets.find((dataset) => dataset.id === job.input_dataset_id).name
38+
// Should update jobOutputDatasetName
39+
jobOutputDatasetName = datasets.find((dataset) => dataset.id === job.output_dataset_id).name
40+
// Should update jobStatus
41+
jobStatus = job.status
42+
})
43+
44+
async function fetchJob() {
45+
await getJob(workflowJobId)
46+
.then((data) => {
47+
job = data
48+
})
49+
.catch(error => {
50+
console.error(error)
51+
})
52+
}
53+
54+
</script>
55+
56+
<div class="modal modal-lg" id="workflowJobInfoModal">
57+
<div class="modal-dialog">
58+
<div class="modal-content">
59+
<div class="modal-header d-flex justify-content-between">
60+
<h1 class="h5 modal-title">Workflow Job #{workflowJobId}</h1>
61+
<div class="d-flex align-items-center">
62+
<button class="btn btn-light me-3" on:click={fetchJob}><i class="bi-arrow-clockwise"></i></button>
63+
<button class="btn-close bg-light p-2" data-bs-dismiss="modal"></button>
64+
</div>
65+
</div>
66+
<div class="modal-body">
67+
<div class="row mb-3">
68+
<div class="col-12">
69+
<p class="lead">Workflow job properties</p>
70+
<ul class="list-group">
71+
<li class="list-group-item list-group-item-light fw-bold">Id</li>
72+
<li class="list-group-item">{job?.id}</li>
73+
<li class="list-group-item list-group-item-light fw-bold">Workflow</li>
74+
<li class="list-group-item">{jobWorkflowName}</li>
75+
<li class="list-group-item list-group-item-light fw-bold">Project</li>
76+
<li class="list-group-item">{projectName}</li>
77+
<li class="list-group-item list-group-item-light fw-bold">Input dataset</li>
78+
<li class="list-group-item">{jobInputDatasetName}</li>
79+
<li class="list-group-item list-group-item-light fw-bold">Output dataset</li>
80+
<li class="list-group-item">{jobOutputDatasetName}</li>
81+
<li class="list-group-item list-group-item-light fw-bold">Status</li>
82+
{#key jobStatus }
83+
<li class="list-group-item"><StatusBadge status={jobStatus}></StatusBadge></li>
84+
{/key}
85+
<li class="list-group-item list-group-item-light fw-bold">Working directory</li>
86+
<li class="list-group-item"><code>{job?.working_dir}</code></li>
87+
<li class="list-group-item list-group-item-light fw-bold">User Working directory</li>
88+
<li class="list-group-item"><code>{job?.working_dir_user}</code></li>
89+
</ul>
90+
</div>
91+
</div>
92+
<div class="row">
93+
<div class="col-12">
94+
</div>
95+
</div>
96+
</div>
97+
</div>
98+
</div>
99+
</div>
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
<script>
2+
import { afterUpdate } from 'svelte'
3+
import { getJob } from '$lib/api/v1/monitoring/monitoring_api'
4+
5+
export let workflowJobId = undefined
6+
let logs = ''
7+
8+
afterUpdate(() => {
9+
if (workflowJobId) {
10+
getJob(workflowJobId)
11+
.then((data) => {
12+
logs = data.log
13+
})
14+
.catch(error => {
15+
console.log(error)
16+
})
17+
}
18+
})
19+
20+
</script>
21+
22+
<div class="modal" id="workflowJobLogsModal">
23+
<div class="modal-dialog modal-fullscreen">
24+
<div class="modal-content">
25+
<div class="modal-header">
26+
<h1 class="h5 modal-title">Workflow Job logs</h1>
27+
<button class="btn-close" data-bs-dismiss="modal"></button>
28+
</div>
29+
<div class="modal-body bg-tertiary text-secondary">
30+
<pre>{ logs }</pre>
31+
</div>
32+
</div>
33+
</div>
34+
</div>

src/lib/components/projects/controller.js

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
1+
import { get } from 'svelte/store'
12
import { getProject, getWorkflows } from '$lib/api/v1/project/project_api'
23
import { contextProject } from '$lib/stores/projectStores'
34

45
export async function loadProjectContext(projectId) {
6+
7+
const context = get(contextProject)
8+
9+
// If the context is already loaded, do not reload it
10+
if (context.project !== undefined) return
11+
512
await getProject(projectId)
613
.then((project) => {
714
contextProject.update(context => {

src/routes/projects/[id]/jobs/+page.svelte

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
import { getJobs } from '$lib/api/v1/project/project_api'
88
import StatusBadge from '$lib/components/jobs/StatusBadge.svelte'
99
import TimestampBadge from '$lib/components/jobs/TimestampBadge.svelte'
10+
import JobInfoModal from '$lib/components/jobs/JobInfoModal.svelte'
11+
import JobLogsModal from '$lib/components/jobs/JobLogsModal.svelte'
1012
import Th from '$lib/components/common/filterable/Th.svelte'
1113
1214
// Component properties
@@ -16,6 +18,7 @@
1618
let jobs = []
1719
let tableHandler = undefined
1820
let rows = undefined
21+
let workflowJobInfoId = undefined
1922
2023
// Project context properties
2124
$: project = $contextProject.project
@@ -154,6 +157,7 @@
154157
<Th handler={tableHandler} key="input_dataset_id" label="Input dataset"></Th>
155158
<Th handler={tableHandler} key="output_dataset_id" label="Output dataset"></Th>
156159
<Th handler={tableHandler} key="status" label="Status"></Th>
160+
<th>Options</th>
157161
</tr>
158162
<tr>
159163
<th class="col-3">
@@ -196,14 +200,15 @@
196200
<option value="submitted">Submitted</option>
197201
</select>
198202
</th>
203+
<th></th>
199204
</tr>
200205
</thead>
201206

202207
<tbody>
203208
{#if rows }
204209
{#each $rows as row }
205210
{#key row }
206-
<tr>
211+
<tr class="align-middle">
207212
<td>{row.id}</td>
208213
<td>
209214
<TimestampBadge timestamp={row.start_timestamp}></TimestampBadge>
@@ -226,6 +231,23 @@
226231
<td>
227232
<StatusBadge status={row.status}></StatusBadge>
228233
</td>
234+
<td>
235+
<button class="btn btn-info" on:click={() => {
236+
workflowJobInfoId = row.id
237+
const infoModal = new bootstrap.Modal(document.getElementById('workflowJobInfoModal'),{})
238+
infoModal.show()
239+
}}><i class="bi-info-circle"></i> Info</button>
240+
{#if row.status === 'failed' || row.status === 'done'}
241+
<button class="btn btn-light" on:click={() => {
242+
workflowJobInfoId = row.id
243+
const logsModal = new bootstrap.Modal(document.getElementById('workflowJobLogsModal'),{})
244+
logsModal.show()
245+
}}>
246+
<i class="bi-list-columns-reverse"></i>
247+
Logs
248+
</button>
249+
{/if}
250+
</td>
229251
</tr>
230252
{/key}
231253
{/each}
@@ -235,3 +257,6 @@
235257
{/if}
236258
</div>
237259
{/if}
260+
261+
<JobInfoModal workflowJobId={workflowJobInfoId}></JobInfoModal>
262+
<JobLogsModal workflowJobId={workflowJobInfoId}></JobLogsModal>

0 commit comments

Comments
 (0)