multiple fixes

This commit is contained in:
Andre Henriques 2024-05-11 01:11:07 +01:00
parent 0ac6ac8dce
commit 0c0d16c846
34 changed files with 340 additions and 454 deletions

View File

@ -136,17 +136,16 @@ func processZipFile(c *Context, model *BaseModel) {
return
}
if paths[0] != "training" {
if paths[0] == "training" {
training = InsertIfNotPresent(training, paths[1])
} else if paths[0] != "testing" {
} else if paths[0] == "testing" {
testing = InsertIfNotPresent(testing, paths[1])
}
}
if !reflect.DeepEqual(testing, training) {
c.Logger.Info("Diff", "testing", testing, "training", training)
failed("Testing and Training datesets are diferent")
return
c.Logger.Warn("Diff", "testing", testing, "training", training)
c.Logger.Warn("Testing and traing datasets differ")
}
base_path := path.Join("savedData", model.Id, "data")

View File

@ -37,7 +37,7 @@ func ReadJPG(scope *op.Scope, imagePath string, channels int64) *image.Image {
return image.Scale(0, 255)
}
func runModelNormal(base BasePack, model *BaseModel, def_id string, inputImage *tf.Tensor) (order int, confidence float32, err error) {
func runModelNormal(model *BaseModel, def_id string, inputImage *tf.Tensor) (order int, confidence float32, err error) {
order = 0
err = nil
@ -193,7 +193,7 @@ func ClassifyTask(base BasePack, task Task) (err error) {
}
} else {
base.GetLogger().Info("Running model normal", "model", model.Id, "def", def_id)
vi, confidence, err = runModelNormal(base, model, def_id, inputImage)
vi, confidence, err = runModelNormal(model, def_id, inputImage)
if err != nil {
task.UpdateStatusLog(base, TASK_FAILED_RUNNING, "Failed to run model")
return

View File

@ -1298,12 +1298,6 @@ func generateExpandableDefinition(c BasePack, model *BaseModel, target_accuracy
order++
// handle the errors inside the pervious if block
if err != nil {
failed()
return
}
// Create the blocks
loop := int((math.Log(float64(model.Width)) / math.Log(float64(10))))

View File

@ -27,5 +27,11 @@ module.exports = {
parser: '@typescript-eslint/parser'
}
}
]
],
rules: {
'svelte/no-at-html-tags': 'off',
// TODO remove this
'@typescript-eslint/no-explicit-any': 'off'
}
};

View File

