Files
windmill/frontend/src/lib/components/RunChart.svelte
2023-08-20 11:36:32 +02:00

174 lines
3.3 KiB
Svelte

<script lang="ts">
import { Scatter } from 'svelte-chartjs'
import 'chartjs-adapter-date-fns'
import zoomPlugin from 'chartjs-plugin-zoom'
import {
Chart as ChartJS,
Title,
Tooltip,
Legend,
LineElement,
CategoryScale,
LinearScale,
PointElement,
TimeScale,
LogarithmicScale
} from 'chart.js'
import type { CompletedJob } from '$lib/gen'
import { createEventDispatcher } from 'svelte'
import { getDbClockNow } from '$lib/forLater'
export let jobs: CompletedJob[] | undefined = []
export let maxIsNow: boolean = false
const dispatch = createEventDispatcher()
$: success = jobs?.filter((x) => x.success)
$: failed = jobs?.filter((x) => !x.success)
ChartJS.register(
Title,
Tooltip,
Legend,
zoomPlugin,
LineElement,
CategoryScale,
LinearScale,
LogarithmicScale,
PointElement,
TimeScale
)
$: data = {
labels: ['Duration'],
datasets: [
{
// borderColor: 'rgba(99,0,125, .2)',
backgroundColor: '#f87171',
label: 'Failed',
data:
failed?.map((job) => ({
x: job.started_at as any,
y: job.duration_ms,
id: job.id,
path: job.script_path
})) ?? []
},
{
// borderColor: 'rgba(99,0,125, .2)',
backgroundColor: '#4ade80',
label: 'Successful',
data:
success?.map((job) => ({
x: job.started_at as any,
y: job.duration_ms,
id: job.id,
path: job.script_path
})) ?? []
}
]
}
const zoomOptions = {
pan: {
enabled: true,
modifierKey: 'ctrl' as 'ctrl',
onPanComplete: ({ chart }) => {
dispatch('zoom', {
min: addSeconds(new Date(chart.scales.x.min), -1),
max: addSeconds(new Date(chart.scales.x.max), 1)
})
}
},
zoom: {
drag: {
enabled: true
},
mode: 'x' as 'x',
onZoom: ({ chart }) => {
dispatch('zoom', {
min: addSeconds(new Date(chart.scales.x.min), -1),
max: addSeconds(new Date(chart.scales.x.max), 1)
})
}
}
}
function getPath(x: any): string {
return x.path
}
let minTime = addSeconds(new Date(), -300)
let maxTime = getDbClockNow()
$: computeMinMaxTime(jobs)
function computeMinMaxTime(jobs: CompletedJob[] | undefined) {
if (jobs == undefined || jobs?.length == 0) {
minTime = addSeconds(new Date(), -300)
maxTime = getDbClockNow()
return
}
const maxJob = maxIsNow ? getDbClockNow() : new Date(jobs?.[0].started_at)
const minJob = new Date(jobs?.[jobs?.length - 1].started_at)
const diff = (maxJob.getTime() - minJob.getTime()) / 20000
minTime = addSeconds(minJob, -diff)
if (maxIsNow) {
maxTime = maxJob
} else {
maxTime = addSeconds(maxJob, diff)
}
}
function addSeconds(date: Date, seconds: number): Date {
date.setTime(date.getTime() + seconds * 1000)
return date
}
$: scatterOptions = {
responsive: true,
maintainAspectRatio: false,
plugins: {
zoom: zoomOptions,
legend: {
display: false
},
tooltip: {
callbacks: {
label: function (context) {
return getPath(context.raw)
}
}
}
},
scales: {
x: {
type: 'time',
grid: {
display: false
},
min: minTime,
max: maxTime
},
y: {
grid: {
display: false
},
title: {
display: true,
text: 'job duration (ms)'
},
type: 'logarithmic'
}
},
animation: false
} as any
</script>
<div class="relative">
<Scatter {data} options={scatterOptions} />
</div>