Skip to content

Commit d0c2c48

Browse files
authored
Merge pull request #103 from fractal-analytics-platform/v0.3
Client v0.3 release
2 parents ca18457 + 25dd172 commit d0c2c48

File tree

10 files changed

+405
-302
lines changed

10 files changed

+405
-302
lines changed

package-lock.json

Lines changed: 290 additions & 258 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -13,22 +13,22 @@
1313
"format": "prettier --write ."
1414
},
1515
"devDependencies": {
16-
"@playwright/test": "^1.25.0",
16+
"@playwright/test": "^1.32.3",
1717
"@sveltejs/adapter-auto": "^2.0.0",
18-
"@sveltejs/kit": "^1.8.3",
19-
"eslint": "^8.16.0",
18+
"@sveltejs/kit": "^1.15.7",
19+
"eslint": "^8.38.0",
2020
"eslint-config-prettier": "^8.3.0",
2121
"eslint-plugin-svelte3": "^4.0.0",
2222
"prettier": "^2.6.2",
2323
"prettier-plugin-svelte": "^2.7.0",
24-
"svelte": "^3.44.0",
24+
"svelte": "^3.58.0",
2525
"svelte-check": "^2.7.1",
2626
"typescript": "^4.7.4",
27-
"vite": "^4.1.3"
27+
"vite": "^4.2.2"
2828
},
2929
"type": "module",
3030
"dependencies": {
31-
"@vincjo/datatables": "^1.5.2",
31+
"@vincjo/datatables": "^1.6.0",
3232
"sweetalert2": "^11.4.37"
3333
}
3434
}

