add button to add new image to class closes #15

This commit is contained in:
Andre Henriques 2024-04-16 14:09:03 +01:00
parent 7d742e7970
commit 3ad07e6ce5
6 changed files with 240 additions and 11 deletions

View File

@ -29,7 +29,7 @@ type BasePackStruct struct {
}
func (b BasePackStruct) GetHost() string {
return b.Host
return b.Host
}
func (b BasePackStruct) GetDb() *sql.DB {
@ -315,6 +315,31 @@ func InsertReturnId(c QueryInterface, store interface{}, tablename string, retur
return
}
func GetDbVar[T interface{}](c QueryInterface, var_to_extract string, tablename string, args ...any) (*T, error) {
db_query, err := c.Prepare(fmt.Sprintf("select %s from %s", var_to_extract, tablename))
if err != nil {
return nil, err
}
defer db_query.Close()
rows, err := db_query.Query(args...)
if err != nil {
return nil, err
}
defer rows.Close()
if !rows.Next() {
return nil, NotFoundError
}
dat := new(T)
if err = rows.Scan(dat); err != nil {
return nil, err
}
return dat, nil
}
func GetDBOnce(db QueryInterface, store interface{}, tablename string, args ...any) error {
t := reflect.TypeOf(store).Elem()

View File

@ -498,6 +498,88 @@ func handleDataUpload(handle *Handle) {
return c.SendJSON(modelClass)
})
type AddNewImage struct {
ClassId string `json:"id" validate:"required"`
}
PostAuthFormJson(handle, "/models/data/class/image", User_Normal, func(c *Context, dat *AddNewImage, file []byte) *Error {
model_id, err := GetDbVar[string](c, "m.id", "model_classes as mc inner join models as m on m.id = mc.model_id where mc.id=$1;", dat.ClassId)
if err == NotFoundError {
return c.JsonBadRequest("Could not find the class")
} else if err != nil {
return c.E500M("Error getting class information", err)
}
c.Logger.Info("model", "model", *model_id)
model, err := GetBaseModel(c.Db, *model_id)
if err == ModelNotFoundError {
return c.JsonBadRequest("Could not find the model")
} else if err != nil {
return c.E500M("Error getting model information", err)
}
// TODO make this work for zip files as well
/*c.Logger.Debug("Processing File", "file", file.Name)
data, err := reader.Open(file.Name)
if err != nil {
c.Logger.Error("Could not open file in zip %s\n", "file name", file.Name, "err", err)
back_channel <- index
continue
}
defer data.Close()
file_data, err := io.ReadAll(data)
if err != nil {
c.Logger.Error("Could not open file in zip %s\n", "file name", file.Name, "err", err)
back_channel <- index
continue
}*/
// TODO check if the file is a valid photo that matched the defined photo on the database
//parts := strings.Split(file.Name, "/")
mode := DATA_POINT_MODE_TRAINING
// TODO add the mode
/*mode := DATA_POINT_MODE_TRAINING
if parts[0] == "testing" {
mode = DATA_POINT_MODE_TESTING
}*/
data_point_id, err := model_classes.AddDataPoint(c.Db, dat.ClassId, "id://", mode)
if err != nil {
//c.Logger.Error("Failed to add datapoint", "model", model.Id, "file name", file.Name, "err", err)
c.Logger.Error("Failed to add datapoint", "data_point_id", data_point_id)
return c.E500M("Could not add image to model", err)
}
file_path := path.Join("savedData", model.Id, "data", data_point_id+"."+model.Format)
f, err := os.Create(file_path)
if err != nil {
//c.Logger.Error("Failed to save datapoint to disk", "model", model.Id, "file name", file.Name, "err", err)
c.Logger.Error("Failed to save datapoint to disk", "model", model.Id, "err", err)
return c.E500M("Could not add image to model", err)
}
defer f.Close()
f.Write(file)
if !TestImgForModel(c, model, file_path) {
//c.Logger.Errorf("Image did not have valid format for model %s (in zip: %s)!", file_path, file.Name)
c.Logger.Errorf("Image did not have valid format for model %s!", file_path)
c.Logger.Warn("Not failling updating data point to status -1")
message := "Image did not have valid format for the model"
if err = model_classes.UpdateDataPointStatus(c.Db, data_point_id, -1, &message); err != nil {
//c.Logger.Error("Failed to update data point", "model", model.Id, "file name", file.Name, "err", err)
c.Logger.Error("Failed to update data point", "model", model.Id, "err", err)
return c.E500M("Could not update information about the data point", err)
}
return c.JsonBadRequest("Provided file is not a valid image for this model")
}
return c.SendJSON(struct {
Id string `json:"id"`
}{data_point_id})
})
// ------
// ------ CLASS DATA UPLOAD
// ------

View File

@ -1,9 +1,11 @@
package utils
import (
"bytes"
"database/sql"
"errors"
"fmt"
"io"
"net/http"
"os"
"path"
@ -89,7 +91,7 @@ func (x *Handle) PostAuth(path string, authLevel dbtypes.UserType, fn func(c *Co
x.posts = append(x.posts, HandleFunc{path, inner_fn})
}
func PostAuthJson[T interface{}](x *Handle, path string, authLevel dbtypes.UserType, fn func(c *Context, obj *T) *Error) {
func PostAuthJson[T interface{}](x *Handle, path string, authLevel dbtypes.UserType, fn func(c *Context, dat *T) *Error) {
inner_fn := func(c *Context) *Error {
if !c.CheckAuthLevel(authLevel) {
return nil
@ -107,6 +109,56 @@ func PostAuthJson[T interface{}](x *Handle, path string, authLevel dbtypes.UserT
x.posts = append(x.posts, HandleFunc{path, inner_fn})
}
func PostAuthFormJson[T interface{}](x *Handle, path string, authLevel dbtypes.UserType, fn func(c *Context, dat *T, file []byte) *Error) {
inner_fn := func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
read_form, err := c.R.MultipartReader()
if err != nil {
return c.JsonBadRequest("Please provide a valid form data request!")
}
var json_data string
var file []byte
for {
part, err_part := read_form.NextPart()
if err_part == io.EOF {
break
} else if err_part != nil {
return c.JsonBadRequest("Please provide a valid form data request!")
}
if part.FormName() == "json_data" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
json_data = buf.String()
}
if part.FormName() == "file" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
file = buf.Bytes()
}
}
if !c.CheckAuthLevel(authLevel) {
return nil
}
dat := new(T)
decoder := json.NewDecoder(strings.NewReader(json_data))
if err := c.decodeAndValidade(decoder, dat); err != nil {
return err
}
return fn(c, dat, file)
}
x.posts = append(x.posts, HandleFunc{path, inner_fn})
}
func (x *Handle) Delete(path string, fn func(c *Context) *Error) {
x.deletes = append(x.deletes, HandleFunc{path, fn})
}
@ -200,7 +252,7 @@ func (c Context) GetLogger() *log.Logger {
}
func (c Context) GetHost() string {
return c.Handle.Config.Hostname
return c.Handle.Config.Hostname
}
func (c Context) Query(query string, args ...any) (*sql.Rows, error) {

View File

@ -35,6 +35,7 @@
<button
class="icon"
class:adapt={replace_slot && file && !notExpand}
type="button"
on:click={() => fileInput.click()}
>
{#if replace_slot && file}

View File

@ -111,6 +111,7 @@ export async function showMessage(
messages.display(await e.json());
return true;
} else {
console.error(e);
messages.display(message);
return true;
}

View File

@ -112,6 +112,33 @@
console.error('TODO notify user', e);
}
}
let addFile: File | undefined = $state();
let addImageMessages: MessageSimple;
let adding = $state(Promise.resolve());
let uploadImageDialog: HTMLDialogElement;
async function addImage() {
if (!selected_class?.id || !addFile) {
return;
}
try {
let form = new FormData();
form.append(
'json_data',
JSON.stringify({
id: selected_class?.id
})
);
form.append('file', addFile, 'upload.zip');
const r = await postFormData('models/data/class/image', form);
console.log(r);
uploadImageDialog.close();
addFile = undefined;
getList();
} catch (e) {
showMessage(e, addImageMessages);
}
}
</script>
{#if classes.length == 0}
@ -219,15 +246,18 @@
{/if}
{#if selected_class}
<div class="content selected">
{#if model.model_type == 2}
{#if selected_class?.status == 1}
<h2>Class to train</h2>
{:else if selected_class?.status == 2}
<h2>Class training</h2>
{:else if selected_class?.status == 3}
<h2>Class trained</h2>
<h2>
{#if model.model_type == 2}
{#if selected_class?.status == 1}
Class to train
{:else if selected_class?.status == 2}
Class training
{:else if selected_class?.status == 3}
Class trained
{/if}
{/if}
{/if}
<button on:click={() => uploadImageDialog.showModal()}> Upload Image </button>
</h2>
<table>
<thead>
<tr>
@ -312,7 +342,45 @@
</Tabs>
{/if}
<dialog class="newImageDialog" bind:this={uploadImageDialog}>
<form on:submit|preventDefault={addImage}>
<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={addFile} accept="application/zip,image/*" notExpand>
<img src="/imgs/upload-icon.png" alt="" />
<span> Upload Zip File or Image </span>
<div slot="replaced" style="display: inline;">
<img src="/imgs/upload-icon.png" alt="" />
<span> File selected </span>
</div>
</FileUpload>
</fieldset>
<MessageSimple bind:this={addImageMessages} />
{#if addFile}
{#await adding}
<button disabled> Uploading </button>
{:then}
<button> Add </button>
{/await}
{/if}
</form>
</dialog>
<style lang="scss">
.newImageDialog {
border-radius: 20px;
min-width: 300px;
min-height: 200px;
}
.buttons {
width: 100%;
display: flex;