added some nice graphs to the tasks

This commit is contained in:
2024-04-18 15:01:36 +01:00
parent f41cdf78df
commit 2fa7680d0b
13 changed files with 407 additions and 75 deletions

View File

@@ -1,11 +0,0 @@
<script lang="ts">
function updateSize() {
let s = document.body.getBoundingClientRect();
document.body.style.minHeight = `${Math.max(window.innerHeight, s.height)}px`;
}
$effect(() => {
updateSize();
});
</script>
<svelte:window on:scroll={updateSize} />

View File

@@ -5,20 +5,20 @@
let x = $state(0);
let y = $state(0);
let div: HTMLDivElement;
let div: HTMLDivElement;
function mouseOver(event: MouseEvent) {
isHovered = true;
x = event.pageX + 5;
y = event.pageY + 5;
}
function focus() {
isHovered = true;
function focus() {
isHovered = true;
const box = div.getBoundingClientRect();
x = box.x + 5;
y = box.y;
}
const box = div.getBoundingClientRect();
x = box.x + 5;
y = box.y;
}
function mouseMove(event: MouseEvent) {
x = event.pageX + 5;
y = event.pageY + 5;
@@ -28,7 +28,15 @@
}
</script>
<div bind:this={div} on:mouseover={mouseOver} on:mouseleave={mouseLeave} on:mousemove={mouseMove} on:focus={focus} role="tooltip">
<div
bind:this={div}
on:mouseover={mouseOver}
on:mouseleave={mouseLeave}
on:mousemove={mouseMove}
on:focus={focus}
role="tooltip"
class="tooltipContainer"
>
<slot />
</div>
@@ -37,6 +45,9 @@
{/if}
<style>
.tooltipContainer {
display: inline-block;
}
.tooltip {
border: 1px solid #ddd;
box-shadow: 1px 1px 1px #ddd;

View File

@@ -42,7 +42,6 @@
import ModelDataPage from './ModelDataPage.svelte';
import 'src/styles/forms.css';
import KeepPageSize from 'src/lib/KeepPageSize.svelte';
let model: Promise<Model> = $state(new Promise(() => {}));
let _model: Model | undefined = $state(undefined);
@@ -137,8 +136,6 @@
{/await}
</svelte:head>
<KeepPageSize />
<main>
{#if [4, 7].includes(_model?.status ?? 0)}
<div class="big-msg warning">The Model is currently training</div>

View File

@@ -27,7 +27,7 @@
}
</script>
<form on:submit|preventDefault={deleteModel} class:submmited>
<form on:submit|preventDefault={deleteModel} class:submmited class="danger-bg">
<fieldset>
<label for="name">
To delete this model please type "{model.name}":

View File

@@ -2,12 +2,12 @@
import { createEventDispatcher } from 'svelte';
import type { Model } from './+page.svelte';
import ModelData from './ModelData.svelte';
import { post } from 'src/lib/requests.svelte';
import { post, showMessage } from 'src/lib/requests.svelte';
import ModelDataPageStatsGraph from './ModelDataPageStatsGraph.svelte';
import type { ModelStats } from './types';
import DeleteZip from './DeleteZip.svelte';
let { model, active } = $props<{ model: Model; active?: boolean }>();
let { model, active }: { model: Model; active?: boolean } = $props();
const dispatch = createEventDispatcher<{ reload: void }>();
@@ -21,26 +21,28 @@
if (!model) return;
try {
stats = await post(`models/class/stats`, { id: model.id });
} catch {
return;
} catch (e) {
showMessage(e);
}
}
</script>
<div class="content" class:selected={active}>
{#if stats}
<ModelDataPageStatsGraph data={stats} />
{/if}
{#if active}
<div class="content selected">
{#if stats}
<ModelDataPageStatsGraph data={stats} />
{/if}
{#if [-6, -2].includes(model.status)}
<DeleteZip {model} on:reload={() => dispatch('reload')} expand />
{/if}
{#if [-6, -2].includes(model.status)}
<DeleteZip {model} on:reload={() => dispatch('reload')} expand />
{/if}
<ModelData
{model}
on:reload={() => {
getData();
dispatch('reload');
}}
/>
</div>
<ModelData
{model}
on:reload={() => {
getData();
dispatch('reload');
}}
/>
</div>
{/if}

View File

@@ -2,14 +2,18 @@
import { post } from 'src/lib/requests.svelte';
import type { Model } from 'src/routes/models/edit/+page.svelte';
import RunModel from './RunModel.svelte';
import TasksTable from './TasksTable.svelte';
import TasksTable from './tasks/TasksTable.svelte';
import Stats from './tasks/Stats.svelte';
const { active, model } = $props<{ active?: boolean; model: Model }>();
const { active, model }: { active?: boolean; model: Model } = $props();
let table: TasksTable;
</script>
<div class="content" class:selected={active}>
<RunModel {model} on:upload={() => table.getList()} on:taskReload={() => table.getList()} />
<TasksTable {model} bind:this={table} />
</div>
{#if active}
<div class="content selected">
<RunModel {model} on:upload={() => table.getList()} on:taskReload={() => table.getList()} />
<TasksTable {model} bind:this={table} />
<Stats {model} />
</div>
{/if}

View File

@@ -0,0 +1,163 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import type { Model } from '../+page.svelte';
import { post, showMessage } from 'src/lib/requests.svelte';
import type { DataPoint, TasksStatsDay } from 'src/types/stats/task';
import {
Chart,
Title,
Tooltip,
Legend,
CategoryScale,
LinearScale,
PieController,
LineController,
Colors,
ArcElement,
PointElement,
LineElement
} from 'chart.js';
import ModelData from '../ModelData.svelte';
Chart.register(
Title,
Tooltip,
Legend,
ArcElement,
CategoryScale,
LinearScale,
PieController,
LineController,
Colors,
PointElement,
LineElement
);
let { model }: { model: Model } = $props();
// Load data
$effect(() => {
getStats();
});
async function getStats() {
if (pieChart && lineChart) {
return;
}
try {
const s = await post('stats/task/model/day', {
model_id: model.id
});
console.log(s);
createPie(s);
createLine(s);
} catch (e) {
showMessage(e);
}
}
let pie: HTMLCanvasElement;
let pieChart: Chart<'pie'> | undefined;
function createPie(s: TasksStatsDay) {
if (pieChart) {
pieChart.destroy();
}
let t = s.total;
pieChart = new Chart(pie, {
type: 'pie',
data: {
labels: [
'Classfication Error',
'Classfication Success',
'Classfication Failure',
'Classfication Preparing',
'Classfication Running',
'Classfication Unknown',
'Non Classfication Error',
'Non Classfication Success'
],
datasets: [
{
label: 'Total',
data: [
t.c_error,
t.c_success,
t.c_failure,
t.c_pre_running,
t.c_running,
t.c_unknown,
t.nc_error,
t.nc_success
]
}
]
},
options: {
animation: false
}
});
}
let line: HTMLCanvasElement;
let lineChart: Chart<'line'> | undefined;
function createLine(s: TasksStatsDay) {
if (lineChart) {
lineChart.destroy();
}
const keys: (keyof DataPoint)[] = Object.keys(s.total) as any;
let names: Record<keyof DataPoint, string> = {
c_error: 'Classfication Error',
c_success: 'Classfication Success',
c_failure: 'Classfication Failure',
c_pre_running: 'Classfication Preparing',
c_running: 'Classfication Running',
c_unknown: 'Classfication Unknown',
nc_error: 'Non Classfication Error',
nc_success: 'Non Classfication Success'
};
let t = s.total;
let labels = new Array(24).fill(0).map((_, i) => i);
lineChart = new Chart(line, {
type: 'line',
data: {
labels: labels,
datasets: keys.map((key) => ({
label: names[key],
data: s.hours.map((e) => e[key])
}))
},
options: {
animation: false
}
});
}
onDestroy(() => {
if (pieChart) {
pieChart.destroy();
pieChart = undefined;
}
if (lineChart) {
lineChart.destroy();
lineChart = undefined;
}
});
</script>
<h1>Statistics (Day)</h1>
<h2>Total</h2>
<div>
<canvas bind:this={pie}></canvas>
</div>
<h2>Hourly</h2>
<div>
<canvas bind:this={line}></canvas>
</div>
<style lang="scss">
canvas {
width: 100%;
}
</style>

View File

@@ -29,12 +29,31 @@
3: 'Task running',
4: 'Task complete'
};
export const TaskTypeIcons: Record<number, string> = {
[-2]: 'bi-exclamation-octagon-fill',
[-1]: 'bi-exclamation-diamond-fill',
0: 'database-fill-gear',
1: 'database-fill-up',
2: 'database-fill-lock',
3: 'database-fill-gear',
4: 'database-fill-check'
};
export const TaskTypeColors: Record<number, string> = {
[-2]: 'red',
[-1]: 'red',
0: 'blue',
1: 'blue',
2: 'orange',
3: 'orange',
4: 'green'
};
</script>
<script lang="ts">
import { post, showMessage } from 'src/lib/requests.svelte';
import type { Model } from './+page.svelte';
import type { Model } from '../+page.svelte';
import MessageSimple from 'src/lib/MessageSimple.svelte';
import Tooltip from 'src/lib/Tooltip.svelte';
let { model }: { model: Model } = $props();
@@ -45,7 +64,7 @@
export async function getList() {
try {
const res = await post('tasks/list', {
id: model.id,
model_id: model.id,
page: page
});
showNext = res.show_next;
@@ -134,16 +153,22 @@
{:else}
TODO {task.user_confirmed}
{/if}
{#if task.user_confirmed == -1}
<button type="button" on:click={userPreception(task.id, 1)}>
<span class="bi bi-check"></span>
</button>
{/if}
{#if task.user_confirmed == 1}
<button class="danger" type="button" on:click={userPreception(task.id, -1)}>
<span class="bi bi-x-lg"></span>
</button>
{/if}
<div>
{#if task.user_confirmed != 1}
<Tooltip title="Agree with the result of the task">
<button type="button" on:click={userPreception(task.id, 1)}>
<span class="bi bi-check"></span>
</button>
</Tooltip>
{/if}
{#if task.user_confirmed != -1}
<Tooltip title="Disagree with the result">
<button class="danger" type="button" on:click={userPreception(task.id, -1)}>
<span class="bi bi-x-lg"></span>
</button>
</Tooltip>
{/if}
</div>
{:else}
-
{/if}
@@ -155,14 +180,20 @@
{#if task.status == 4}
{#if task.type == 1}
{@const temp = JSON.parse(task.result)}
{temp.class}({temp.confidence * 100}%)
{temp.class}({Math.round(temp.confidence * 100000) / 1000}%)
{:else}
{task.result}
{/if}
{/if}
</td>
<td>
{TaskTypeStrings[task.status]}
<td style="text-align: center;">
<Tooltip title={TaskTypeStrings[task.status]}>
<span
class="bi bi-{TaskTypeIcons[task.status]}"
style="color: {TaskTypeColors[task.status]}; font-size: 1.5em;"
>
</span>
</Tooltip>
</td>
<td>
{task.status_message}

View File

@@ -0,0 +1,15 @@
export type DataPoint = {
nc_success: number;
nc_error: number;
c_success: number;
c_failure: number;
c_unknown: number;
c_error: number;
c_pre_running: number;
c_running: number;
};
export type TasksStatsDay = {
total: DataPoint;
hours: DataPoint[];
};