multiple fixes
This commit is contained in:
parent
0ac6ac8dce
commit
0c0d16c846
@ -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")
|
||||
|
@ -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
|
||||
|
@ -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))))
|
||||
|
||||
|
@ -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'
|
||||
}
|
||||
};
|
||||
|
@ -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>
|
||||
|
@ -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}
|
@ -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
6
webpage/src/lib/utils.ts
Normal file
@ -0,0 +1,6 @@
|
||||
export function preventDefault(fn: any) {
|
||||
return function (event: Event) {
|
||||
event.preventDefault();
|
||||
fn.call(this, event);
|
||||
};
|
||||
}
|
@ -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;
|
||||
|
@ -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} />
|
||||
|
@ -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>
|
||||
|
@ -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} />
|
||||
|
@ -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>
|
||||
|
@ -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">
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -97,7 +97,7 @@
|
||||
});
|
||||
</script>
|
||||
|
||||
<div><canvas bind:this={ctx} /></div>
|
||||
<div><canvas bind:this={ctx}></canvas></div>
|
||||
|
||||
<style lang="scss">
|
||||
canvas {
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
27
webpage/src/routes/models/edit/api/CreateNewClass.svelte
Normal file
27
webpage/src/routes/models/edit/api/CreateNewClass.svelte
Normal 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>
|
@ -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',
|
||||
|
@ -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 {
|
||||
|
@ -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;
|
||||
};
|
||||
|
@ -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} />
|
||||
|
@ -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>
|
||||
|
@ -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}
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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'
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user