add button to add new image to class closes #15
This commit is contained in:
parent
7d742e7970
commit
3ad07e6ce5
@ -315,6 +315,31 @@ func InsertReturnId(c QueryInterface, store interface{}, tablename string, retur
|
|||||||
return
|
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 {
|
func GetDBOnce(db QueryInterface, store interface{}, tablename string, args ...any) error {
|
||||||
t := reflect.TypeOf(store).Elem()
|
t := reflect.TypeOf(store).Elem()
|
||||||
|
|
||||||
|
@ -498,6 +498,88 @@ func handleDataUpload(handle *Handle) {
|
|||||||
return c.SendJSON(modelClass)
|
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
|
// ------ CLASS DATA UPLOAD
|
||||||
// ------
|
// ------
|
||||||
|
@ -1,9 +1,11 @@
|
|||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"database/sql"
|
"database/sql"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"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})
|
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 {
|
inner_fn := func(c *Context) *Error {
|
||||||
if !c.CheckAuthLevel(authLevel) {
|
if !c.CheckAuthLevel(authLevel) {
|
||||||
return nil
|
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})
|
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) {
|
func (x *Handle) Delete(path string, fn func(c *Context) *Error) {
|
||||||
x.deletes = append(x.deletes, HandleFunc{path, fn})
|
x.deletes = append(x.deletes, HandleFunc{path, fn})
|
||||||
}
|
}
|
||||||
|
@ -35,6 +35,7 @@
|
|||||||
<button
|
<button
|
||||||
class="icon"
|
class="icon"
|
||||||
class:adapt={replace_slot && file && !notExpand}
|
class:adapt={replace_slot && file && !notExpand}
|
||||||
|
type="button"
|
||||||
on:click={() => fileInput.click()}
|
on:click={() => fileInput.click()}
|
||||||
>
|
>
|
||||||
{#if replace_slot && file}
|
{#if replace_slot && file}
|
||||||
|
@ -111,6 +111,7 @@ export async function showMessage(
|
|||||||
messages.display(await e.json());
|
messages.display(await e.json());
|
||||||
return true;
|
return true;
|
||||||
} else {
|
} else {
|
||||||
|
console.error(e);
|
||||||
messages.display(message);
|
messages.display(message);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -112,6 +112,33 @@
|
|||||||
console.error('TODO notify user', e);
|
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>
|
</script>
|
||||||
|
|
||||||
{#if classes.length == 0}
|
{#if classes.length == 0}
|
||||||
@ -219,15 +246,18 @@
|
|||||||
{/if}
|
{/if}
|
||||||
{#if selected_class}
|
{#if selected_class}
|
||||||
<div class="content selected">
|
<div class="content selected">
|
||||||
|
<h2>
|
||||||
{#if model.model_type == 2}
|
{#if model.model_type == 2}
|
||||||
{#if selected_class?.status == 1}
|
{#if selected_class?.status == 1}
|
||||||
<h2>Class to train</h2>
|
Class to train
|
||||||
{:else if selected_class?.status == 2}
|
{:else if selected_class?.status == 2}
|
||||||
<h2>Class training</h2>
|
Class training
|
||||||
{:else if selected_class?.status == 3}
|
{:else if selected_class?.status == 3}
|
||||||
<h2>Class trained</h2>
|
Class trained
|
||||||
{/if}
|
{/if}
|
||||||
{/if}
|
{/if}
|
||||||
|
<button on:click={() => uploadImageDialog.showModal()}> Upload Image </button>
|
||||||
|
</h2>
|
||||||
<table>
|
<table>
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
@ -312,7 +342,45 @@
|
|||||||
</Tabs>
|
</Tabs>
|
||||||
{/if}
|
{/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">
|
<style lang="scss">
|
||||||
|
.newImageDialog {
|
||||||
|
border-radius: 20px;
|
||||||
|
min-width: 300px;
|
||||||
|
min-height: 200px;
|
||||||
|
}
|
||||||
|
|
||||||
.buttons {
|
.buttons {
|
||||||
width: 100%;
|
width: 100%;
|
||||||
display: flex;
|
display: flex;
|
||||||
|
Loading…
Reference in New Issue
Block a user