feat(web): show what the pipeline is doing while a job runs
"running" for two and a half minutes tells the user nothing. The job page now shows a spinner, the elapsed time, and the pipeline step Nextflow is actually on. - the API streams the Nextflow output into jobs.log as it arrives, instead of keeping it only when the run dies. Writes are throttled to one every 3s, or immediately when a new process starts, and are skipped for a job that has already finished, so a late line cannot overwrite the loader's result. - web/src/lib/progress.ts formats the elapsed time and picks the latest [PROCESS] line. No percentage: the pipeline cannot honestly estimate one. - the spinner animates only under prefers-reduced-motion: no-preference. Verified on a live run: the page showed "VEP (tiny)" for the duration, then the variant table replaced it on success. Tests: api 53, web 20; ruff, mypy, svelte-check clean.
This commit is contained in:
@@ -2,6 +2,7 @@
|
||||
import { onDestroy, onMount } from 'svelte';
|
||||
import { api, type Job, type VariantPage } from '$lib/api';
|
||||
import { poll } from '$lib/poll';
|
||||
import { formatElapsed, latestStep } from '$lib/progress';
|
||||
|
||||
const POLL_MS = 3000;
|
||||
const finished = (j: Job) => j.status === 'succeeded' || j.status === 'failed';
|
||||
@@ -15,6 +16,18 @@
|
||||
let busy = $state(false);
|
||||
let error = $state<string | null>(null);
|
||||
let stopPolling: (() => void) | null = null;
|
||||
let now = $state(Date.now());
|
||||
|
||||
const running = $derived(!!job && !finished(job));
|
||||
const elapsedMs = $derived(job ? now - Date.parse(job.created_at) : 0);
|
||||
const step = $derived(latestStep(job?.log ?? null));
|
||||
|
||||
// Tick the elapsed time while a run is in flight; polling refreshes the step itself.
|
||||
$effect(() => {
|
||||
if (!running) return;
|
||||
const tick = setInterval(() => (now = Date.now()), 1000);
|
||||
return () => clearInterval(tick);
|
||||
});
|
||||
|
||||
onMount(async () => {
|
||||
try {
|
||||
@@ -79,6 +92,12 @@
|
||||
|
||||
{#if job}
|
||||
<p>Job <span class="hgvs">{job.id.slice(0, 8)}</span>: <span class="status {job.status}">{job.status}</span></p>
|
||||
{#if running}
|
||||
<p class="progress" aria-live="polite">
|
||||
<span class="spinner" aria-hidden="true"></span>
|
||||
Annotating for {formatElapsed(elapsedMs)}{#if step} · <span class="hgvs">{step}</span>{/if}
|
||||
</p>
|
||||
{/if}
|
||||
{#if job.status === 'failed' && job.log}
|
||||
<pre class="hgvs" style="white-space:pre-wrap; background:white; border:1px solid var(--line); padding:0.75rem; max-height:16rem; overflow:auto">{job.log}</pre>
|
||||
{/if}
|
||||
|
||||
Reference in New Issue
Block a user