src/lib/api/v1/project/project_api.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,6 @@ export async function createProject(data) {
2121

2222
const requestData = {
2323
name: data.get('projectName'),
24-
project_dir: data.get('projectDirectory')
2524
}
2625

2726
const headers = new Headers()

src/lib/api/v1/workflow/workflow_api.js

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -251,3 +251,42 @@ export async function applyWorkflow(projectId, workflowId, formData) {
251251

252252
throw new PostResourceException(await response.json())
253253
}
254+
255+
// Download a workflow job log as zip file
256+
export async function downloadWorkflowJobLog(workflowJobId) {
257+
258+
const response = await fetch(PUBLIC_FRACTAL_SERVER_HOST + `/api/v1/job/download/${workflowJobId}`, {
259+
method: 'GET',
260+
credentials: 'include',
261+
mode: 'cors'
262+
})
263+
264+
if (response.ok) {
265+
// The API uses a StreamResponse to stream the zip file to the client
266+
// Get a stream reader from the body
267+
const reader = response.body.getReader()
268+
// Read the stream and create a blob
269+
const readableStream = new ReadableStream({
270+
start(controller) {
271+
return pump()
272+
function pump() {
273+
return reader.read().then(({ done, value }) => {
274+
// When no more data needs to be consumed, close the stream
275+
if (done) {
276+
controller.close()
277+
return
278+
}
279+
// Enqueue the next data chunk into our target stream
280+
controller.enqueue(value)
281+
return pump()
282+
})
283+
}
284+
}
285+
})
286+
// Create a blob from the stream
287+
const blob = await new Response(readableStream).blob()
288+
return blob
289+
}
290+
291+
throw new Error('The client was not able to retrieve the workflow job log from the server')
292+
}

src/lib/components/projects/ProjectInfoModal.svelte

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,6 @@
3535
<ul class="list-group">
3636
<li class="list-group-item list-group-item-light fw-bold">Name</li>
3737
<li class="list-group-item">{project?.name}</li>
38-
<li class="list-group-item list-group-item-light fw-bold">Directory</li>
39-
<li class="list-group-item">{project?.project_dir}</li>
4038
<li class="list-group-item list-group-item-light fw-bold">Read only</li>
4139
<li class="list-group-item">{project?.read_only ? "Yes" : "No"}</li>
4240
</ul>

src/lib/components/projects/ProjectsList.svelte

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -61,13 +61,6 @@
6161
<input name="projectName" type="text" class="form-control">
6262
</div>
6363
</div>
64-
<div class="col-auto">
65-
<div class="input-group">
66-
<div class="input-group-text">Project directory</div>
67-
<input name="projectDirectory" type="text" class="form-control">
68-
</div>
69-
</div>
70-
7164
<div class="col-auto">
7265
<button type="submit" class="btn btn-primary">Create</button>
7366
</div>

src/lib/components/projects/WorkflowImport.svelte

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,16 @@
1717
cancel()
1818
1919
const workflowFile = data.get('workflowFile')
20-
console.log(workflowFile)
2120
2221
if (workflowFile.name == '') {
2322
throw new Error('No workflow file specified')
2423
}
2524
26-
const workflowMetadata = await workflowFile.text().then(data => JSON.parse(data))
27-
console.log(workflowMetadata)
25+
const workflowMetadata = await workflowFile.text()
26+
.then(data => JSON.parse(data))
27+
.catch(() => {
28+
throw new Error('The format of the selected file is not valid. Please select a valid workflow file.')
29+
})
2830
2931
// Request workflow import
3032
importing = true
@@ -58,7 +60,7 @@
5860

5961
<div class="mb-3">
6062
<label for="workflowFile" class="form-label">Select a workflow file</label>
61-
<input class="form-control" type="file" name="workflowFile" id="workflowFile">
63+
<input class="form-control" accept="application/json" type="file" name="workflowFile" id="workflowFile">
6264
</div>
6365

6466
<button class="btn btn-primary" disabled={importing}>

src/lib/components/projects/controller.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,15 @@ export async function loadProjectContext(projectId) {
77
const context = get(contextProject)
88

99
// If the context is already loaded, do not reload it
10-
if (context.project !== undefined) return
10+
if (context.project !== undefined) {
11+
const currentProjectId = context.project.id
12+
13+
// If the project loaded is the same as the one we want to load, do not reload it
14+
if (currentProjectId === projectId) {
15+
return
16+
}
17+
18+
}
1119

1220
await getProject(projectId)
1321
.then((project) => {

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

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
<script>
22
import { onMount } from 'svelte'
33
import { page } from '$app/stores'
4-
import { DataHandler } from '@vincjo/datatables'
4+
import { DataHandler, check } from '@vincjo/datatables'
55
import { loadProjectContext } from '$lib/components/projects/controller'
66
import { contextProject } from '$lib/stores/projectStores'
77
import { getJobs } from '$lib/api/v1/project/project_api'
8+
import { downloadWorkflowJobLog } from '$lib/api/v1/workflow/workflow_api'
89
import StatusBadge from '$lib/components/jobs/StatusBadge.svelte'
910
import TimestampBadge from '$lib/components/jobs/TimestampBadge.svelte'
1011
import JobInfoModal from '$lib/components/jobs/JobInfoModal.svelte'
@@ -45,37 +46,37 @@
4546
// Set filters
4647
const idFilter = $page.url.searchParams.get('id')
4748
if (idFilter) {
48-
tableHandler.filter(idFilter, 'id')
49+
tableHandler.filter(idFilter, 'id', check.isEqualTo)
4950
}
5051
5152
let workflowQueryFilter = $page.url.searchParams.get('workflow')
5253
if (workflowQueryFilter) {
53-
workflowFilter = workflowQueryFilter
54+
tableHandler.filter(workflowQueryFilter, 'workflow_id', check.isEqualTo)
5455
}
5556
5657
let inputDatasetQueryFilter = $page.url.searchParams.get('input_dataset')
5758
if (inputDatasetQueryFilter) {
58-
inputDatasetFilter = inputDatasetQueryFilter
59+
tableHandler.filter(inputDatasetQueryFilter, 'input_dataset_id', check.isEqualTo)
5960
}
6061
6162
let outputDatasetQueryFilter = $page.url.searchParams.get('output_dataset')
6263
if (outputDatasetQueryFilter) {
63-
outputDatasetFilter = outputDatasetQueryFilter
64+
tableHandler.filter(outputDatasetQueryFilter, 'output_dataset_id', check.isEqualTo)
6465
}
6566
6667
let statusQueryFilter = $page.url.searchParams.get('status')
6768
if (statusQueryFilter) {
68-
statusFilter = statusQueryFilter
69+
tableHandler.filter(statusQueryFilter, 'status', check.isEqualTo)
6970
}
7071
7172
})
7273
7374
setupTableHandler()
7475
7576
// Filters
76-
$: tableHandler.filter(workflowFilter, 'workflow_id')
77-
$: tableHandler.filter(inputDatasetFilter, 'input_dataset_id')
78-
$: tableHandler.filter(outputDatasetFilter, 'output_dataset_id')
77+
$: tableHandler.filter(workflowFilter, 'workflow_id', check.isEqualTo)
78+
$: tableHandler.filter(inputDatasetFilter, 'input_dataset_id', check.isEqualTo)
79+
$: tableHandler.filter(outputDatasetFilter, 'output_dataset_id', check.isEqualTo)
7980
$: tableHandler.filter(statusFilter, 'status')
8081
8182
async function loadProjectJobs() {
@@ -99,6 +100,30 @@
99100
rows = tableHandler.getRows()
100101
}
101102
103+
async function handleJobLogsDownload(jobId) {
104+
console.log('Download job')
105+
106+
const downloadUrl = await downloadWorkflowJobLog(jobId)
107+
.then(blob => {
108+
// Create a download URL for the file
109+
return URL.createObjectURL(blob)
110+
})
111+
.catch(error => {
112+
console.error(error)
113+
})
114+
115+
// Create a hidden link within the page to trigger the download of the file
116+
const link = document.createElement('a')
117+
// Append the link to the body
118+
document.body.appendChild(link)
119+
// Set the href of the link to the download URL
120+
link.href = downloadUrl
121+
link.download = `${jobId}_logs.zip`
122+
// Trigger the download
123+
link.click()
124+
document.body.removeChild(link)
125+
}
126+
102127
</script>
103128

104129
<div class="d-flex justify-content-between align-items-center">
@@ -246,6 +271,7 @@
246271
<i class="bi-list-columns-reverse"></i>
247272
Logs
248273
</button>
274+
<button class="btn btn-light" on:click={handleJobLogsDownload.bind(this, row.id)}><i class="bi-arrow-down-circle"></i></button>
249275
{/if}
250276
</td>
251277
</tr>

src/routes/projects/[id]/workflows/[workflowId]/+page.svelte

Lines changed: 20 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -398,21 +398,27 @@
398398
{#if workflow !== undefined && updatableWorkflowList.length == 0 }
399399
<p class="text-center mt-3">No workflow tasks yet, add one.</p>
400400
{:else if workflow !== undefined}
401-
<ul class="list-group list-group-flush">
402-
{#each updatableWorkflowList as workflowTask, i }
403-
<li class="list-group-item" data-fs-target={workflowTask.id}>
404-
<div class="d-flex justify-content-between align-items-center">
405-
<div>
406-
{workflowTask.task.name} #{workflowTask.id}
407-
</div>
408-
<div>
409-
<button class="btn btn-light" on:click|preventDefault={moveWorkflowTask.bind(this, i, 'up')}><i class="bi-arrow-up"></i></button>
410-
<button class="btn btn-light" on:click|preventDefault={moveWorkflowTask.bind(this, i, 'down')}><i class="bi-arrow-down"></i></button>
401+
{#key updatableWorkflowList }
402+
<ul class="list-group list-group-flush">
403+
{#each updatableWorkflowList as workflowTask, i }
404+
<li class="list-group-item" data-fs-target={workflowTask.id}>
405+
<div class="d-flex justify-content-between align-items-center">
406+
<div>
407+
{workflowTask.task.name} #{workflowTask.id}
408+
</div>
409+
<div>
410+
{#if i !== 0 }
411+
<button class="btn btn-light" on:click|preventDefault={moveWorkflowTask.bind(this, i, 'up')}><i class="bi-arrow-up"></i></button>
412+
{/if}
413+
{#if i !== updatableWorkflowList.length - 1 }
414+
<button class="btn btn-light" on:click|preventDefault={moveWorkflowTask.bind(this, i, 'down')}><i class="bi-arrow-down"></i></button>
415+
{/if}
416+
</div>
411417
</div>
412-
</div>
413-
</li>
414-
{/each}
415-
</ul>
418+
</li>
419+
{/each}
420+
</ul>
421+
{/key}
416422
{/if}
417423
418424
</div>

0 commit comments

Comments
 (0)