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

View File

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