add button to create new class without images closes #14

This commit is contained in:
Andre Henriques 2024-04-16 10:37:18 +01:00
parent f4e70d7a73
commit 7d742e7970
6 changed files with 180 additions and 67 deletions

View File

@ -459,6 +459,45 @@ func handleDataUpload(handle *Handle) {
return c.SendJSON(model.Id)
})
// Create New class
type CreateNewEmptyClass struct {
Id string `json:"id" validate:"required"`
Name string `json:"name" validate:"required"`
}
PostAuthJson(handle, "/models/data/class/new", User_Normal, func(c *Context, obj *CreateNewEmptyClass) *Error {
model, err := GetBaseModel(c.Db, obj.Id)
if err == ModelNotFoundError {
return c.JsonBadRequest("Model not found")
}
if model.ModelType != 2 && model.Status != CONFIRM_PRE_TRAINING || model.ModelType == 2 && model.Status != CONFIRM_PRE_TRAINING && model.Status != READY {
return c.JsonBadRequest("Model not in the correct status for adding new class")
}
var baseOrder struct {
Order int `db:"class_order"`
}
err = GetDBOnce(c, &baseOrder, "model_classes where model_id=$1 order by class_order desc;", model.Id)
if err != nil {
return c.E500M("Could not create class", err)
}
id, err := model_classes.CreateClass(c.Db, model.Id, baseOrder.Order+1, obj.Name)
if err == model_classes.ClassAlreadyExists {
return c.JsonBadRequest("Class Already exists")
} else if err != nil {
return c.E500M("Could not create class", err)
}
var modelClass model_classes.ModelClass
err = GetDBOnce(c, &modelClass, "model_classes where id=$1;", id)
if err != nil {
return c.E500M("Failed to get class information but class was creted", err)
}
return c.SendJSON(modelClass)
})
// ------
// ------ CLASS DATA UPLOAD
// ------

View File

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

View File

@ -99,3 +99,19 @@ export async function postFormData(url: string, body: FormData) {
return r.json();
}
export async function showMessage(
e: any,
messages: any,
message = 'Could not complete request'
): Promise<boolean> {
if (e == null) {
return false;
} else if (e instanceof Response) {
messages.display(await e.json());
return true;
} else {
messages.display(message);
return true;
}
}

View File