@ -36,7 +36,7 @@
class="icon"
class:adapt={replace_slot && file && !notExpand}
type="button"
on:click={() => fileInput.click()}
onclick={() => fileInput.click()}
>
{#if replace_slot && file}
<slot name="replaced" {file}>
@ -54,6 +54,6 @@
required
{accept}
bind:this={fileInput}
on:change={onChange}
onchange={onChange}
/>
</div>

View File

@ -1,57 +0,0 @@
<script context="module" lang="ts">
export type DisplayFn = (
msg: string,
options?: {
type?: 'error' | 'success';
timeToShow?: number;
}
) => void;
</script>
<script lang="ts">
let message = $state<string | undefined>(undefined);
let type = $state<'error' | 'success'>('error');
let timeout: number | undefined = undefined;
export function clear() {
if (timeout) clearTimeout(timeout);
message = undefined;
}
export function display(
msg: string,
options?: {
type?: 'error' | 'success';
timeToShow?: number;
}
) {
if (timeout) clearTimeout(timeout);
if (!msg) {
message = undefined;
return;
}
let { type: l_type, timeToShow } = options ?? { type: 'error', timeToShow: undefined };
if (l_type) {
type = l_type;
}
message = msg;
if (timeToShow) {
timeout = setTimeout(() => {
message = undefined;
timeout = undefined;
}, timeToShow);
}
}
</script>
{#if message}
<div class="form-msg {type}">
{message}
</div>
{/if}

View File

@ -1,5 +1,5 @@
<script lang="ts">
let { title } = $props<{ title: string }>();
let { title }: { title: string } = $props();
let isHovered = $state(false);
let x = $state(0);
@ -30,10 +30,10 @@
<div
bind:this={div}
on:mouseover={mouseOver}
on:mouseleave={mouseLeave}
on:mousemove={mouseMove}
on:focus={focus}
onmouseover={mouseOver}
onmouseleave={mouseLeave}
onmousemove={mouseMove}
onfocus={focus}
role="tooltip"
class="tooltipContainer"
>

6
webpage/src/lib/utils.ts Normal file
View File

@ -0,0 +1,6 @@
export function preventDefault(fn: any) {
return function (event: Event) {
event.preventDefault();
fn.call(this, event);
};
}

View File

@ -12,7 +12,7 @@
let width = $state(0);
let height = $state(0);
function drag(simulation: any) {
function drag(simulation: d3.Simulation<d3.HierarchyNode<Base>, undefined>) {
function dragstarted(event: any, d: any) {
if (!event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;

View File

@ -3,6 +3,7 @@
import 'src/styles/forms.css';
import { userStore } from '../UserStore.svelte';
import { goto } from '$app/navigation';
import { preventDefault } from 'src/lib/utils';
let submitted = $state(false);
@ -39,7 +40,7 @@
<div class="login-page">
<div>
<h1>Login</h1>
<form on:submit|preventDefault={onSubmit} class:submitted>
<form onsubmit={preventDefault(onSubmit)} class:submitted>
<fieldset>
<label for="email">Email</label>
<input type="email" required name="email" bind:value={loginData.email} />

View File

@ -85,11 +85,4 @@
.list-header .expand {
flex-grow: 1;
}
.list-header .button,
.list-header button {
padding: 10px 10px;
height: calc(100% - 20px);
margin-top: 5px;
}
</style>

View File

@ -5,6 +5,7 @@
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
import 'src/styles/forms.css';
import { preventDefault } from 'src/lib/utils';
let submitted = $state(false);
@ -44,7 +45,7 @@
<main>
<h1>Create new Model</h1>
<form class:submitted on:submit|preventDefault={onSubmit}>
<form class:submitted onsubmit={preventDefault(onSubmit)}>
<fieldset>
<label for="name">Name</label>
<input id="name" name="name" required bind:value={data.name} />

View File

@ -30,18 +30,18 @@
import BaseModelInfo from './BaseModelInfo.svelte';
import DeleteModel from './DeleteModel.svelte';
import { goto } from '$app/navigation';
import { get, rdelete } from 'src/lib/requests.svelte';
import MessageSimple from '$lib/MessageSimple.svelte';
import { get, rdelete, showMessage } from 'src/lib/requests.svelte';
import { preventDefault } from 'src/lib/utils';
import ModelData from './ModelData.svelte';
import DeleteZip from './DeleteZip.svelte';
import RunModel from './RunModel.svelte';
import Tabs from 'src/lib/Tabs.svelte';
import TasksDataPage from './TasksDataPage.svelte';
import ModelDataPage from './ModelDataPage.svelte';
import 'src/styles/forms.css';
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
let model: Promise<Model> = $state(new Promise(() => {}));
let _model: Model | undefined = $state(undefined);
@ -92,10 +92,7 @@
getModel();
});
let resetMessages: MessageSimple;
async function resetModel() {
resetMessages.display('');
let _model = await model;
try {
@ -105,11 +102,7 @@
getModel();
} catch (e) {
if (e instanceof Response) {
resetMessages.display(await e.json());
} else {
resetMessages.display('Could not reset model!');
}
showMessage(e, notificationStore, 'Could not reset model!');
}
}
@ -147,7 +140,8 @@
<div slot="buttons" let:setActive let:isActive>
<button
class="tab"
on:click|preventDefault={setActive('model')}
type="button"
onclick={setActive('model')}
class:selected={isActive('model')}
>
Model
@ -155,7 +149,8 @@
{#if _model && [2, 3, 4, 5, 6, 7, -6, -7].includes(_model.status)}
<button
class="tab"
on:click|preventDefault={setActive('model-data')}
type="button"
onclick={setActive('model-data')}
class:selected={isActive('model-data')}
>
Model Data
@ -164,7 +159,8 @@
{#if _model && [5, 6, 7, -6, -7].includes(_model.status)}
<button
class="tab"
on:click|preventDefault={setActive('tasks')}
type="button"
onclick={setActive('tasks')}
class:selected={isActive('tasks')}
>
Tasks
@ -172,7 +168,7 @@
{/if}
</div>
{#if _model}
<ModelDataPage model={_model} on:reload={getModel} active={isActive('model-data')} />
<ModelDataPage model={_model} onreload={getModel} active={isActive('model-data')} />
<TasksDataPage model={_model} active={isActive('tasks')} />
{/if}
<div class="content" class:selected={isActive('model')}>
@ -200,12 +196,12 @@
<!-- PRE TRAINING STATUS -->
{:else if m.status == 2}
<BaseModelInfo model={m} />
<ModelData model={m} on:reload={getModel} />
<ModelData model={m} onreload={getModel} />
<!-- {{ template "train-model-card" . }} -->
<DeleteModel model={m} />
{:else if m.status == -2}
<BaseModelInfo model={m} />
<DeleteZip model={m} on:reload={getModel} />
<DeleteZip model={m} onreload={getModel} />
<DeleteModel model={m} />
{:else if m.status == 3}
<BaseModelInfo model={m} />
@ -215,10 +211,9 @@
</div>
{:else if m.status == -3 || m.status == -4}
<BaseModelInfo model={m} />
<form on:submit|preventDefault={resetModel}>
<form onsubmit={preventDefault(resetModel)}>
Failed Prepare for training.<br />
<div class="spacer"></div>
<MessageSimple bind:this={resetMessages} />
<button class="danger"> Try Again </button>
</form>
<DeleteModel model={m} />
@ -337,7 +332,7 @@
<div class="card">Model expading... Processing ZIP file</div>
{/if}
{#if m.status == -6}
<DeleteZip model={m} on:reload={getModel} expand />
<DeleteZip model={m} onreload={getModel} expand />
{/if}
{#if m.status == -7}
<form>
@ -346,7 +341,7 @@
</form>
{/if}
{#if m.model_type == 2}
<ModelData simple model={m} on:reload={getModel} />
<ModelData simple model={m} onreload={getModel} />
{/if}
<DeleteModel model={m} />
{:else}
@ -383,10 +378,4 @@
table tr th:first-child {
border-left: none;
}
table tr td button,
table tr td .button {
padding: 5px 10px;
box-shadow: 0 2px 5px 1px #66666655;
}
</style>

View File

@ -1,6 +1,6 @@
<script lang="ts">
import type { Model } from './+page.svelte';
let { model } = $props<{ model: Model }>();
let { model }: { model: Model } = $props();
</script>
<div class="card model-card">

View File

@ -1,39 +1,31 @@
<script lang="ts">
import MessageSimple from 'src/lib/MessageSimple.svelte';
import type { Model } from './+page.svelte';
import { rdelete } from '$lib/requests.svelte';
import { rdelete, showMessage } from '$lib/requests.svelte';
import { goto } from '$app/navigation';
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
let { model } = $props<{ model: Model }>();
let { model }: { model: Model } = $props();
let name: string = $state('');
let submmited: boolean = $state(false);
let messageSimple: MessageSimple;
async function deleteModel() {
submmited = true;
messageSimple.display('');
try {
await rdelete('models/delete', { id: model.id, name });
goto('/models');
} catch (e) {
if (e instanceof Response) {
messageSimple.display(await e.json());
} else {
messageSimple.display('Could not delete the model');
}
showMessage(e, notificationStore, 'Could not delete the model');
}
}
</script>
<form on:submit|preventDefault={deleteModel} class:submmited class="danger-bg">
<form onsubmit={deleteModel} class:submmited class="danger-bg">
<fieldset>
<label for="name">
To delete this model please type "{model.name}":
</label>
<input name="name" id="name" required bind:value={name} />
</fieldset>
<MessageSimple bind:this={messageSimple} />
<button class="danger"> Delete </button>
</form>

View File

@ -1,32 +1,30 @@
<script lang="ts">
import { rdelete } from 'src/lib/requests.svelte';
import { rdelete, showMessage } from 'src/lib/requests.svelte';
import type { Model } from './+page.svelte';
import MessageSimple from 'src/lib/MessageSimple.svelte';
import { createEventDispatcher } from 'svelte';
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
import { preventDefault } from 'src/lib/utils';
let message: MessageSimple;
let { model, expand } = $props<{ model: Model; expand?: boolean }>();
const dispatch = createEventDispatcher<{ reload: void }>();
let {
model,
expand,
onreload = () => {}
}: {
model: Model;
expand?: boolean;
onreload?: () => void;
} = $props();
async function deleteZip() {
message.clear();
try {
await rdelete('models/data/delete-zip-file', { id: model.id });
dispatch('reload');
onreload();
} catch (e) {
if (e instanceof Response) {
message.display(await e.json());
} else {
message.display('Could not delete the zip file');
}
showMessage(e, notificationStore, 'Could not delete the zip file');
}
}
</script>
<form on:submit|preventDefault={deleteZip}>
<form onsubmit={preventDefault(deleteZip)}>
{#if expand}
Failed to proccess the zip file.<br />
Delete file and upload a correct version do add more classes.<br />
@ -37,6 +35,5 @@
<br />
{/if}
<div class="spacer"></div>
<MessageSimple bind:this={message} />
<button class="danger"> Delete Zip File </button>
</form>

View File

@ -1,38 +1,29 @@
<script lang="ts" context="module">
export type Class = {
name: string;
id: string;
status: number;
};
</script>
<script lang="ts">
import FileUpload from 'src/lib/FileUpload.svelte';
import Tabs from 'src/lib/Tabs.svelte';
import type { Model } from './+page.svelte';
import { postFormData, get } from 'src/lib/requests.svelte';
import MessageSimple from 'src/lib/MessageSimple.svelte';
import { createEventDispatcher } from 'svelte';
import type { Class } from './types';
import { postFormData, get, showMessage } from 'src/lib/requests.svelte';
import ModelTable from './ModelTable.svelte';
import TrainModel from './TrainModel.svelte';
import ZipStructure from './ZipStructure.svelte';
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
import { preventDefault } from 'src/lib/utils';
let { model, simple } = $props<{ model: Model; simple?: boolean }>();
let {
model,
simple,
onreload = () => {}
}: { model: Model; simple?: boolean; onreload?: () => void } = $props();
let classes: Class[] = $state([]);
let has_data: boolean = $state(false);
let file: File | undefined = $state();
const dispatch = createEventDispatcher<{
reload: void;
}>();
let uploading: Promise<void> = $state(Promise.resolve());
let numberOfInvalidImages = $state(0);
let uploadImage: MessageSimple;
async function uploadZip() {
if (!file) return;
@ -44,13 +35,9 @@
try {
await postFormData('models/data/upload', form);
dispatch('reload');
onreload();
} catch (e) {
if (e instanceof Response) {
uploadImage.display(await e.json());
} else {
uploadImage.display('');
}
showMessage(e, notificationStore, 'Could not upload data');
}
uploading = Promise.resolve();
@ -67,8 +54,8 @@
classes = data.classes;
numberOfInvalidImages = data.number_of_invalid_images;
has_data = data.has_data;
} catch {
return;
} catch (e) {
showMessage(e, notificationStore, 'Could not get information on classes');
}
}
</script>
@ -80,22 +67,22 @@
<p>You need to upload data so the model can train.</p>
<Tabs active="upload" let:isActive>
<div slot="buttons" let:setActive let:isActive>
<button class="tab" class:selected={isActive('upload')} on:click={setActive('upload')}>
<button class="tab" class:selected={isActive('upload')} onclick={setActive('upload')}>
Upload
</button>
<button
class="tab"
class:selected={isActive('create-class')}
on:click={setActive('create-class')}
onclick={setActive('create-class')}
>
Create Class
</button>
<button class="tab" class:selected={isActive('api')} on:click={setActive('api')}>
<button class="tab" class:selected={isActive('api')} onclick={setActive('api')}>
Api
</button>
</div>
<div class="content" class:selected={isActive('upload')}>
<form on:submit|preventDefault={uploadZip}>
<form onsubmit={preventDefault(uploadZip)}>
<fieldset class="file-upload">
<label for="file">Data file</label>
<div class="form-msg">
@ -115,7 +102,6 @@
</div>
</FileUpload>
</fieldset>
<MessageSimple bind:this={uploadImage} />
{#if file}
{#await uploading}
<button disabled> Uploading </button>
@ -126,7 +112,7 @@
</form>
</div>
<div class="content" class:selected={isActive('create-class')}>
<ModelTable {classes} {model} on:reload={() => dispatch('reload')} />
<ModelTable {classes} {model} {onreload} />
</div>
<div class="content" class:selected={isActive('api')}>TODO</div>
</Tabs>
@ -139,33 +125,11 @@
These images will be delete when the model trains.
</p>
{/if}
<Tabs active="create-class" let:isActive>
<div slot="buttons" let:setActive let:isActive>
<button
class="tab"
class:selected={isActive('create-class')}
on:click={setActive('create-class')}
>
Create Class
</button>
<button class="tab" class:selected={isActive('api')} on:click={setActive('api')}>
Api
</button>
</div>
<div class="content" class:selected={isActive('create-class')}>
<ModelTable {classes} {model} on:reload={() => dispatch('reload')} />
</div>
<div class="content" class:selected={isActive('api')}>TODO</div>
</Tabs>
<ModelTable {classes} {model} {onreload} />
{/if}
</div>
{/if}
{#if classes.some((item) => item.status == 1) && ![-6, 6].includes(model.status)}
<TrainModel
number_of_invalid_images={numberOfInvalidImages}
{model}
{has_data}
on:reload={() => dispatch('reload')}
/>
<TrainModel number_of_invalid_images={numberOfInvalidImages} {model} {has_data} {onreload} />
{/if}

View File

@ -1,5 +1,4 @@
<script lang="ts">
import { createEventDispatcher } from 'svelte';
import type { Model } from './+page.svelte';
import ModelData from './ModelData.svelte';
import { post, showMessage } from 'src/lib/requests.svelte';
@ -7,9 +6,11 @@
import type { ModelStats } from './types';
import DeleteZip from './DeleteZip.svelte';
let { model, active }: { model: Model; active?: boolean } = $props();
const dispatch = createEventDispatcher<{ reload: void }>();
let {
model,
active,
onreload = () => {}
}: { model: Model; active?: boolean; onreload?: () => void } = $props();
$effect(() => {
if (active) getData();
@ -34,14 +35,14 @@
{/if}
{#if [-6, -2].includes(model.status)}
<DeleteZip {model} on:reload={() => dispatch('reload')} expand />
<DeleteZip {model} {onreload} expand />
{/if}
<ModelData
{model}
on:reload={() => {
onreload={() => {
getData();
dispatch('reload');
onreload();
}}
/>
</div>

View File

@ -97,7 +97,7 @@
});
</script>
<div><canvas bind:this={ctx} /></div>
<div><canvas bind:this={ctx}></canvas></div>
<style lang="scss">
canvas {

View File

@ -1,33 +1,24 @@
<script lang="ts" context="module">
export type Image = {
file_path: string;
mode: number;
status: number;
id: string;
};
</script>
<script lang="ts">
import Tabs from 'src/lib/Tabs.svelte';
import type { Class } from './ModelData.svelte';
import type { Class, Image } from './types';
import { post, postFormData, rdelete, showMessage } from 'src/lib/requests.svelte';
import type { Model } from './+page.svelte';
import FileUpload from 'src/lib/FileUpload.svelte';
import MessageSimple from 'src/lib/MessageSimple.svelte';
import { createEventDispatcher } from 'svelte';
import ZipStructure from './ZipStructure.svelte';
const dispatch = createEventDispatcher<{ reload: void }>();
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
import { preventDefault } from 'src/lib/utils.js';
import CreateNewClass from './api/CreateNewClass.svelte';
let selected_class: Class | undefined = $state();
let { classes, model }: { classes: Class[]; model: Model } = $props();
let { classes, model, onreload }: { classes: Class[]; model: Model; onreload?: () => void } =
$props();
let createClass: { className: string } = $state({
className: ''
});
let page = $state(0);
let page = $state(-1);
let showNext = $state(false);
let image_list = $state<Image[]>([]);
@ -41,6 +32,7 @@
});
async function getList() {
console.log(page);
try {
let res = await post('models/data/list', {
id: selected_class?.id ?? '',
@ -53,19 +45,20 @@
}
}
$effect(() => {
getList();
});
$effect(() => {
if (selected_class) {
page = 0;
getList();
}
});
let file: File | undefined = $state();
let uploadImage: MessageSimple;
let uploading = $state(Promise.resolve());
async function uploadZip() {
uploadImage.clear();
if (!file) return;
uploading = new Promise(() => {});
@ -76,19 +69,14 @@
try {
await postFormData('models/data/class/upload', form);
dispatch('reload');
if (onreload) onreload();
} catch (e) {
if (e instanceof Response) {
uploadImage.display(await e.json());
} else {
uploadImage.display('');
}
showMessage(e, notificationStore, 'Failed to upload');
}
uploading = Promise.resolve();
}
let createNewClassMessages: MessageSimple;
async function createNewClass() {
try {
const r = await post('models/data/class/new', {
@ -100,7 +88,7 @@
classes = classes;
getList();
} catch (e) {
showMessage(e, createNewClassMessages);
showMessage(e, notificationStore);
}
}
@ -109,12 +97,11 @@
rdelete('models/data/point', { id });
getList();
} catch (e) {
console.error('TODO notify user', e);
showMessage(e, notificationStore);
}
}
let addFile: File | undefined = $state();
let addImageMessages: MessageSimple;
let adding = $state(Promise.resolve());
let uploadImageDialog: HTMLDialogElement;
async function addImage() {
@ -136,7 +123,7 @@
addFile = undefined;
getList();
} catch (e) {
showMessage(e, addImageMessages);
showMessage(e, notificationStore);
}
}
</script>
@ -151,30 +138,30 @@
{#each classes as item}
<button
style="width: auto; white-space: nowrap;"
on:click={() => setActiveClass(item, setActive)}
onclick={() => setActiveClass(item, setActive)}
class="tab"
class:selected={isActive(item.name)}
>
{item.name}
{#if model.model_type == 2}
{#if item.status == 1}
<span class="bi bi-book" style="color: orange;" />
<span class="bi bi-book" style="color: orange;"></span>
{:else if item.status == 2}
<span class="bi bi-book" style="color: green;" />
<span class="bi bi-book" style="color: green;"></span>
{:else if item.status == 3}
<span class="bi bi-check" style="color: green;" />
<span class="bi bi-check" style="color: green;"></span>
{/if}
{/if}
</button>
{/each}
</div>
<button
on:click={() => {
onclick={() => {
setActive('-----New Class-----')();
selected_class = undefined;
}}
>
<span class="bi bi-plus" />
<span class="bi bi-plus"></span>
</button>
</div>
{#if selected_class == undefined && isActive('-----New Class-----')}
@ -184,21 +171,31 @@
<div slot="buttons" let:setActive let:isActive>
<button
class="tab"
on:click|preventDefault={setActive('zip')}
type="button"
onclick={setActive('zip')}
class:selected={isActive('zip')}
>
Zip
</button>
<button
class="tab"
on:click|preventDefault={setActive('empty')}
type="button"
onclick={setActive('empty')}
class:selected={isActive('empty')}
>
Empty Class
</button>
<button
class="tab"
type="button"
onclick={setActive('api')}
class:selected={isActive('api')}
>
API
</button>
</div>
<div class="content" class:selected={isActive('zip')}>
<form on:submit|preventDefault={uploadZip}>
<form onsubmit={preventDefault(uploadZip)}>
<fieldset class="file-upload">
<label for="file">Data file</label>
<div class="form-msg">
@ -218,7 +215,6 @@
</div>
</FileUpload>
</fieldset>
<MessageSimple bind:this={uploadImage} />
{#if file}
{#await uploading}
<button disabled> Uploading </button>
@ -229,7 +225,7 @@
</form>
</div>
<div class="content" class:selected={isActive('empty')}>
<form on:submit|preventDefault={createNewClass}>
<form onsubmit={preventDefault(createNewClass)}>
<div class="form-msg">
This Creates an empty class that allows images to be added after
</div>
@ -237,10 +233,12 @@
<label for="className">Class Name</label>
<input required name="className" bind:value={createClass.className} />
</fieldset>
<MessageSimple bind:this={createNewClassMessages} />
<button> Create New Class </button>
</form>
</div>
<div class="content" class:selected={isActive('api')}>
<CreateNewClass {model} />
</div>
</Tabs>
</div>
{/if}
@ -258,7 +256,7 @@
{:else}
Class to train
{/if}
<button on:click={() => uploadImageDialog.showModal()}> Upload Image </button>
<button onclick={() => uploadImageDialog.showModal()}> Upload Image </button>
</h2>
<table>
<thead>
@ -314,7 +312,7 @@
{/if}
</td>
<td style="width: 3ch">
<button class="danger" on:click={() => deleteDataPoint(image.id)}>
<button class="danger" onclick={() => deleteDataPoint(image.id)}>
<span class="bi bi-trash"></span>
</button>
</td>
@ -325,7 +323,7 @@
<div class="flex justify-center align-center">
<div class="grow-1 flex justify-end align-center">
{#if page > 0}
<button on:click={() => (page -= 1)}> Prev </button>
<button onclick={() => (page -= 1)}> Prev </button>
{/if}
</div>
@ -335,7 +333,7 @@
<div class="grow-1 flex justify-start align-center">
{#if showNext}
<button on:click={() => (page += 1)}> Next </button>
<button onclick={() => (page += 1)}> Next </button>
{/if}
</div>
</div>
@ -345,7 +343,7 @@
{/if}
<dialog class="newImageDialog" bind:this={uploadImageDialog}>
<form on:submit|preventDefault={addImage}>
<form onsubmit={preventDefault(addImage)}>
<fieldset class="file-upload">
<label for="file">Data file</label>
<div class="form-msg">
@ -365,7 +363,6 @@
</div>
</FileUpload>
</fieldset>
<MessageSimple bind:this={addImageMessages} />
{#if addFile}
{#await adding}
<button disabled> Uploading </button>
@ -415,10 +412,4 @@
table tr th:first-child {
border-left: none;
}
table tr td button,
table tr td .button {
padding: 5px 10px;
box-shadow: 0 2px 5px 1px #66666655;
}
</style>

View File

@ -2,20 +2,22 @@
import { post, postFormData, showMessage } from 'src/lib/requests.svelte';
import type { Model } from './+page.svelte';
import FileUpload from 'src/lib/FileUpload.svelte';
import MessageSimple from 'src/lib/MessageSimple.svelte';
import { createEventDispatcher, onDestroy } from 'svelte';
import { onDestroy } from 'svelte';
import Spinner from 'src/lib/Spinner.svelte';
import type { Task } from './tasks/TasksTable.svelte';
import type { Task } from './tasks/types';
import Tabs from 'src/lib/Tabs.svelte';
import hljs from 'highlight.js';
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
import { preventDefault } from 'src/lib/utils';
let { model } = $props<{ model: Model }>();
let {
model,
onupload = () => {},
ontaskReload = () => {}
}: { model: Model; onupload?: () => void; ontaskReload?: () => void } = $props();
let file: File | undefined = $state();
const dispatch = createEventDispatcher<{ upload: void; taskReload: void }>();
let _result: Promise<Task> = $state(new Promise(() => {}));
let run = $state(false);
@ -32,7 +34,7 @@
const r = await post('tasks/task', { id: last_task });
if ([0, 1, 2, 3].includes(r.status)) {
setTimeout(reloadLastTimeout, 500);
setTimeout(() => dispatch('taskReload'), 500);
setTimeout(ontaskReload, 500);
} else {
_result = Promise.resolve(r);
}
@ -59,7 +61,7 @@
showMessage(e, notificationStore, 'Could not run the model');
}
dispatch('upload');
onupload();
}
onDestroy(() => {
@ -71,10 +73,10 @@
<Tabs active="upload" let:isActive>
<div class="buttons" slot="buttons" let:setActive let:isActive>
<button class="tab" class:selected={isActive('upload')} on:click={setActive('upload')}>
<button class="tab" class:selected={isActive('upload')} onclick={setActive('upload')}>
Upload
</button>
<button class="tab" class:selected={isActive('api')} on:click={setActive('api')}> Api </button>
<button class="tab" class:selected={isActive('api')} onclick={setActive('api')}> Api </button>
</div>
<div class="content" class:selected={isActive('api')}>
<div class="codeinfo">
@ -134,7 +136,7 @@ const r = await fetch('${window.location.protocol}//${window.location.hostname}/
</div>
</div>
<div class="content" class:selected={isActive('upload')}>
<form on:submit|preventDefault={submit} style="box-shadow: none;">
<form onsubmit={preventDefault(submit)} style="box-shadow: none;">
<fieldset class="file-upload">
<label for="file">Image</label>
<div class="form-msg">Run image through them model and get the result</div>

View File

@ -1,5 +1,4 @@
<script lang="ts">
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 './tasks/TasksTable.svelte';
@ -12,7 +11,7 @@
{#if active}
<div class="content selected">
<RunModel {model} on:upload={() => table.getList()} on:taskReload={() => table.getList()} />
<RunModel {model} onupload={() => table.getList()} ontaskReload={() => table.getList()} />
<TasksTable {model} bind:this={table} />
<Stats {model} />
</div>

View File

@ -1,14 +1,20 @@
<script lang="ts">
import MessageSimple from 'src/lib/MessageSimple.svelte';
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
import type { Model } from './+page.svelte';
import { post } from 'src/lib/requests.svelte';
import { createEventDispatcher } from 'svelte';
import { post, showMessage } from 'src/lib/requests.svelte';
import { preventDefault } from 'src/lib/utils';
let { number_of_invalid_images, has_data, model } = $props<{
let {
number_of_invalid_images,
has_data,
model,
onreload = () => {}
}: {
number_of_invalid_images: number;
has_data: boolean;
model: Model;
}>();
onreload?: () => void;
} = $props();
let data = $state({
model_type: 'simple',
@ -18,46 +24,32 @@
let submitted = $state(false);
let dispatch = createEventDispatcher<{ reload: void }>();
let messages: MessageSimple;
async function submit() {
messages.clear();
submitted = true;
try {
await post('models/train', {
id: model.id,
...data
});
dispatch('reload');
onreload();
} catch (e) {
if (e instanceof Response) {
messages.display(await e.json());
} else {
messages.display('Could not start the training of the model');
}
showMessage(e, notificationStore, 'Could not start the training of the model');
}
}
async function submitRetrain() {
messages.clear();
submitted = true;
try {
await post('model/train/retrain', { id: model.id });
dispatch('reload');
onreload();
} catch (e) {
if (e instanceof Response) {
messages.display(await e.json());
} else {
messages.display('Could not start the training of the model');
}
showMessage(e, notificationStore, 'Could not start the training of the model');
}
}
</script>
{#if model.status == 2}
<form class:submitted on:submit|preventDefault={submit}>
<form class:submitted onsubmit={preventDefault(submit)}>
{#if has_data}
{#if number_of_invalid_images > 0}
<p class="danger">
@ -65,7 +57,6 @@
These images will be delete when the model trains.
</p>
{/if}
<MessageSimple bind:this={messages} />
<!-- TODO expading mode -->
<fieldset>
<legend> Model Type </legend>
@ -111,7 +102,7 @@
{/if}
</form>
{:else}
<form class:submitted on:submit|preventDefault={submitRetrain}>
<form class:submitted onsubmit={submitRetrain}>
{#if has_data}
<h2>This model has new classes and can be expanded</h2>
{#if number_of_invalid_images > 0}
@ -120,7 +111,6 @@
These images will be delete when the model trains.
</p>
{/if}
<MessageSimple bind:this={messages} />
<button> Retrain </button>
{:else}
<h2>To train the model please provide data to the model first</h2>

View File

@ -0,0 +1,27 @@
<script lang="ts">
import hljs from 'highlight.js';
import type { Model } from '../+page.svelte';
let { model }: { model: Model } = $props();
</script>
To create a new class via the API you can:
<pre style="font-family: Fira Code;">{@html hljs.highlight(
`let form = new FormData();
form.append('json_data', JSON.stringify({
id: '${model.id}',
name: 'New class name'
}));
form.append('file', file, 'file');
const headers = new Headers();
headers.append('response-type', 'application/json');
headers.append('token', token);
const r = await fetch('${window.location.protocol}//${window.location.hostname}/models/data/class/new', {
method: 'POST',
headers: headers,
body: form
});`,
{ language: 'javascript' }
).value}</pre>

View File

@ -1,5 +1,5 @@
<script lang="ts">
import { onDestroy, onMount } from 'svelte';
import { onDestroy } 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';
@ -124,7 +124,6 @@
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',

View File

@ -40,7 +40,6 @@
<script lang="ts">
import { post, showMessage } from 'src/lib/requests.svelte';
import type { Model } from '../+page.svelte';
import MessageSimple from 'src/lib/MessageSimple.svelte';
import Tooltip from 'src/lib/Tooltip.svelte';
import type { Task } from './types';
@ -70,7 +69,6 @@
}
});
let userPreceptionMessages: MessageSimple;
// This returns a function that performs the call and does not do the call it self
function userPreception(task: string, agree: number) {
return async function () {
@ -89,7 +87,6 @@
<div>
<h2>Tasks</h2>
<MessageSimple bind:this={userPreceptionMessages} />
<table>
<thead>
<tr>
@ -146,14 +143,14 @@
<div>
{#if task.user_confirmed != 1}
<Tooltip title="Agree with the result of the task">
<button type="button" on:click={userPreception(task.id, 1)}>
<button type="button" onclick={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)}>
<button class="danger" type="button" onclick={userPreception(task.id, -1)}>
<span class="bi bi-x-lg"></span>
</button>
</Tooltip>
@ -198,7 +195,7 @@
<div class="flex justify-center align-center">
<div class="grow-1 flex justify-end align-center">
{#if page > 0}
<button on:click={() => (page -= 1)}> Prev </button>
<button onclick={() => (page -= 1)}> Prev </button>
{/if}
</div>
@ -208,7 +205,7 @@
<div class="grow-1 flex justify-start align-center">
{#if showNext}
<button on:click={() => (page += 1)}> Next </button>
<button onclick={() => (page += 1)}> Next </button>
{/if}
</div>
</div>
@ -219,10 +216,6 @@
width: 100%;
display: flex;
justify-content: space-between;
& > button {
margin: 3px 5px;
}
}
table {

View File

@ -3,3 +3,16 @@ export type ModelStats = Array<{
training: number;
testing: number;
}>;
export type Class = {
name: string;
id: string;
status: number;
};
export type Image = {
file_path: string;
mode: number;
status: number;
id: string;
};

View File

@ -3,6 +3,7 @@
import 'src/styles/forms.css';
import { userStore } from '../UserStore.svelte';
import { goto } from '$app/navigation';
import { preventDefault } from 'src/lib/utils';
let submitted = $state(false);
@ -39,7 +40,7 @@
<div class="login-page">
<div>
<h1>Register</h1>
<form on:submit|preventDefault={onSubmit} class:submitted>
<form onsubmit={preventDefault(onSubmit)} class:submitted>
<fieldset>
<label for="username">Username</label>
<input required name="username" bind:value={loginData.username} />

View File

@ -4,10 +4,11 @@
import { onMount } from 'svelte';
import 'src/styles/forms.css';
import { post } from 'src/lib/requests.svelte';
import MessageSimple, { type DisplayFn } from 'src/lib/MessageSimple.svelte';
import { post, showMessage } from 'src/lib/requests.svelte';
import TokenTable from './TokenTable.svelte';
import DeleteUser from './DeleteUser.svelte';
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
import { preventDefault } from 'src/lib/utils';
onMount(() => {
if (!userStore.isLogin()) {
@ -26,12 +27,8 @@
let submiitedEmail = $state(false);
let submiitedPassword = $state(false);
let msgEmail: MessageSimple;
let msgPassword: MessageSimple;
async function onSubmitEmail() {
submiitedEmail = true;
msgEmail.display('');
if (!userStore.user) return;
@ -44,31 +41,31 @@
...userStore.user,
...req
};
msgEmail.display('User updated successufly!', { type: 'success', timeToShow: 10000 });
notificationStore.add({
message: 'User updated successufly!',
type: 'success',
timeToLive: 10000
});
} catch (e) {
if (e instanceof Response) {
msgEmail.display(await e.json());
} else {
msgEmail.display('Could not update email');
}
showMessage(e, notificationStore, 'Could not update email');
}
}
async function onSubmitPassword() {
submiitedPassword = true;
msgPassword.display('');
if (!userStore.user) return;
try {
await post('user/info/password', passwordData);
passwordData = { old_password: '', password: '', password2: '' };
msgPassword.display('Password updated successufly!', { type: 'success', timeToShow: 10000 });
notificationStore.add({
message: 'Password updated successufly!',
type: 'success',
timeToLive: 10000
});
} catch (e) {
if (e instanceof Response) {
msgPassword.display(await e.json());
} else {
msgPassword.display('Could not update password');
}
showMessage(e, notificationStore, 'Could not update password');
}
}
</script>
@ -80,15 +77,14 @@
<div class="login-page">
<div>
<h1>User Infomation</h1>
<form on:submit|preventDefault={onSubmitEmail} class:submiitedEmail>
<form onsubmit={onSubmitEmail} class:submiitedEmail>
<fieldset>
<label for="email">Email</label>
<input type="email" required name="email" bind:value={email} />
</fieldset>
<MessageSimple bind:this={msgEmail} />
<button> Update </button>
</form>
<form on:submit|preventDefault={onSubmitPassword} class:submiitedPassword>
<form onsubmit={preventDefault(onSubmitPassword)} class:submiitedPassword>
<fieldset>
<label for="old_password">Old Password</label>
<input
@ -106,7 +102,6 @@
<label for="password2">Repeat New Password</label>
<input required bind:value={passwordData.password2} name="password2" type="password" />
</fieldset>
<MessageSimple bind:this={msgPassword} />
<div>
<button> Update </button>
</div>

View File

@ -1,36 +1,32 @@
<script lang="ts">
import {createEventDispatcher} from 'svelte';
import MessageSimple from 'src/lib/MessageSimple.svelte';
import Tooltip from 'src/lib/Tooltip.svelte';
import 'src/styles/forms.css';
import {post} from 'src/lib/requests.svelte';
import Spinner from 'src/lib/Spinner.svelte';
import { post } from 'src/lib/requests.svelte';
import Spinner from 'src/lib/Spinner.svelte';
import { preventDefault } from 'src/lib/utils';
const dispatch = createEventDispatcher<{reload: void}>();
let { onreload = () => {} }: { onreload?: () => void } = $props();
let addNewToken = $state(false);
let messages: MessageSimple;
let expiry_date: HTMLInputElement = $state(undefined as any);
type NewToken = {
name: string,
expiry: number,
token: string,
}
let token: Promise<NewToken> | undefined = $state();
type NewToken = {
name: string;
expiry: number;
token: string;
};
let token: Promise<NewToken> | undefined = $state();
let newToken: {
name: string;
expiry: string;
password: string;
password: string;
} = $state({
name: '',
expiry: '',
password: '',
password: ''
});
async function createToken(e: SubmitEvent & { currentTarget: HTMLFormElement }) {
@ -47,86 +43,85 @@
}
}
try {
const r = await post("user/token/add", {
name: newToken.name,
expiry: expiry,
password: newToken.password,
});
token = Promise.resolve(r)
setTimeout(() => dispatch('reload'), 500)
} catch (e) {
token = undefined;
console.error("Notify user", e)
}
try {
const r = await post('user/token/add', {
name: newToken.name,
expiry: expiry,
password: newToken.password
});
token = Promise.resolve(r);
setTimeout(onreload, 500);
} catch (e) {
token = undefined;
console.error('Notify user', e);
}
}
$effect(() => {
if (expiry_date) {
if (isNaN(Number(newToken.expiry))) {
expiry_date.setCustomValidity('Invalid Number');
} else {
expiry_date.setCustomValidity('');
}
}
if (expiry_date) {
if (isNaN(Number(newToken.expiry))) {
expiry_date.setCustomValidity('Invalid Number');
} else {
expiry_date.setCustomValidity('');
}
}
});
</script>
{#if addNewToken}
<div>
<h2>Add New Token</h2>
{#if !token}
<form on:submit|preventDefault={createToken}>
<fieldset>
<label for="name">Name</label>
<input required bind:value={newToken.name} name="name" />
</fieldset>
<fieldset>
<label for="expiry_date">Expiry Date</label>
<div class="flex">
<input bind:this={expiry_date} bind:value={newToken.expiry} name="expiry_date" />
<Tooltip title="Time in seconds. Leave empty to last forever">
<span class="center-question bi bi-question-circle-fill" />
</Tooltip>
</div>
</fieldset>
<fieldset>
<label for="password">Password</label>
<input required bind:value={newToken.password} name="password" />
</fieldset>
<MessageSimple bind:this={messages} />
<div>
<button> Update </button>
</div>
</form>
{:else}
{#await token}
<Spinner /> Generating
{:then t}
<h3> Token generated </h3>
<form on:submit|preventDefault={() => {}}>
<fieldset>
<label for="token">Token</label>
<div class="flex">
<input value={t.token} on:input={(e) => e.preventDefault() } name="token" />
<div style="width: 5em;">
<button on:click={() => navigator.clipboard.writeText(t.token)} >
<span class="bi bi-clipboard" />
</button>
</div>
</div>
</fieldset>
<div>
<button on:click={() => token = undefined}> Generate new token </button>
</div>
</form>
{:catch e}
{e}
{/await}
{/if}
{#if !token}
<form onsubmit={preventDefault(createToken)}>
<fieldset>
<label for="name">Name</label>
<input required bind:value={newToken.name} name="name" />
</fieldset>
<fieldset>
<label for="expiry_date">Expiry Date</label>
<div class="flex">
<input bind:this={expiry_date} bind:value={newToken.expiry} name="expiry_date" />
<Tooltip title="Time in seconds. Leave empty to last forever">
<span class="center-question bi bi-question-circle-fill"></span>
</Tooltip>
</div>
</fieldset>
<fieldset>
<label for="password">Password</label>
<input required bind:value={newToken.password} name="password" />
</fieldset>
<div>
<button> Update </button>
</div>
</form>
{:else}
{#await token}
<Spinner /> Generating
{:then t}
<h3>Token generated</h3>
<form onsubmit={preventDefault(() => {})}>
<fieldset>
<label for="token">Token</label>
<div class="flex">
<input value={t.token} oninput={(e) => e.preventDefault()} name="token" />
<div style="width: 5em;">
<button onclick={() => navigator.clipboard.writeText(t.token)}>
<span class="bi bi-clipboard"></span>
</button>
</div>
</div>
</fieldset>
<div>
<button onclick={() => (token = undefined)}> Generate new token </button>
</div>
</form>
{:catch e}
{e}
{/await}
{/if}
</div>
{:else}
<div>
<button class="expander" on:click={() => (addNewToken = true)}> Add New Token </button>
<button class="expander" onclick={() => (addNewToken = true)}> Add New Token </button>
</div>
{/if}

View File

@ -2,6 +2,7 @@
import { goto } from '$app/navigation';
import { notificationStore } from 'src/lib/NotificationsStore.svelte';
import { rdelete, showMessage } from 'src/lib/requests.svelte';
import { preventDefault } from 'src/lib/utils';
import { userStore } from 'src/routes/UserStore.svelte';
let data = $state({ password: '' });
@ -23,7 +24,7 @@
}
</script>
<form class="danger-bg" on:submit|preventDefault={deleteUser}>
<form class="danger-bg" onsubmit={preventDefault(deleteUser)}>
<h2 class="no-top-margin">Delete user</h2>
Deleting the user will delete all your data stored in the service including the images.
<fieldset>

View File

@ -10,7 +10,7 @@
import { post, rdelete } from 'src/lib/requests.svelte';
import { userStore } from 'src/routes/UserStore.svelte';
import AddToken from './AddToken.svelte';
import AddToken from './AddToken.svelte';
let page = $state(0);
let showNext = $state(false);
@ -70,7 +70,7 @@
{new Date(token.create_date).toLocaleString()}
</td>
<td>
<button class="danger" on:click={() => removeToken(token)}>
<button class="danger" onclick={() => removeToken(token)}>
<span class="bi bi-trash"></span>
</button>
</td>
@ -81,7 +81,7 @@
<div class="flex justify-center align-center">
<div class="grow-1 flex justify-end align-center">
{#if page > 0}
<button on:click={() => (page -= 1)}> Prev </button>
<button onclick={() => (page -= 1)}> Prev </button>
{/if}
</div>
@ -91,25 +91,15 @@
<div class="grow-1 flex justify-start align-center">
{#if showNext}
<button on:click={() => (page += 1)}> Next </button>
<button onclick={() => (page += 1)}> Next </button>
{/if}
</div>
</div>
</div>
<AddToken on:reload={getList} />
<AddToken onreload={getList} />
<style lang="scss">
.buttons {
width: 100%;
display: flex;
justify-content: space-between;
& > button {
margin: 3px 5px;
}
}
table {
width: 100%;
box-shadow: 0 2px 8px 1px #66666622;
@ -132,10 +122,4 @@
table tr th:first-child {
border-left: none;
}
table tr td button,
table tr td .button {
padding: 5px 10px;
box-shadow: 0 2px 5px 1px #66666655;
}
</style>

View File

@ -65,6 +65,7 @@ button.expander::after {
a.button {
text-decoration: none;
height: 1.6em;
}
.flex {
@ -190,3 +191,12 @@ form.danger-bg {
background-color: var(--danger-transparent);
border: 1px solid var(--danger);
}
pre {
font-family: 'Fira Code';
background-color: #f6f8fa;
word-break: break-word;
white-space: pre-wrap;
border-radius: 10px;
padding: 10px;
}

View File

@ -12,10 +12,10 @@ const config = {
// If your environment is not supported or you settled on a specific environment, switch out the adapter.
// See https://kit.svelte.dev/docs/adapters for more information about adapters.
adapter: adapter(),
alias: {
src: "src",
routes: "src/routes",
}
alias: {
src: 'src',
routes: 'src/routes'
}
}
};