@ -42,6 +42,7 @@
import ModelDataPage from './ModelDataPage.svelte';
import 'src/styles/forms.css';
import KeepPageSize from 'src/lib/KeepPageSize.svelte';
let model: Promise<Model> = $state(new Promise(() => {}));
let _model: Model | undefined = $state(undefined);
@ -87,7 +88,6 @@
});
let resetMessages: MessageSimple;
async function resetModel() {
resetMessages.display('');
@ -125,11 +125,7 @@
{/await}
</svelte:head>
<!--
{{ define "run-model-card" }}
{{ end }}
-->
<KeepPageSize />
<main>
<Tabs active="model" let:isActive nobox>

View File

@ -10,17 +10,22 @@
<script lang="ts">
import Tabs from 'src/lib/Tabs.svelte';
import type { Class } from './ModelData.svelte';
import { post, postFormData, rdelete } from 'src/lib/requests.svelte';
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 }>();
let selected_class: Class | undefined = $state();
let { classes, model } = $props<{ classes: Class[]; model: Model }>();
let { classes, model }: { classes: Class[]; model: Model } = $props();
let createClass: { className: string } = $state({
className: ''
});
let page = $state(0);
let showNext = $state(false);
@ -51,11 +56,6 @@
$effect(() => {
if (selected_class) {
page = 0;
}
});
$effect(() => {
if (selected_class) {
getList();
}
});
@ -88,6 +88,22 @@
uploading = Promise.resolve();
}
let createNewClassMessages: MessageSimple;
async function createNewClass() {
try {
const r = await post('models/data/class/new', {
id: model.id,
name: createClass.className
});
selected_class = r;
classes.push(r);
classes = classes;
getList();
} catch (e) {
showMessage(e, createNewClassMessages);
}
}
function deleteDataPoint(id: string) {
try {
rdelete('models/data/point', { id });
@ -104,9 +120,10 @@
<Tabs active={classes[0]?.name} let:isActive>
<div class="buttons" slot="buttons" let:setActive let:isActive>
<!-- TODO Auto Load 1st -->
<div>
<div style="display: flex; overflow-x: scroll;">
{#each classes as item}
<button
style="width: auto; white-space: nowrap;"
on:click={() => setActiveClass(item, setActive)}
class="tab"
class:selected={isActive(item.name)}
@ -136,60 +153,68 @@
{#if selected_class == undefined && isActive('-----New Class-----')}
<div class="content selected">
<h2>Add New Class</h2>
<form on:submit|preventDefault={uploadZip}>
<fieldset class="file-upload">
<label for="file">Data file</label>
<div class="form-msg">
Please provide a file that has the training and testing data<br />
The file must have 2 folders one with testing images and one with training images.
<br />
Each of the folders will contain the classes of the model. The folders must be the same
in testing and training. The class folders must have the images for the classes.
<pre>
training\
class1\
img1.png
img2.png
img2.png
...
class2\
img1.png
img2.png
img2.png
...
...
testing\
class1\
img1.png
img2.png
img2.png
...
class2\
img1.png
img2.png
img2.png
...
...
</pre>
</div>
<FileUpload replace_slot bind:file accept="application/zip" notExpand>
<img src="/imgs/upload-icon.png" alt="" />
<span> Upload Zip File </span>
<div slot="replaced" style="display: inline;">
<img src="/imgs/upload-icon.png" alt="" />
<span> File selected </span>
<Tabs active="zip" let:isActive nobox>
<div slot="buttons" let:setActive let:isActive>
<button
class="tab"
on:click|preventDefault={setActive('zip')}
class:selected={isActive('zip')}
>
Zip
</button>
<button
class="tab"
on:click|preventDefault={setActive('empty')}
class:selected={isActive('empty')}
>
Empty Class
</button>
</div>
<div class="content" class:selected={isActive('zip')}>
<form on:submit|preventDefault={uploadZip}>
<fieldset class="file-upload">
<label for="file">Data file</label>
<div class="form-msg">
Please provide a file that has the training and testing data<br />
The file must have 2 folders one with testing images and one with training images.
<br />
Each of the folders will contain the classes of the model. The folders must be the
same in testing and training. The class folders must have the images for the classes.
<ZipStructure />
</div>
<FileUpload replace_slot bind:file accept="application/zip" notExpand>
<img src="/imgs/upload-icon.png" alt="" />
<span> Upload Zip File </span>
<div slot="replaced" style="display: inline;">
<img src="/imgs/upload-icon.png" alt="" />
<span> File selected </span>
</div>
</FileUpload>
</fieldset>
<MessageSimple bind:this={uploadImage} />
{#if file}
{#await uploading}
<button disabled> Uploading </button>
{:then}
<button> Add </button>
{/await}
{/if}
</form>
</div>
<div class="content" class:selected={isActive('empty')}>
<form on:submit|preventDefault={createNewClass}>
<div class="form-msg">
This Creates an empty class that allows images to be added after
</div>
</FileUpload>
</fieldset>
<MessageSimple bind:this={uploadImage} />
{#if file}
{#await uploading}
<button disabled> Uploading </button>
{:then}
<button> Add </button>
{/await}
{/if}
</form>
<fieldset>
<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>
</Tabs>
</div>
{/if}
{#if selected_class}

View File

@ -0,0 +1,26 @@
<pre>
training\
class1\
img1.png
img2.png
img2.png
...
class2\
img1.png
img2.png
img2.png
...
...
testing\
class1\
img1.png
img2.png
img2.png
...
class2\
img1.png
img2.png
img2.png
...
...
</pre>