feat: removed non svelte front page

This commit is contained in:
Andre Henriques 2024-03-09 10:52:08 +00:00
parent 0d37ba8d59
commit 274d7d22aa
32 changed files with 614 additions and 3695 deletions

View File

@ -86,90 +86,14 @@ func loadBaseImage(c *Context, id string) {
}
func handleAdd(handle *Handle) {
handle.GetHTML("/models/add", AnswerTemplate("models/add.html", nil, 1))
handle.Post("/models/add", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Post("/models/add", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
read_form, err := r.MultipartReader()
if err != nil {
return c.JsonErrorBadRequest(err, "Please provide a valid multipart Reader request")
}
var name string
var file []byte
for {
part, err_part := read_form.NextPart()
if err_part == io.EOF {
break
} else if err_part != nil {
return &Error{Code: http.StatusBadRequest}
}
if part.FormName() == "name" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
name = buf.String()
}
if part.FormName() == "file" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
file = buf.Bytes()
}
}
if name == "" || len(file) == 0 {
return c.JsonBadRequest("Name is empty or file is empty")
}
row, err := handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id)
if err != nil {
return c.Error500(err)
}
if row.Next() {
return c.JsonBadRequest("Model with that name already exists!")
}
row, err = handle.Db.Query("insert into models (user_id, name) values ($1, $2) returning id", c.User.Id, name)
if err != nil || !row.Next() {
return c.Error500(err)
}
var id string
err = row.Scan(&id)
if err != nil {
return c.Error500(err)
}
// TODO mk this path configurable
dir_path := path.Join("savedData", id)
err = os.Mkdir(dir_path, os.ModePerm)
if err != nil {
return c.Error500(err)
}
f, err := os.Create(path.Join(dir_path, "baseimage.png"))
if err != nil {
return c.Error500(err)
}
defer f.Close()
f.Write(file)
c.Logger.Warn("Created model with id %s! Started to proccess image!\n", "id", id)
go loadBaseImage(c, id)
return c.SendJSON(id)
}
read_form, err := r.MultipartReader()
read_form, err := c.R.MultipartReader()
if err != nil {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil))
return nil
return c.JsonErrorBadRequest(err, "Please provide a valid multipart Reader request")
}
var name string
@ -195,8 +119,7 @@ func handleAdd(handle *Handle) {
}
if name == "" || len(file) == 0 {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil))
return nil
return c.JsonBadRequest("Name is empty or file is empty")
}
row, err := handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id)
@ -205,27 +128,14 @@ func handleAdd(handle *Handle) {
}
if row.Next() {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(AnyMap{
"NameFoundError": true,
"Name": name,
}))
return nil
return c.JsonBadRequest("Model with that name already exists!")
}
_, err = handle.Db.Exec("insert into models (user_id, name) values ($1, $2)", c.User.Id, name)
if err != nil {
row, err = handle.Db.Query("insert into models (user_id, name) values ($1, $2) returning id", c.User.Id, name)
if err != nil || !row.Next() {
return c.Error500(err)
}
row, err = handle.Db.Query("select id from models where name=$1 and user_id=$2;", name, c.User.Id)
if err != nil {
return c.Error500(err)
}
if !row.Next() {
return &Error{Code: http.StatusInternalServerError}
}
var id string
err = row.Scan(&id)
if err != nil {
@ -239,6 +149,7 @@ func handleAdd(handle *Handle) {
if err != nil {
return c.Error500(err)
}
f, err := os.Create(path.Join(dir_path, "baseimage.png"))
if err != nil {
return c.Error500(err)
@ -250,7 +161,6 @@ func handleAdd(handle *Handle) {
c.Logger.Warn("Created model with id %s! Started to proccess image!\n", "id", id)
go loadBaseImage(c, id)
Redirect("/models/edit?id="+id, c.Mode, w, r)
return nil
return c.SendJSON(id)
})
}

View File

@ -1,148 +1,69 @@
package model_classes
import (
"net/http"
"strconv"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
"git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func models_data_list_json(w http.ResponseWriter, r *http.Request, c *Context) *Error {
id, err := GetIdFromUrl(r, "id")
if err != nil {
return c.JsonBadRequest("Model Class not found!")
}
page := 0
if r.URL.Query().Has("page") {
page_url := r.URL.Query().Get("page")
page_url_number, err := strconv.Atoi(page_url)
if err != nil {
return c.JsonBadRequest("Page is not a number")
}
page = page_url_number
}
var class_row struct {
Name string
Model_id string
}
err = utils.GetDBOnce(c, &class_row, "model_classes where id=$1", id)
if err == NotFoundError {
return c.JsonBadRequest("Model Class not found!")
} else if err != nil {
return c.Error500(err)
}
type baserow struct {
Id string `json:"id"`
File_Path string `json:"file_path"`
Model_Mode int `json:"model_mode"`
Status int `json:"status"`
}
rows, err := utils.GetDbMultitple[baserow](c, "model_data_point where class_id=$1 limit 11 offset $2", id, page*10)
if err != nil {
return c.Error500(err)
}
type ReturnType struct {
ImageList []*baserow `json:"image_list"`
Page int `json:"page"`
ShowNext bool `json:"showNext"`
}
max_len := min(11, len(rows))
return c.SendJSON(ReturnType{
ImageList: rows[0:max_len],
Page: page,
ShowNext: len(rows) == 11,
})
}
func HandleList(handle *Handle) {
handle.Get("/models/data/list", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Get("/models/data/list", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
return models_data_list_json(w, r, c)
}
id, err := GetIdFromUrl(r, "id")
id, err := GetIdFromUrl(c, "id")
if err != nil {
return ErrorCode(err, 400, c.AddMap(nil))
return c.JsonBadRequest("Model Class not found!")
}
page := 0
if r.URL.Query().Has("page") {
page_url := r.URL.Query().Get("page")
if c.R.URL.Query().Has("page") {
page_url := c.R.URL.Query().Get("page")
page_url_number, err := strconv.Atoi(page_url)
if err != nil {
return ErrorCode(err, http.StatusBadRequest, c.AddMap(nil))
return c.JsonBadRequest("Page is not a number")
}
page = page_url_number
}
class_rows, err := handle.Db.Query("select name, model_id from model_classes where id=$1;", id)
if err != nil {
return Error500(err)
}
defer class_rows.Close()
if !class_rows.Next() {
return ErrorCode(nil, 404, c.AddMap(nil))
var class_row struct {
Name string
Model_id string
}
name := ""
model_id := ""
if err = class_rows.Scan(&name, &model_id); err != nil {
return Error500(nil)
err = utils.GetDBOnce(c, &class_row, "model_classes where id=$1", id)
if err == NotFoundError {
return c.JsonBadRequest("Model Class not found!")
} else if err != nil {
return c.Error500(err)
}
model, err := GetBaseModel(c.Db, model_id)
if err != nil {
return Error500(err)
}
rows, err := handle.Db.Query("select id, file_path, model_mode, status from model_data_point where class_id=$1 limit 11 offset $2;", id, page*10)
if err != nil {
return Error500(err)
}
defer rows.Close()
type baserow struct {
Id string
FilePath string
Mode int
Status int
Id string `json:"id"`
File_Path string `json:"file_path"`
Model_Mode int `json:"model_mode"`
Status int `json:"status"`
}
got := []baserow{}
for rows.Next() {
nrow := baserow{}
err = rows.Scan(&nrow.Id, &nrow.FilePath, &nrow.Mode, &nrow.Status)
if err != nil {
return Error500(err)
}
got = append(got, nrow)
rows, err := utils.GetDbMultitple[baserow](c, "model_data_point where class_id=$1 limit 11 offset $2", id, page*10)
if err != nil {
return c.Error500(err)
}
max_len := min(11, len(got))
type ReturnType struct {
ImageList []*baserow `json:"image_list"`
Page int `json:"page"`
ShowNext bool `json:"showNext"`
}
LoadDefineTemplate(w, "/models/edit.html", "data-model-create-class-table-table", c.AddMap(AnyMap{
"List": got[0:max_len],
"Page": page,
"Id": id,
"Name": name,
"Model": model,
"ShowNext": len(got) == 11,
}))
return nil
max_len := min(11, len(rows))
return c.SendJSON(ReturnType{
ImageList: rows[0:max_len],
Page: page,
ShowNext: len(rows) == 11,
})
})
}

View File

@ -152,8 +152,8 @@ func processZipFile(c *Context, model *BaseModel) {
}
func processZipFileExpand(c *Context, model *BaseModel) {
var err error
var err error
failed := func(msg string) {
c.Logger.Error(msg, "err", err)
ModelUpdateStatus(c, model.Id, READY_FAILED)
@ -161,7 +161,7 @@ func processZipFileExpand(c *Context, model *BaseModel) {
reader, err := zip.OpenReader(path.Join("savedData", model.Id, "expand_data.zip"))
if err != nil {
failed("Faield to proccess zip file failed to open reader\n")
failed("Faield to proccess zip file failed to open reader\n")
return
}
defer reader.Close()
@ -257,8 +257,8 @@ func processZipFileExpand(c *Context, model *BaseModel) {
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 {
failed(fmt.Sprintf("Failed to update data point status"))
return
failed(fmt.Sprintf("Failed to update data point status"))
return
}
}
}
@ -268,130 +268,12 @@ func processZipFileExpand(c *Context, model *BaseModel) {
}
func handleDataUpload(handle *Handle) {
handle.Post("/models/data/upload", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Post("/models/data/upload", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
read_form, err := r.MultipartReader()
if err != nil {
return c.JsonBadRequest("Please provide a valid form data request!")
}
var id 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() == "id" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
id = buf.String()
}
if part.FormName() == "file" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
file = buf.Bytes()
}
}
model, err := GetBaseModel(handle.Db, id)
if err == ModelNotFoundError {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return Error500(err)
}
// TODO mk this path configurable
dir_path := path.Join("savedData", id)
f, err := os.Create(path.Join(dir_path, "base_data.zip"))
if err != nil {
return Error500(err)
}
defer f.Close()
f.Write(file)
ModelUpdateStatus(c, id, PREPARING_ZIP_FILE)
go processZipFile(c, model)
return c.SendJSON(model.Id)
}
read_form, err := r.MultipartReader()
if err != nil {
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil))
return nil
}
var id string
var file []byte
for {
part, err_part := read_form.NextPart()
if err_part == io.EOF {
break
} else if err_part != nil {
return &Error{Code: http.StatusBadRequest}
}
if part.FormName() == "id" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
id = buf.String()
}
if part.FormName() == "file" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
file = buf.Bytes()
}
}
model, err := GetBaseModel(handle.Db, id)
if err == ModelNotFoundError {
return c.ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
} else if err != nil {
return Error500(err)
}
// TODO mk this path configurable
dir_path := path.Join("savedData", id)
f, err := os.Create(path.Join(dir_path, "base_data.zip"))
if err != nil {
return Error500(err)
}
defer f.Close()
f.Write(file)
ModelUpdateStatus(c, id, PREPARING_ZIP_FILE)
go processZipFile(c, model)
Redirect("/models/edit?id="+id, c.Mode, w, r)
return nil
})
// ------
// ------ CLASS DATA UPLOAD
// ------
handle.PostJSON("/models/data/class/upload", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
return nil
}
read_form, err := r.MultipartReader()
read_form, err := c.R.MultipartReader()
if err != nil {
return c.JsonBadRequest("Please provide a valid form data request!")
}
@ -418,26 +300,86 @@ func handleDataUpload(handle *Handle) {
}
}
c.Logger.Info("Trying to expand model", "id", id)
model, err := GetBaseModel(handle.Db, id)
if err == ModelNotFoundError {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return c.Error500(err)
}
// TODO mk this path configurable
dir_path := path.Join("savedData", id)
f, err := os.Create(path.Join(dir_path, "base_data.zip"))
if err != nil {
return c.Error500(err)
}
defer f.Close()
f.Write(file)
ModelUpdateStatus(c, id, PREPARING_ZIP_FILE)
go processZipFile(c, model)
return c.SendJSON(model.Id)
})
// ------
// ------ CLASS DATA UPLOAD
// ------
handle.Post("/models/data/class/upload", 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 id 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() == "id" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
id = buf.String()
}
if part.FormName() == "file" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
file = buf.Bytes()
}
}
c.Logger.Info("Trying to expand model", "id", id)
model, err := GetBaseModel(handle.Db, id)
if err == ModelNotFoundError {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return Error500(err)
return c.Error500(err)
}
// TODO work in allowing the model to add new in the pre ready moment
if model.Status != READY {
return c.JsonBadRequest("Model not in the correct state to add a more classes")
}
// TODO work in allowing the model to add new in the pre ready moment
if model.Status != READY {
return c.JsonBadRequest("Model not in the correct state to add a more classes")
}
// TODO mk this path configurable
dir_path := path.Join("savedData", id)
f, err := os.Create(path.Join(dir_path, "expand_data.zip"))
if err != nil {
return Error500(err)
return c.Error500(err)
}
defer f.Close()
@ -450,115 +392,68 @@ func handleDataUpload(handle *Handle) {
return c.SendJSON(model.Id)
})
handle.Delete("/models/data/delete-zip-file", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Delete("/models/data/delete-zip-file", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
type ModelData struct {
Id string `json:"id"`
}
var dat ModelData
if err := c.ToJSON(r, &dat); err != nil {
return err
}
model, err := GetBaseModel(handle.Db, dat.Id)
if err == ModelNotFoundError {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return Error500(err)
}
delete_path := "base_data.zip"
if model.Status == READY_FAILED {
delete_path = "expand_data.zip"
} else if model.Status != FAILED_PREPARING_ZIP_FILE {
return c.JsonBadRequest("Model not in the correct status")
}
err = os.Remove(path.Join("savedData", model.Id, delete_path))
if err != nil {
return Error500(err)
}
if model.Status != READY_FAILED {
err = os.RemoveAll(path.Join("savedData", model.Id, "data"))
if err != nil {
return Error500(err)
}
} else {
c.Logger.Warn("Handle failed to remove the savedData when deleteing the zip file while expanding")
}
if model.Status != READY_FAILED {
_, err = handle.Db.Exec("delete from model_classes where model_id=$1;", model.Id)
if err != nil {
return Error500(err)
}
} else {
_, err = handle.Db.Exec("delete from model_classes where model_id=$1 and status=$2;", model.Id, MODEL_CLASS_STATUS_TO_TRAIN)
if err != nil {
return Error500(err)
}
}
if model.Status != READY_FAILED {
ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING)
} else {
ModelUpdateStatus(c, model.Id, READY)
}
return c.SendJSON(model.Id)
type ModelData struct {
Id string `json:"id"`
}
f, err := MyParseForm(r)
if err != nil {
return ErrorCode(err, 400, c.AddMap(nil))
var dat ModelData
if err := c.ToJSON(&dat); err != nil {
return err
}
if !CheckId(f, "id") {
return ErrorCode(err, 400, c.AddMap(nil))
}
id := f.Get("id")
model, err := GetBaseModel(handle.Db, id)
model, err := GetBaseModel(handle.Db, dat.Id)
if err == ModelNotFoundError {
return ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return Error500(err)
return c.Error500(err)
}
if model.Status != FAILED_PREPARING_ZIP_FILE {
// TODO add message
return ErrorCode(nil, 400, c.AddMap(nil))
delete_path := "base_data.zip"
if model.Status == READY_FAILED {
delete_path = "expand_data.zip"
} else if model.Status != FAILED_PREPARING_ZIP_FILE {
return c.JsonBadRequest("Model not in the correct status")
}
err = os.Remove(path.Join("savedData", id, "base_data.zip"))
err = os.Remove(path.Join("savedData", model.Id, delete_path))
if err != nil {
return Error500(err)
return c.Error500(err)
}
err = os.RemoveAll(path.Join("savedData", id, "data"))
if err != nil {
return Error500(err)
if model.Status != READY_FAILED {
err = os.RemoveAll(path.Join("savedData", model.Id, "data"))
if err != nil {
return c.Error500(err)
}
} else {
c.Logger.Warn("Handle failed to remove the savedData when deleteing the zip file while expanding")
}
_, err = handle.Db.Exec("delete from model_classes where model_id=$1;", id)
if err != nil {
return Error500(err)
if model.Status != READY_FAILED {
_, err = handle.Db.Exec("delete from model_classes where model_id=$1;", model.Id)
if err != nil {
return c.Error500(err)
}
} else {
_, err = handle.Db.Exec("delete from model_classes where model_id=$1 and status=$2;", model.Id, MODEL_CLASS_STATUS_TO_TRAIN)
if err != nil {
return c.Error500(err)
}
}
ModelUpdateStatus(c, id, CONFIRM_PRE_TRAINING)
Redirect("/models/edit?id="+id, c.Mode, w, r)
return nil
if model.Status != READY_FAILED {
ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING)
} else {
ModelUpdateStatus(c, model.Id, READY)
}
return c.SendJSON(model.Id)
})
}

View File

@ -4,40 +4,12 @@ import (
"net/http"
"os"
"path"
"strconv"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
utils "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func deleteModel(handle *Handle, id string, w http.ResponseWriter, c *Context, model BaseModel) {
c.Logger.Warnf("Removing model with id: %s", id)
_, err := handle.Db.Exec("delete from models where id=$1;", id)
if err != nil {
c.Logger.Error(err)
panic("TODO handle better deleteModel failed delete database query")
}
model_path := path.Join("./savedData", id)
c.Logger.Warnf("Removing folder of model with id: %s at %s", id, model_path)
err = os.RemoveAll(model_path)
if err != nil {
c.Logger.Error(err)
panic("TODO handle better deleteModel failed to delete folder")
}
if c.Mode == HTML {
// TODO move this to a constant so i don't forget
w.WriteHeader(309)
c.Mode = HTMLFULL
}
LoadBasedOnAnswer(c.Mode, w, "/models/delete.html", c.AddMap(AnyMap{
"Model": model,
}))
}
func deleteModelJSON(c *Context, id string) *Error {
c.Logger.Warnf("Removing model with id: %s", id)
_, err := c.Db.Exec("delete from models where id=$1;", id)
@ -56,94 +28,29 @@ func deleteModelJSON(c *Context, id string) *Error {
}
func handleDelete(handle *Handle) {
handle.Delete("/models/delete", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
var dat struct {
Id string `json:"id" validate:"required"`
Name *string `json:"name,omitempty"`
}
if err_ := c.ToJSON(r, &dat); err_ != nil {
return err_
}
var model struct {
Id string
Name string
Status int
}
err := utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2;", dat.Id, c.User.Id)
if err == NotFoundError {
return c.SendJSONStatus(http.StatusNotFound, "Model not found!")
} else if err != nil {
return c.Error500(err)
}
switch model.Status {
case FAILED_TRAINING:
fallthrough
case FAILED_PREPARING_ZIP_FILE:
fallthrough
case FAILED_PREPARING_TRAINING:
fallthrough
case FAILED_PREPARING:
return deleteModelJSON(c, dat.Id)
case READY:
fallthrough
case CONFIRM_PRE_TRAINING:
if dat.Name == nil {
return c.JsonBadRequest("Provided name does not match the model name")
}
if *dat.Name != model.Name {
return c.JsonBadRequest("Provided name does not match the model name")
}
return deleteModelJSON(c, dat.Id)
default:
c.Logger.Warn("Do not know how to handle model in status", "status", model.Status)
return c.JsonBadRequest("Model in invalid status")
}
handle.Delete("/models/delete", func(c *Context) *Error {
if c.CheckAuthLevel(1) {
return nil
}
var dat struct {
Id string `json:"id" validate:"required"`
Name *string `json:"name,omitempty"`
}
// This is required to parse delete forms with bodies
f, err := MyParseForm(r)
if err != nil {
return c.ErrorCode(err, 400, nil)
if err_ := c.ToJSON(&dat); err_ != nil {
return err_
}
if !CheckId(f, "id") {
return c.ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
var model struct {
Id string
Name string
Status int
}
id := f.Get("id")
// TODO handle admin users
rows, err := handle.Db.Query("select name, status from models where id=$1 and user_id=$2;", id, c.User.Id)
if err != nil {
return c.ErrorCode(err, http.StatusInternalServerError, nil)
}
defer rows.Close()
if !rows.Next() {
c.Logger.Warn("Could not find model for", id, c.User.Id)
return c.ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
model := BaseModel{}
model.Id = id
if err = rows.Scan(&model.Name, &model.Status); err != nil {
err := utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2;", dat.Id, c.User.Id)
if err == NotFoundError {
return c.SendJSONStatus(http.StatusNotFound, "Model not found!")
} else if err != nil {
return c.Error500(err)
}
@ -155,32 +62,23 @@ func handleDelete(handle *Handle) {
case FAILED_PREPARING_TRAINING:
fallthrough
case FAILED_PREPARING:
deleteModel(handle, id, w, c, model)
return nil
return deleteModelJSON(c, dat.Id)
case READY:
fallthrough
case CONFIRM_PRE_TRAINING:
if CheckEmpty(f, "name") {
return c.Error400(nil, "Name is empty", w, "/models/edit.html", "delete-model-card", AnyMap{
"NameDoesNotMatch": true,
"Model": model,
})
if dat.Name == nil {
return c.JsonBadRequest("Provided name does not match the model name")
}
name := f.Get("name")
if name != model.Name {
LoadDefineTemplate(w, "/models/edit.html", "delete-model-card", c.AddMap(AnyMap{
"NameDoesNotMatch": true,
"Model": model,
}))
return nil
if *dat.Name != model.Name {
return c.JsonBadRequest("Provided name does not match the model name")
}
deleteModel(handle, id, w, c, model)
return nil
return deleteModelJSON(c, dat.Id)
default:
panic("Do not know how to handle model in status:" + strconv.Itoa(model.Status))
c.Logger.Warn("Do not know how to handle model in status", "status", model.Status)
return c.JsonBadRequest("Model in invalid status")
}
})
}

View File

@ -2,7 +2,6 @@ package models
import (
"fmt"
"net/http"
model_classes "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/classes"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
@ -10,79 +9,30 @@ import (
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func handleJson(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
return nil
}
id, err := GetIdFromUrl(r, "id")
if err != nil {
return c.JsonBadRequest("Model not found")
}
type rowmodel struct {
Name string `json:"name"`
Status int `json:"status"`
Id string `json:"id"`
Width *int `json:"width"`
Height *int `json:"height"`
Color_mode *string `json:"color_mode"`
Format string `json:"format"`
Model_type int `json:"model_type"`
}
var model rowmodel = rowmodel{}
err = utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2", id, c.User.Id)
if err == NotFoundError {
return c.SendJSONStatus(404, "Model not found")
} else if err != nil {
return c.Error500(err)
}
return c.SendJSON(model)
/*
switch model.Status {
case TRAINING:
}
*/
}
func handleEdit(handle *Handle) {
handle.Get("/models/edit/classes", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Get("/models/edit/classes", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode != JSON {
return c.ErrorCode(nil, 400, AnyMap{})
}
id, err := GetIdFromUrl(r, "id")
if err != nil {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
}
model, err := GetBaseModel(c.Db, id)
if err == ModelNotFoundError {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return c.Error500(err)
}
model, err_ := c.GetModelFromId("id")
if err_ != nil {
return err_
}
wrong_number, err := model_classes.GetNumberOfWrongDataPoints(c.Db, model.Id)
if err != nil {
return c.Error500(err)
}
cls, err := model_classes.ListClasses(c, id)
cls, err := model_classes.ListClasses(c, model.Id)
if err != nil {
return c.Error500(err)
}
has_data, err := model_classes.ModelHasDataPoints(handle.Db, id)
has_data, err := model_classes.ModelHasDataPoints(handle.Db, model.Id)
if err != nil {
return Error500(err)
return c.Error500(err)
}
type ReturnType struct {
@ -98,25 +48,15 @@ func handleEdit(handle *Handle) {
})
})
handle.Get("/models/edit/definitions", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Get("/models/edit/definitions", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode != JSON {
return c.ErrorCode(nil, 400, AnyMap{})
}
id, err := GetIdFromUrl(r, "id")
if err != nil {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
}
model, err := GetBaseModel(c.Db, id)
if err == ModelNotFoundError {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return c.Error500(err)
}
model, err_ := c.GetModelFromId("id")
if err_ != nil {
return err_
}
type defrow struct {
Id string
@ -243,217 +183,39 @@ func handleEdit(handle *Handle) {
return c.SendJSON(defsToReturn)
})
handle.Get("/models/edit", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Get("/models/edit", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
return handleJson(w, r, c)
if !c.CheckAuthLevel(1) {
return nil
}
id, err := GetIdFromUrl(r, "id")
id, err := GetIdFromUrl(c, "id")
if err != nil {
return ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
}
// TODO handle admin users
rows, err := handle.Db.Query("select name, status, width, height, color_mode, format, model_type from models where id=$1 and user_id=$2;", id, c.User.Id)
if err != nil {
return Error500(err)
}
defer rows.Close()
if !rows.Next() {
return ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
return c.JsonBadRequest("Model not found")
}
type rowmodel struct {
Name string
Status int
Id string
Width *int
Height *int
Color_mode *string
Format string
Type int
Name string `json:"name"`
Status int `json:"status"`
Id string `json:"id"`
Width *int `json:"width"`
Height *int `json:"height"`
Color_mode *string `json:"color_mode"`
Format string `json:"format"`
Model_type int `json:"model_type"`
}
var model rowmodel = rowmodel{}
model.Id = id
err = rows.Scan(&model.Name, &model.Status, &model.Width, &model.Height, &model.Color_mode, &model.Format, &model.Type)
if err != nil {
return Error500(err)
err = utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2", id, c.User.Id)
if err == NotFoundError {
return c.SendJSONStatus(404, "Model not found")
} else if err != nil {
return c.Error500(err)
}
// Handle errors
// All errors will be negative
if model.Status < 0 {
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
}))
return nil
}
switch model.Status {
case PREPARING:
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
}))
case CONFIRM_PRE_TRAINING:
wrong_number, err := model_classes.GetNumberOfWrongDataPoints(c.Db, model.Id)
if err != nil {
return c.Error500(err)
}
cls, err := model_classes.ListClasses(c, id)
if err != nil {
return c.Error500(err)
}
has_data, err := model_classes.ModelHasDataPoints(handle.Db, id)
if err != nil {
return Error500(err)
}
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
"Classes": cls,
"HasData": has_data,
"NumberOfInvalidImages": wrong_number,
}))
case READY:
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
}))
case TRAINING:
type defrow struct {
Id string
Status int
EpochProgress int
Epoch int
Accuracy float64
}
defs := []defrow{}
if model.Type == 2 {
def_rows, err := c.Db.Query("select md.id, md.status, md.epoch, h.epoch_progress, h.accuracy from model_definition as md inner join exp_model_head as h on h.def_id = md.id where md.model_id=$1 order by md.created_on asc", model.Id)
if err != nil {
return c.Error500(err)
}
defer def_rows.Close()
for def_rows.Next() {
var def defrow
err = def_rows.Scan(&def.Id, &def.Status, &def.Epoch, &def.EpochProgress, &def.Accuracy)
if err != nil {
return c.Error500(err)
}
defs = append(defs, def)
}
} else {
def_rows, err := c.Db.Query("select id, status, epoch, epoch_progress, accuracy from model_definition where model_id=$1 order by created_on asc", model.Id)
if err != nil {
return c.Error500(err)
}
defer def_rows.Close()
for def_rows.Next() {
var def defrow
err = def_rows.Scan(&def.Id, &def.Status, &def.Epoch, &def.EpochProgress, &def.Accuracy)
if err != nil {
return c.Error500(err)
}
defs = append(defs, def)
}
}
type layerdef struct {
id string
LayerType int
Shape string
}
layers := []layerdef{}
for _, def := range defs {
if def.Status == MODEL_DEFINITION_STATUS_TRAINING {
rows, err := c.Db.Query("select id, layer_type, shape from model_definition_layer where def_id=$1 order by layer_order asc;", def.Id)
if err != nil {
return c.Error500(err)
}
defer rows.Close()
for rows.Next() {
var layerdef layerdef
err = rows.Scan(&layerdef.id, &layerdef.LayerType, &layerdef.Shape)
if err != nil {
return c.Error500(err)
}
layers = append(layers, layerdef)
}
if model.Type == 2 {
type lastLayerType struct {
Id string
Range_start int
Range_end int
}
var lastLayer lastLayerType
err := GetDBOnce(c, &lastLayer, "exp_model_head where def_id=$1 and status=3;", def.Id)
if err != nil {
return c.Error500(err)
}
layers = append(layers, layerdef{
id: lastLayer.Id,
LayerType: LAYER_DENSE,
Shape: fmt.Sprintf("%d, 1", lastLayer.Range_end-lastLayer.Range_start+1),
})
}
break
}
}
sep_mod := 100
if len(layers) > 8 {
sep_mod = 100 - (len(layers)-8)*10
}
if sep_mod < 10 {
sep_mod = 10
}
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
"Defs": defs,
"Layers": layers,
"SepMod": sep_mod,
}))
case PREPARING_ZIP_FILE:
LoadBasedOnAnswer(c.Mode, w, "/models/edit.html", c.AddMap(AnyMap{
"Model": model,
}))
default:
fmt.Printf("Unkown Status: %d\n", model.Status)
return Error500(nil)
}
return nil
return c.SendJSON(model)
})
}

View File

@ -1,66 +1,26 @@
package models
import (
"net/http"
"git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
func handleList(handle *Handle) {
handle.Get("/models", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Get("/models", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
// TODO handle admin
if c.Mode == JSON {
rows, err := handle.Db.Query("select id, name from models where user_id=$1;", c.User.Id)
if err != nil {
return Error500(err)
}
defer rows.Close()
type Row struct {
Name string `json:"name"`
Id string `json:"id"`
}
got, err := utils.GetDbMultitple[Row](c, "models where user_id=$1", c.User.Id);
if err != nil {
c.Logger.Warn("HERE 6")
return c.Error500(nil)
}
return c.SendJSON(got)
type Row struct {
Name string `json:"name"`
Id string `json:"id"`
}
rows, err := handle.Db.Query("select id, name from models where user_id=$1;", c.User.Id)
got, err := utils.GetDbMultitple[Row](c, "models where user_id=$1", c.User.Id)
if err != nil {
return Error500(err)
}
defer rows.Close()
type row struct {
Name string
Id string
return c.Error500(nil)
}
got := []row{}
for rows.Next() {
var r row
err = rows.Scan(&r.Id, &r.Name)
if err != nil {
return Error500(err)
}
got = append(got, r)
}
LoadBasedOnAnswer(c.Mode, w, "/models/list.html", c.AddMap(AnyMap{
"List": got,
}))
return nil
return c.SendJSON(got)
})
}

View File

@ -2,9 +2,7 @@ package models
import (
"bytes"
"fmt"
"io"
"net/http"
"os"
"path"
@ -118,143 +116,14 @@ func runModelExp(c *Context, model *BaseModel, def_id string, inputImage *tf.Ten
}
func handleRun(handle *Handle) {
handle.Post("/models/run", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Post("/models/run", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
read_form, err := r.MultipartReader()
if err != nil {
// TODO improve message
return ErrorCode(nil, 400, nil)
}
var id 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("Invalid multipart data")
}
if part.FormName() == "id" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
id = buf.String()
}
if part.FormName() == "file" {
buf := new(bytes.Buffer)
buf.ReadFrom(part)
file = buf.Bytes()
}
}
model, err := GetBaseModel(handle.Db, id)
if err == ModelNotFoundError {
return c.JsonBadRequest("Models not found")
} else if err != nil {
return c.Error500(err)
}
if model.Status != READY {
return c.JsonBadRequest("Model not ready to run images")
}
def := JustId{}
err = GetDBOnce(c, &def, "model_definition where model_id=$1", model.Id)
if err == NotFoundError {
return c.JsonBadRequest("Could not find definition")
} else if err != nil {
return c.Error500(err)
}
def_id := def.Id
// TODO create a database table with tasks
run_path := path.Join("/tmp", model.Id, "runs")
os.MkdirAll(run_path, os.ModePerm)
img_path := path.Join(run_path, "img."+model.Format)
img_file, err := os.Create(img_path)
if err != nil {
return c.Error500(err)
}
defer img_file.Close()
img_file.Write(file)
if !testImgForModel(c, model, img_path) {
return c.JsonBadRequest("Provided image does not match the model")
}
root := tg.NewRoot()
var tf_img *image.Image = nil
switch model.Format {
case "png":
tf_img = ReadPNG(root, img_path, int64(model.ImageMode))
case "jpeg":
tf_img = ReadJPG(root, img_path, int64(model.ImageMode))
default:
panic("Not sure what to do with '" + model.Format + "'")
}
exec_results := tg.Exec(root, []tf.Output{tf_img.Value()}, nil, &tf.SessionOptions{})
inputImage, err := tf.NewTensor(exec_results[0].Value())
if err != nil {
return c.Error500(err)
}
vi := -1
var confidence float32 = 0
if model.ModelType == 2 {
c.Logger.Info("Running model normal", "model", model.Id, "def", def_id)
vi, confidence, err = runModelExp(c, model, def_id, inputImage)
if err != nil {
return c.Error500(err)
}
} else {
c.Logger.Info("Running model normal", "model", model.Id, "def", def_id)
vi, confidence, err = runModelNormal(c, model, def_id, inputImage)
if err != nil {
return c.Error500(err)
}
}
os.RemoveAll(run_path)
rows, err := handle.Db.Query("select name from model_classes where model_id=$1 and class_order=$2;", model.Id, vi)
if err != nil {
return c.Error500(err)
}
if !rows.Next() {
return c.SendJSON(nil)
}
var name string
if err = rows.Scan(&name); err != nil {
return c.Error500(err)
}
returnValue := struct {
Class string `json:"class"`
Confidence float32 `json:"confidence"`
}{
Class: name,
Confidence: confidence,
}
return c.SendJSON(returnValue)
}
read_form, err := r.MultipartReader()
read_form, err := c.R.MultipartReader()
if err != nil {
// TODO improve message
return ErrorCode(nil, 400, nil)
return c.JsonBadRequest("Invalid muilpart body")
}
var id string
@ -265,7 +134,7 @@ func handleRun(handle *Handle) {
if err_part == io.EOF {
break
} else if err_part != nil {
return &Error{Code: http.StatusBadRequest}
return c.JsonBadRequest("Invalid multipart data")
}
if part.FormName() == "id" {
buf := new(bytes.Buffer)
@ -281,27 +150,21 @@ func handleRun(handle *Handle) {
model, err := GetBaseModel(handle.Db, id)
if err == ModelNotFoundError {
return ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
return c.JsonBadRequest("Models not found")
} else if err != nil {
return Error500(err)
return c.Error500(err)
}
if model.Status != READY {
// TODO improve this
return ErrorCode(nil, 400, c.AddMap(nil))
return c.JsonBadRequest("Model not ready to run images")
}
def := JustId{}
err = GetDBOnce(c, &def, "model_definition where model_id=$1", model.Id)
if err == NotFoundError {
// TODO improve this
fmt.Printf("Could not find definition\n")
return ErrorCode(nil, 400, c.AddMap(nil))
return c.JsonBadRequest("Could not find definition")
} else if err != nil {
return Error500(err)
return c.Error500(err)
}
def_id := def.Id
@ -313,19 +176,13 @@ func handleRun(handle *Handle) {
img_file, err := os.Create(img_path)
if err != nil {
return Error500(err)
return c.Error500(err)
}
defer img_file.Close()
img_file.Write(file)
if !testImgForModel(c, model, img_path) {
LoadDefineTemplate(w, "/models/edit.html", "run-model-card", c.AddMap(AnyMap{
"Model": model,
"NotFound": false,
"Result": nil,
"ImageError": true,
}))
return nil
return c.JsonBadRequest("Provided image does not match the model")
}
root := tg.NewRoot()
@ -344,7 +201,7 @@ func handleRun(handle *Handle) {
exec_results := tg.Exec(root, []tf.Output{tf_img.Value()}, nil, &tf.SessionOptions{})
inputImage, err := tf.NewTensor(exec_results[0].Value())
if err != nil {
return Error500(err)
return c.Error500(err)
}
vi := -1
@ -368,27 +225,25 @@ func handleRun(handle *Handle) {
rows, err := handle.Db.Query("select name from model_classes where model_id=$1 and class_order=$2;", model.Id, vi)
if err != nil {
return Error500(err)
return c.Error500(err)
}
if !rows.Next() {
LoadDefineTemplate(w, "/models/edit.html", "run-model-card", c.AddMap(AnyMap{
"Model": model,
"NotFound": true,
"Result": nil,
"Confidence": confidence,
}))
return nil
return c.SendJSON(nil)
}
var name string
if err = rows.Scan(&name); err != nil {
return nil
return c.Error500(err)
}
LoadDefineTemplate(w, "/models/edit.html", "run-model-card", c.AddMap(AnyMap{
"Model": model,
"Result": name,
}))
return nil
returnValue := struct {
Class string `json:"class"`
Confidence float32 `json:"confidence"`
}{
Class: name,
Confidence: confidence,
}
return c.SendJSON(returnValue)
})
}

View File

@ -7,7 +7,4 @@ import (
func HandleTrainEndpoints(handle *Handle) {
handleTrain(handle)
handleRest(handle)
//TODO remove
handleTest(handle)
}

View File

@ -1,7 +1,6 @@
package models_train
import (
"net/http"
"os"
"path"
@ -10,82 +9,39 @@ import (
)
func handleRest(handle *Handle) {
handle.Delete("/models/train/reset", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Delete("/models/train/reset", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
var dat struct {
Id string `json:"id"`
}
if err := c.ToJSON(r, &dat); err != nil {
return err;
}
model, err := GetBaseModel(c.Db, dat.Id)
if err == ModelNotFoundError {
return c.JsonBadRequest("Model not found");
} else if err != nil {
// TODO improve response
return c.Error500(err)
}
if model.Status != FAILED_PREPARING_TRAINING && model.Status != FAILED_TRAINING {
return c.JsonBadRequest("Model is not in status that be reset")
}
os.RemoveAll(path.Join("savedData", model.Id, "defs"))
_, err = c.Db.Exec("delete from model_definition where model_id=$1", model.Id)
if err != nil {
// TODO improve response
return c.Error500(err)
}
ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING)
return c.SendJSON(model.Id)
var dat struct {
Id string `json:"id"`
}
f, err := MyParseForm(r)
if err != nil {
// TODO improve response
return c.ErrorCode(nil, 400, c.AddMap(nil))
if err := c.ToJSON(&dat); err != nil {
return err
}
if !CheckId(f, "id") {
// TODO improve response
return c.ErrorCode(nil, 400, c.AddMap(nil))
}
id := f.Get("id")
model, err := GetBaseModel(handle.Db, id)
model, err := GetBaseModel(c.Db, dat.Id)
if err == ModelNotFoundError {
return c.ErrorCode(nil, http.StatusNotFound, AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
})
return c.JsonBadRequest("Model not found")
} else if err != nil {
// TODO improve response
return c.Error500(err)
}
if model.Status != FAILED_PREPARING_TRAINING && model.Status != FAILED_TRAINING {
// TODO improve response
return c.ErrorCode(nil, 400, c.AddMap(nil))
return c.JsonBadRequest("Model is not in status that be reset")
}
os.RemoveAll(path.Join("savedData", model.Id, "defs"))
_, err = handle.Db.Exec("delete from model_definition where model_id=$1", model.Id)
_, err = c.Db.Exec("delete from model_definition where model_id=$1", model.Id)
if err != nil {
// TODO improve response
return c.Error500(err)
}
ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING)
Redirect("/models/edit?id="+model.Id, c.Mode, w, r)
return nil
return c.SendJSON(model.Id)
})
}

View File

@ -1,175 +0,0 @@
package models_train
import (
"fmt"
"io"
"net/http"
"os"
"os/exec"
"path"
"strconv"
"strings"
"text/template"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
/*import (
tf "github.com/galeone/tensorflow/tensorflow/go"
tg "github.com/galeone/tfgo"
"github.com/galeone/tfgo/image"
"github.com/galeone/tfgo/image/filter"
"github.com/galeone/tfgo/image/padding"
)*/
func getDir() string {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
return dir
}
func shapeToSize(shape string) string {
split := strings.Split(shape, ",")
return strings.Join(split[:len(split) - 1], ",")
}
func handleTest(handle *Handle) {
handle.Post("/models/train/test", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
return nil
}
id, err := GetIdFromUrl(r, "id")
if err != nil {
return ErrorCode(err, 400, c.AddMap(nil))
}
rows, err := handle.Db.Query("select mc.name, mdp.file_path from model_classes as mc join model_data_point as mdp on mdp.class_id = mc.id where mdp.model_mode = 1 and mc.model_id = $1 limit 1;", id)
if err != nil {
return Error500(err)
}
defer rows.Close()
if !rows.Next() {
return Error500(err)
}
var name string
var file_path string
err = rows.Scan(&name, &file_path)
if err != nil {
return Error500(err)
}
file_path = strings.Replace(file_path, "file://", "", 1)
img_path := path.Join("savedData", id, "data", "training", name, file_path)
fmt.Printf("%s\n", img_path)
definitions, err := handle.Db.Query("select id from model_definition where model_id=$1 and status=2 limit 1;", id)
if err != nil {
return Error500(err)
}
defer definitions.Close()
if !definitions.Next() {
fmt.Println("Did not find definition")
return Error500(nil)
}
var definition_id string
if err = definitions.Scan(&definition_id); err != nil {
return Error500(err)
}
layers, err := handle.Db.Query("select layer_type, shape from model_definition_layer where def_id=$1 order by layer_order asc;", definition_id)
if err != nil {
return Error500(err)
}
defer layers.Close()
type layerrow struct {
LayerType int
Shape string
}
got := []layerrow{}
for layers.Next() {
var row = layerrow{}
if err = layers.Scan(&row.LayerType, &row.Shape); err != nil {
return Error500(err)
}
row.Shape = shapeToSize(row.Shape)
got = append(got, row)
}
// Generate folder
run_path := path.Join("/tmp", id, "defs", definition_id)
err = os.MkdirAll(run_path, os.ModePerm)
if err != nil {
return Error500(err)
}
f, err := os.Create(path.Join(run_path, "run.py"))
if err != nil {
return Error500(err)
}
defer f.Close()
fmt.Printf("Using path: %s\n", run_path)
tmpl, err := template.New("python_model_template.py").ParseFiles("views/py/python_model_template.py")
if err != nil {
return Error500(err)
}
if err = tmpl.Execute(f, AnyMap{
"Layers": got,
"Size": got[0].Shape,
"DataDir": path.Join(getDir(), "savedData", id, "data", "training"),
}); err != nil {
return Error500(err)
}
cmd := exec.Command("bash", "-c", fmt.Sprintf("cd %s && python run.py", run_path))
_, err = cmd.Output()
if err != nil {
return Error500(err)
}
result_path := path.Join("savedData", id, "defs", definition_id)
os.MkdirAll(result_path, os.ModePerm)
err = exec.Command("cp", path.Join(run_path, "model.keras"), path.Join(result_path, "model.keras")).Run()
if err != nil {
return Error500(err)
}
accuracy_file, err := os.Open(path.Join(run_path, "accuracy.val"))
if err != nil {
return Error500(err)
}
defer accuracy_file.Close()
accuracy_file_bytes, err := io.ReadAll(accuracy_file)
if err != nil {
return Error500(err)
}
accuracy, err := strconv.ParseFloat(string(accuracy_file_bytes), 64)
if err != nil {
return Error500(err)
}
w.Write([]byte(strconv.FormatFloat(accuracy, 'f', -1, 64)))
return nil
})
}

View File

@ -6,12 +6,12 @@ import (
"fmt"
"io"
"math"
"net/http"
"os"
"os/exec"
"path"
"sort"
"strconv"
"strings"
"text/template"
model_classes "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/classes"
@ -23,6 +23,19 @@ import (
const EPOCH_PER_RUN = 20
const MAX_EPOCH = 100
func shapeToSize(shape string) string {
split := strings.Split(shape, ",")
return strings.Join(split[:len(split)-1], ",")
}
func getDir() string {
dir, err := os.Getwd()
if err != nil {
panic(err)
}
return dir
}
func MakeDefenition(db *sql.DB, model_id string, target_accuracy int) (id string, err error) {
id = ""
rows, err := db.Query("insert into model_definition (model_id, target_accuracy) values ($1, $2) returning id;", model_id, target_accuracy)
@ -702,13 +715,13 @@ func trainModelExp(c *Context, model *BaseModel) {
ModelUpdateStatus(c, model.Id, FAILED_TRAINING)
}
var definitions TrainModelRowUsables
var definitions TrainModelRowUsables
definitions, err = GetDbMultitple[TrainModelRowUsable](c, "model_definition where status=$1 and model_id=$2", MODEL_DEFINITION_STATUS_INIT, model.Id)
if err != nil {
failed("Failed to get definitions");
return
}
definitions, err = GetDbMultitple[TrainModelRowUsable](c, "model_definition where status=$1 and model_id=$2", MODEL_DEFINITION_STATUS_INIT, model.Id)
if err != nil {
failed("Failed to get definitions")
return
}
if len(definitions) == 0 {
failed("No Definitions defined!")
return
@ -810,38 +823,38 @@ func trainModelExp(c *Context, model *BaseModel) {
}
}
// Set the class status to trained
// Set the class status to trained
err = setModelClassStatus(c, MODEL_CLASS_STATUS_TRAINED, "model_id=$1 and status=$2;", model.Id, MODEL_CLASS_STATUS_TRAINING)
if err != nil {
failed("Failed to set class status")
return
}
var dat JustId
err = GetDBOnce(c, &dat, "model_definition where model_id=$1 and status=$2 order by accuracy desc limit 1;", model.Id, MODEL_DEFINITION_STATUS_TRANIED)
if err == NotFoundError {
var dat JustId
err = GetDBOnce(c, &dat, "model_definition where model_id=$1 and status=$2 order by accuracy desc limit 1;", model.Id, MODEL_DEFINITION_STATUS_TRANIED)
if err == NotFoundError {
failed("All definitions failed to train!")
return
} else if err != nil {
} else if err != nil {
failed("DB: failed to read definition")
return
}
}
if _, err = c.Db.Exec("update model_definition set status=$1 where id=$2;", MODEL_DEFINITION_STATUS_READY, dat.Id); err != nil {
failed("Failed to update model definition")
return
}
to_delete, err := GetDbMultitple[JustId](c, "model_definition where status!=$1 and model_id=$2", MODEL_DEFINITION_STATUS_READY, model.Id)
if err != nil {
to_delete, err := GetDbMultitple[JustId](c, "model_definition where status!=$1 and model_id=$2", MODEL_DEFINITION_STATUS_READY, model.Id)
if err != nil {
failed("Failed to select model_definition to delete")
return
}
}
for _, d := range(to_delete) {
for _, d := range to_delete {
os.RemoveAll(path.Join("savedData", model.Id, "defs", d.Id))
}
}
// TODO Check if returning also works here
if _, err = c.Db.Exec("delete from model_definition where status!=$1 and model_id=$2;", MODEL_DEFINITION_STATUS_READY, model.Id); err != nil {
@ -1293,163 +1306,81 @@ func generateExpandableDefinitions(c *Context, model *BaseModel, target_accuracy
return nil
}
func handle_models_train_json(w http.ResponseWriter, r *http.Request, c *Context) *Error {
var dat struct {
Id string `json:"id"`
ModelType string `json:"model_type"`
NumberOfModels int `json:"number_of_models"`
Accuracy int `json:"accuracy"`
}
if err_ := c.ToJSON(r, &dat); err_ != nil {
return err_
}
if dat.Id == "" {
return c.JsonBadRequest("Please provide a id")
}
modelTypeId := 1
if dat.ModelType == "expandable" {
modelTypeId = 2
} else if dat.ModelType != "simple" {
return c.JsonBadRequest("Invalid model type!")
}
model, err := GetBaseModel(c.Db, dat.Id)
if err == ModelNotFoundError {
return c.JsonBadRequest("Model not found")
} else if err != nil {
return c.Error500(err)
}
if model.Status != CONFIRM_PRE_TRAINING {
return c.JsonBadRequest("Model in invalid status for training")
}
if modelTypeId == 2 {
full_error := generateExpandableDefinitions(c, model, dat.Accuracy, dat.NumberOfModels)
if full_error != nil {
return full_error
}
} else {
full_error := generateDefinitions(c, model, dat.Accuracy, dat.NumberOfModels)
if full_error != nil {
return full_error
}
}
if modelTypeId == 2 {
go trainModelExp(c, model)
} else {
go trainModel(c, model)
}
_, err = c.Db.Exec("update models set status = $1, model_type = $2 where id = $3", TRAINING, modelTypeId, model.Id)
if err != nil {
fmt.Println("Failed to update model status")
fmt.Println(err)
// TODO improve this response
return Error500(err)
}
return c.SendJSON(model.Id)
}
func handleTrain(handle *Handle) {
handle.Post("/models/train", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Post("/models/train", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
return handle_models_train_json(w, r, c)
var dat struct {
Id string `json:"id"`
ModelType string `json:"model_type"`
NumberOfModels int `json:"number_of_models"`
Accuracy int `json:"accuracy"`
}
r.ParseForm()
f := r.Form
number_of_models := 0
accuracy := 0
if !CheckId(f, "id") || CheckEmpty(f, "model_type") || !CheckNumber(f, "number_of_models", &number_of_models) || !CheckNumber(f, "accuracy", &accuracy) {
// TODO improve this response
return ErrorCode(nil, 400, c.AddMap(nil))
if err_ := c.ToJSON(&dat); err_ != nil {
return err_
}
id := f.Get("id")
model_type_id := 1
model_type_form := f.Get("model_type")
if model_type_form == "expandable" {
model_type_id = 2
} else if model_type_form != "simple" {
return c.Error400(nil, "Invalid model type!", w, "/models/edit.html", "train-model-card", AnyMap{
"HasData": true,
"ErrorMessage": "Invalid model type!",
})
if dat.Id == "" {
return c.JsonBadRequest("Please provide a id")
}
model, err := GetBaseModel(handle.Db, id)
modelTypeId := 1
if dat.ModelType == "expandable" {
modelTypeId = 2
} else if dat.ModelType != "simple" {
return c.JsonBadRequest("Invalid model type!")
}
model, err := GetBaseModel(c.Db, dat.Id)
if err == ModelNotFoundError {
return ErrorCode(nil, http.StatusNotFound, c.AddMap(AnyMap{
"NotFoundMessage": "Model not found",
"GoBackLink": "/models",
}))
return c.JsonBadRequest("Model not found")
} else if err != nil {
// TODO improve this response
return Error500(err)
return c.Error500(err)
}
if model.Status != CONFIRM_PRE_TRAINING {
// TODO improve this response
return ErrorCode(nil, 400, c.AddMap(nil))
return c.JsonBadRequest("Model in invalid status for training")
}
if model_type_id == 2 {
full_error := generateExpandableDefinitions(c, model, accuracy, number_of_models)
if modelTypeId == 2 {
full_error := generateExpandableDefinitions(c, model, dat.Accuracy, dat.NumberOfModels)
if full_error != nil {
return full_error
}
} else {
full_error := generateDefinitions(c, model, accuracy, number_of_models)
full_error := generateDefinitions(c, model, dat.Accuracy, dat.NumberOfModels)
if full_error != nil {
return full_error
}
}
if model_type_id == 2 {
if modelTypeId == 2 {
go trainModelExp(c, model)
} else {
go trainModel(c, model)
}
_, err = c.Db.Exec("update models set status = $1, model_type = $2 where id = $3", TRAINING, model_type_id, model.Id)
_, err = c.Db.Exec("update models set status = $1, model_type = $2 where id = $3", TRAINING, modelTypeId, model.Id)
if err != nil {
fmt.Println("Failed to update model status")
fmt.Println(err)
// TODO improve this response
return Error500(err)
return c.Error500(err)
}
Redirect("/models/edit?id="+model.Id, c.Mode, w, r)
return nil
return c.SendJSON(model.Id)
})
handle.Get("/model/epoch/update", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
// TODO check auth level
if c.Mode != NORMAL {
// This should only handle normal requests
c.Logger.Warn("This function only works with normal")
return c.UnsafeErrorCode(nil, 400, nil)
}
f := r.URL.Query()
handle.Get("/model/epoch/update", func(c *Context) *Error {
f := c.R.URL.Query()
accuracy := 0.0
if !CheckId(f, "model_id") || !CheckId(f, "definition") || CheckEmpty(f, "epoch") || !CheckFloat64(f, "accuracy", &accuracy) {
c.Logger.Warn("Invalid: model_id or definition or epoch or accuracy")
return c.UnsafeErrorCode(nil, 400, nil)
return c.JsonBadRequest("Invalid: model_id or definition or epoch or accuracy")
}
accuracy = accuracy * 100
@ -1458,9 +1389,7 @@ func handleTrain(handle *Handle) {
def_id := f.Get("definition")
epoch, err := strconv.Atoi(f.Get("epoch"))
if err != nil {
c.Logger.Warn("Epoch is not a number")
// No need to improve message because this function is only called internaly
return c.UnsafeErrorCode(nil, 400, nil)
return c.JsonBadRequest("Epoch is not a number")
}
rows, err := c.Db.Query("select md.status from model_definition as md where md.model_id=$1 and md.id=$2", model_id, def_id)
@ -1482,8 +1411,7 @@ func handleTrain(handle *Handle) {
if status != 3 {
c.Logger.Warn("Definition not on status 3(training)", "status", status)
// No need to improve message because this function is only called internaly
return c.UnsafeErrorCode(nil, 400, nil)
return c.JsonBadRequest("Definition not on status 3(training)")
}
c.Logger.Info("Updated model_definition!", "model", model_id, "progress", epoch, "accuracy", accuracy)
@ -1495,21 +1423,13 @@ func handleTrain(handle *Handle) {
return nil
})
handle.Get("/model/head/epoch/update", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
// TODO check auth level
if c.Mode != NORMAL {
// This should only handle normal requests
c.Logger.Warn("This function only works with normal")
return c.UnsafeErrorCode(nil, 400, nil)
}
f := r.URL.Query()
handle.Get("/model/head/epoch/update", func(c *Context) *Error {
f := c.R.URL.Query()
accuracy := 0.0
if !CheckId(f, "head_id") || CheckEmpty(f, "epoch") || !CheckFloat64(f, "accuracy", &accuracy) {
c.Logger.Warn("Invalid: model_id or head_id or epoch or accuracy")
return c.UnsafeErrorCode(nil, 400, nil)
return c.JsonBadRequest("Invalid: model_id or definition or epoch or accuracy")
}
accuracy = accuracy * 100
@ -1517,9 +1437,7 @@ func handleTrain(handle *Handle) {
head_id := f.Get("head_id")
epoch, err := strconv.Atoi(f.Get("epoch"))
if err != nil {
c.Logger.Warn("Epoch is not a number")
// No need to improve message because this function is only called internaly
return c.UnsafeErrorCode(nil, 400, nil)
return c.JsonBadRequest("Epoch is not a number")
}
rows, err := c.Db.Query("select hd.status from exp_model_head as hd where hd.id=$1;", head_id)
@ -1541,8 +1459,7 @@ func handleTrain(handle *Handle) {
if status != 3 {
c.Logger.Warn("Head not on status 3(training)", "status", status)
// No need to improve message because this function is only called internaly
return c.UnsafeErrorCode(nil, 400, nil)
return c.JsonBadRequest("Head not on status 3(training)")
}
c.Logger.Info("Updated model_head!", "head", head_id, "progress", epoch, "accuracy", accuracy)

View File

@ -1,14 +0,0 @@
package models_utils
import (
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
// TODO make this return and caller handle error
func ModelUpdateStatus(c *Context, id string, status int) {
_, err := c.Db.Exec("update models set status=$1 where id=$2;", status, id)
if err != nil {
c.Logger.Error("Failed to update model status", "err", err)
c.Logger.Warn("TODO Maybe handle better")
}
}

View File

@ -13,194 +13,28 @@ import (
"time"
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
"github.com/charmbracelet/log"
"github.com/goccy/go-json"
)
func Mul(n1 int, n2 int) int {
return n1 * n2
}
func Add(n1 int, n2 int) int {
return n1 + n2
}
func baseLoadTemplate(base string, path string) (*template.Template, any) {
funcs := map[string]any{
"startsWith": strings.HasPrefix,
"replace": strings.Replace,
"mul": Mul,
"add": Add,
}
return template.New(base).Funcs(funcs).ParseFiles(
"./views/"+base,
"./views/"+path,
"./views/partials/header.html",
)
}
func loadTemplate(path string) (*template.Template, any) {
return baseLoadTemplate("layout.html", path)
}
func LoadView(writer http.ResponseWriter, path string, data interface{}) {
tmpl, err := loadTemplate(path)
if err != nil {
fmt.Printf("Failed to load view %s\n", path)
fmt.Println(err)
if path == "500.html" {
writer.Write([]byte("<h1>Failed to load 500.html check console for more info</h1>"))
} else {
LoadView(writer, "500.html", nil)
}
return
}
if err := tmpl.Execute(writer, data); err != nil {
fmt.Printf("Failed to load view %s\n", path)
fmt.Println(err)
writer.WriteHeader(http.StatusInternalServerError)
if path == "500.html" {
writer.Write([]byte("<h1>Failed to load 500.html check console for more info</h1>"))
} else {
LoadView(writer, "500.html", nil)
}
return
}
}
/** Only returns the html without template */
func LoadHtml(writer http.ResponseWriter, path string, data interface{}) {
tmpl, err := baseLoadTemplate("html.html", path)
if err != nil {
fmt.Printf("Failed to load template %s\n", path)
fmt.Println(err)
writer.WriteHeader(http.StatusInternalServerError)
if path == "500.html" {
writer.Write([]byte("<h1>Failed to load 500.html check console for more info</h1>"))
} else {
LoadHtml(writer, "500.html", nil)
}
return
}
if err := tmpl.Execute(writer, data); err != nil {
fmt.Printf("Failed to execute template %s\n", path)
fmt.Println(err)
writer.WriteHeader(http.StatusInternalServerError)
if path == "500.html" {
writer.Write([]byte("<h1>Failed to load 500.html check console for more info</h1>"))
} else {
LoadHtml(writer, "500.html", nil)
}
return
}
}
func LoadDefineTemplate(writer http.ResponseWriter, path string, base string, data AnyMap) {
if data == nil {
data = map[string]interface{}{
"Error": true,
}
} else {
data["Error"] = true
}
funcs := map[string]any{
"startsWith": strings.HasPrefix,
"mul": Mul,
"replace": strings.Replace,
"add": Add,
}
tmpl, err := template.New("").Funcs(funcs).Parse("{{template \"" + base + "\" . }}")
if err != nil {
panic("Lol")
}
tmpl, err = tmpl.ParseFiles(
"./views/"+path,
"./views/partials/header.html",
)
if err != nil {
fmt.Printf("Failed to load template %s\n", path)
fmt.Println(err)
writer.WriteHeader(http.StatusInternalServerError)
if path == "500.html" {
writer.Write([]byte("<h1>Failed to load 500.html check console for more info</h1>"))
} else {
LoadHtml(writer, "500.html", nil)
}
return
}
if err := tmpl.Execute(writer, data); err != nil {
fmt.Printf("Failed to execute template %s\n", path)
fmt.Println(err)
writer.WriteHeader(http.StatusInternalServerError)
if path == "500.html" {
writer.Write([]byte("<h1>Failed to load 500.html check console for more info</h1>"))
} else {
LoadHtml(writer, "500.html", nil)
}
return
}
}
type AnyMap = map[string]interface{}
type Error struct {
Code int
Msg *string
data AnyMap
}
type AnswerType int
const (
NORMAL AnswerType = 1 << iota
HTML
JSON
HTMLFULL
)
func LoadBasedOnAnswer(ans AnswerType, w http.ResponseWriter, path string, data map[string]interface{}) {
if ans == NORMAL {
LoadView(w, path, data)
return
} else if ans == HTML {
LoadHtml(w, path, data)
return
} else if ans == HTMLFULL {
if data == nil {
LoadHtml(w, path, map[string]interface{}{
"App": true,
})
} else {
data["App"] = true
LoadHtml(w, path, data)
}
return
} else if ans == JSON {
panic("TODO JSON!")
} else {
panic("unreachable")
}
data any
}
type HandleFunc struct {
path string
mode AnswerType
fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error
fn func(c *Context) *Error
}
type Handler interface {
New()
Startup()
Get(fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error)
Post(fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error)
Get(fn func(c *Context) *Error)
Post(fn func(c *Context) *Error)
}
type Handle struct {
@ -219,202 +53,162 @@ func decodeBody(r *http.Request) (string, *Error) {
return string(body[:]), nil
}
func handleError(err *Error, w http.ResponseWriter, context *Context) {
func handleError(err *Error, c *Context) {
if err != nil {
data := context.AddMap(err.data)
if err.Code == http.StatusNotFound {
if context.Mode == HTML {
w.WriteHeader(309)
context.Mode = HTMLFULL
}
LoadBasedOnAnswer(context.Mode, w, "404.html", data)
return
c.Logger.Warn("Responded with error", "code", err.Code, "data", err.data)
c.Writer.WriteHeader(err.Code)
var e *Error
if err.data != nil {
e = c.SendJSON(err.data)
} else {
e = c.SendJSON(500)
}
w.WriteHeader(err.Code)
if err.Code == http.StatusBadRequest {
LoadBasedOnAnswer(context.Mode, w, "400.html", data)
return
}
if err.Msg != nil {
w.Write([]byte(*err.Msg))
if e != nil {
c.Logger.Error("Something went very wront while trying to send and error message")
c.Writer.Write([]byte("505"))
}
}
}
func (x *Handle) Get(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.gets = append(x.gets, HandleFunc{path, NORMAL | HTML | HTMLFULL | JSON, fn})
func (x *Handle) Get(path string, fn func(c *Context) *Error) {
x.gets = append(x.gets, HandleFunc{path, fn})
}
func (x *Handle) GetHTML(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.gets = append(x.gets, HandleFunc{path, NORMAL | HTML | HTMLFULL, fn})
func (x *Handle) Post(path string, fn func(c *Context) *Error) {
x.posts = append(x.posts, HandleFunc{path, fn})
}
func (x *Handle) GetJSON(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.gets = append(x.gets, HandleFunc{path, JSON, fn})
func (x *Handle) Delete(path string, fn func(c *Context) *Error) {
x.deletes = append(x.deletes, HandleFunc{path, fn})
}
func (x *Handle) Post(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.posts = append(x.posts, HandleFunc{path, NORMAL | HTML | HTMLFULL | JSON, fn})
}
func (x *Handle) PostHTML(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.posts = append(x.posts, HandleFunc{path, NORMAL | HTML | HTMLFULL, fn})
}
func (x *Handle) PostJSON(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.posts = append(x.posts, HandleFunc{path, JSON, fn})
}
func (x *Handle) Delete(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.deletes = append(x.deletes, HandleFunc{path, NORMAL | HTML | HTMLFULL | JSON, fn})
}
func (x *Handle) DeleteHTML(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.deletes = append(x.deletes, HandleFunc{path, NORMAL | HTML | HTMLFULL, fn})
}
func (x *Handle) DeleteJSON(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
x.deletes = append(x.deletes, HandleFunc{path, JSON, fn})
}
func (x *Handle) handleGets(w http.ResponseWriter, r *http.Request, context *Context) {
func (x *Handle) handleGets(context *Context) {
for _, s := range x.gets {
if s.path == r.URL.Path && context.Mode&s.mode != 0 {
handleError(s.fn(w, r, context), w, context)
if s.path == context.R.URL.Path {
handleError(s.fn(context), context)
return
}
}
if context.Mode != HTMLFULL {
w.WriteHeader(http.StatusNotFound)
}
LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
handleError(&Error{404, "Endpoint not found"}, context)
}
func (x *Handle) handlePosts(w http.ResponseWriter, r *http.Request, context *Context) {
func (x *Handle) handlePosts(context *Context) {
for _, s := range x.posts {
if s.path == r.URL.Path && context.Mode&s.mode != 0 {
handleError(s.fn(w, r, context), w, context)
if s.path == context.R.URL.Path {
handleError(s.fn(context), context)
return
}
}
if context.Mode != HTMLFULL {
w.WriteHeader(http.StatusNotFound)
}
LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
handleError(&Error{404, "Endpoint not found"}, context)
}
func (x *Handle) handleDeletes(w http.ResponseWriter, r *http.Request, context *Context) {
func (x *Handle) handleDeletes(context *Context) {
for _, s := range x.deletes {
if s.path == r.URL.Path && context.Mode&s.mode != 0 {
handleError(s.fn(w, r, context), w, context)
if s.path == context.R.URL.Path {
handleError(s.fn(context), context)
return
}
}
if context.Mode != HTMLFULL {
w.WriteHeader(http.StatusNotFound)
}
LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
handleError(&Error{404, "Endpoint not found"}, context)
}
func CheckAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Context) bool {
func (c *Context) CheckAuthLevel(authLevel int) bool {
if authLevel > 0 {
if c.requireAuth(w, r) {
Logoff(c.Mode, w, r)
if c.requireAuth() {
c.Logoff()
return false
}
if c.User.UserType < authLevel {
notAuth(c.Mode, w, r)
c.NotAuth()
return false
}
}
return true
}
func AnswerTemplate(path string, data AnyMap, authLevel int) func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
return func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(authLevel, w, r, c) {
return nil
}
LoadBasedOnAnswer(c.Mode, w, path, c.AddMap(data))
return nil
}
}
type Context struct {
Token *string
User *dbtypes.User
Mode AnswerType
Logger *log.Logger
Db *sql.DB
Writer http.ResponseWriter
Writer http.ResponseWriter
R *http.Request
}
func (c Context) ToJSON(r *http.Request, dat any) *Error {
decoder := json.NewDecoder(r.Body)
func (c Context) ToJSON(dat any) *Error {
err := decoder.Decode(dat)
if err != nil {
return c.Error500(err)
}
decoder := json.NewDecoder(c.R.Body)
return nil
err := decoder.Decode(dat)
if err != nil {
return c.Error500(err)
}
return nil
}
func (c Context) SendJSON(dat any) *Error {
c.Writer.Header().Add("content-type", "application/json")
text, err := json.Marshal(dat)
if err != nil {
return c.Error500(err)
}
if _, err = c.Writer.Write(text); err != nil {
return c.Error500(err)
}
return nil
c.Writer.Header().Add("content-type", "application/json")
text, err := json.Marshal(dat)
if err != nil {
return c.Error500(err)
}
if _, err = c.Writer.Write(text); err != nil {
return c.Error500(err)
}
return nil
}
func (c Context) SendJSONStatus(status int, dat any) *Error {
c.Writer.Header().Add("content-type", "application/json")
c.Writer.WriteHeader(status)
text, err := json.Marshal(dat)
if err != nil {
return c.Error500(err)
}
if _, err = c.Writer.Write(text); err != nil {
return c.Error500(err)
}
return nil
c.Writer.Header().Add("content-type", "application/json")
c.Writer.WriteHeader(status)
text, err := json.Marshal(dat)
if err != nil {
return c.Error500(err)
}
if _, err = c.Writer.Write(text); err != nil {
return c.Error500(err)
}
return nil
}
func (c Context) JsonBadRequest(dat any) *Error {
c.SetReportCaller(true)
c.Logger.Warn("Request failed with a bad request", "dat", dat)
c.SetReportCaller(false)
return c.SendJSONStatus(http.StatusBadRequest, dat)
c.SetReportCaller(true)
c.Logger.Warn("Request failed with a bad request", "dat", dat)
c.SetReportCaller(false)
return c.SendJSONStatus(http.StatusBadRequest, dat)
}
func (c Context) JsonErrorBadRequest(err error, dat any) *Error {
c.SetReportCaller(true)
c.Logger.Error("Error while processing request", "err", err, "dat", dat)
c.SetReportCaller(false)
return c.SendJSONStatus(http.StatusBadRequest, dat)
c.SetReportCaller(true)
c.Logger.Error("Error while processing request", "err", err, "dat", dat)
c.SetReportCaller(false)
return c.SendJSONStatus(http.StatusBadRequest, dat)
}
func (c Context) Error400(err error, message string, w http.ResponseWriter, path string, base string, data AnyMap) *Error {
c.SetReportCaller(true)
c.Logger.Error(message)
c.SetReportCaller(false)
func (c *Context) GetModelFromId(id_path string) (*BaseModel, *Error) {
id, err := GetIdFromUrl(c, id_path)
if err != nil {
c.Logger.Errorf("Something went wrong returning with: %d\n.Err:\n", http.StatusBadRequest)
c.Logger.Error(err)
return nil, c.SendJSONStatus(http.StatusNotFound, "Model not found")
}
if c.Mode == JSON {
return &Error{http.StatusBadRequest, nil, c.AddMap(data)}
model, err := GetBaseModel(c.Db, id)
if err == ModelNotFoundError {
return nil, c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return nil, c.Error500(err)
}
LoadDefineTemplate(w, path, base, c.AddMap(data))
return nil
return model, nil
}
func ModelUpdateStatus(c *Context, id string, status int) {
_, err := c.Db.Exec("update models set status=$1 where id=$2;", status, id)
if err != nil {
c.Logger.Error("Failed to update model status", "err", err)
c.Logger.Warn("TODO Maybe handle better")
}
}
func (c Context) SetReportCaller(report bool) {
@ -427,7 +221,7 @@ func (c Context) SetReportCaller(report bool) {
}
}
func (c Context) ErrorCode(err error, code int, data AnyMap) *Error {
func (c Context) ErrorCode(err error, code int, data any) *Error {
if code == 400 {
c.SetReportCaller(true)
c.Logger.Warn("When returning BadRequest(400) please use context.Error400\n")
@ -437,32 +231,14 @@ func (c Context) ErrorCode(err error, code int, data AnyMap) *Error {
c.Logger.Errorf("Something went wrong returning with: %d\n.Err:\n", code)
c.Logger.Error(err)
}
return &Error{code, nil, c.AddMap(data)}
}
func (c Context) UnsafeErrorCode(err error, code int, data AnyMap) *Error {
if err != nil {
c.Logger.Errorf("Something went wrong returning with: %d\n.Err:\n", code)
c.Logger.Error(err)
}
return &Error{code, nil, c.AddMap(data)}
return &Error{code, data}
}
func (c Context) Error500(err error) *Error {
return c.ErrorCode(err, http.StatusInternalServerError, nil)
}
func (c Context) AddMap(m AnyMap) AnyMap {
if m == nil {
return map[string]interface{}{
"Context": c,
}
}
m["Context"] = c
return m
}
func (c *Context) requireAuth(w http.ResponseWriter, r *http.Request) bool {
func (c *Context) requireAuth() bool {
if c.User == nil {
return true
}
@ -471,7 +247,7 @@ func (c *Context) requireAuth(w http.ResponseWriter, r *http.Request) bool {
var LogoffError = errors.New("Invalid token!")
func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request, w http.ResponseWriter) (*Context, error) {
func (x Handle) createContext(handler *Handle, r *http.Request, w http.ResponseWriter) (*Context, error) {
var token *string
@ -482,27 +258,18 @@ func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request,
Prefix: r.URL.Path,
})
if mode != JSON {
for _, r := range r.Cookies() {
if r.Name == "auth" {
token = &r.Value
}
}
} else {
t := r.Header.Get("token")
if t != "" {
token = &t
}
}
t := r.Header.Get("token")
if t != "" {
token = &t
}
// TODO check that the token is still valid
if token == nil {
return &Context{
Mode: mode,
Logger: logger,
Db: handler.Db,
Writer: w,
Writer: w,
R: r,
}, nil
}
@ -511,52 +278,22 @@ func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request,
return nil, errors.Join(err, LogoffError)
}
return &Context{token, user, mode, logger, handler.Db, w}, nil
return &Context{token, user, logger, handler.Db, w, r}, nil
}
// TODO check if I can use http.Redirect
func Redirect(path string, mode AnswerType, w http.ResponseWriter, r *http.Request) {
w.Header().Set("Location", path)
if mode == JSON {
w.WriteHeader(http.StatusSeeOther)
w.Write([]byte(path))
return
}
if mode&(HTMLFULL|HTML) != 0 {
w.Header().Add("HX-Redirect", path)
w.WriteHeader(204)
} else {
w.WriteHeader(http.StatusSeeOther)
}
func contextlessLogoff(w http.ResponseWriter) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("\"Not Authorized\""))
}
func Logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) {
if (mode == JSON) {
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte("\"Not Authorized\""))
} else {
// Delete cookie
cookie := &http.Cookie{
Name: "auth",
Value: "",
Expires: time.Unix(0, 0),
}
http.SetCookie(w, cookie)
Redirect("/login", mode, w, r)
}
}
func (c *Context) Logoff() { contextlessLogoff(c.Writer) }
func notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) {
if mode == JSON {
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("\"You can not access this resource!\""))
return
}
if mode&(HTMLFULL|HTML) != 0 {
w.WriteHeader(http.StatusForbidden)
w.Write([]byte("You can not access this resource!"))
} else {
w.WriteHeader(http.StatusForbidden)
func (c *Context) NotAuth() {
c.Writer.WriteHeader(http.StatusUnauthorized)
e := c.SendJSON("Not Authorized")
if e != nil {
c.Writer.WriteHeader(http.StatusInternalServerError)
c.Writer.Write([]byte("You can not access this resource!"))
}
}
@ -583,21 +320,6 @@ func (x Handle) StaticFiles(pathTest string, fileType string, contentType string
})
}
func ErrorCode(err error, code int, data AnyMap) *Error {
log.Warn("This function is deprecated please use the one provided by context")
// TODO Improve Logging
if err != nil {
fmt.Printf("Something went wrong returning with: %d\n.Err:\n", code)
fmt.Println(err)
}
return &Error{code, nil, data}
}
func Error500(err error) *Error {
log.Warn("This function is deprecated please use the one provided by context")
return ErrorCode(err, http.StatusInternalServerError, nil)
}
func (x Handle) ReadFiles(pathTest string, baseFilePath string, fileType string, contentType string) {
http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) {
user_path := r.URL.Path[len(pathTest):]
@ -661,9 +383,9 @@ func (x Handle) ReadTypesFiles(pathTest string, baseFilePath string, fileTypes [
}
func (x Handle) ReadTypesFilesApi(pathTest string, baseFilePath string, fileTypes []string, contentTypes []string) {
http.HandleFunc("/api" + pathTest, func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1)
http.HandleFunc("/api"+pathTest, func(w http.ResponseWriter, r *http.Request) {
r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1)
user_path := r.URL.Path[len(pathTest):]
found := false
@ -704,52 +426,50 @@ func NewHandler(db *sql.DB) *Handle {
x := &Handle{db, gets, posts, deletes}
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
w.Header().Add("Access-Control-Allow-Origin", "*")
w.Header().Add("Access-Control-Allow-Headers", "*")
w.Header().Add("Access-Control-Allow-Methods", "*")
// Decide answertype
ans := NORMAL
if r.Header.Get("HX-Request") == "true" || r.Header.Get("Request-Type") == "html" {
ans = HTML
}
if r.Header.Get("Request-Type") == "htmlfull" {
ans = HTMLFULL
}
if r.Header.Get("content-type") == "application/json" || r.Header.Get("response-type") == "application/json" {
ans = JSON
}
if !strings.HasPrefix(r.URL.Path, "/api") {
return
}
r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1)
w.Header().Add("Access-Control-Allow-Origin", "*")
w.Header().Add("Access-Control-Allow-Headers", "*")
w.Header().Add("Access-Control-Allow-Methods", "*")
//Login state
context, err := x.createContext(x, ans, r, w)
if err != nil {
Logoff(ans, w, r)
if !(r.Header.Get("content-type") == "application/json" || r.Header.Get("response-type") == "application/json") {
w.WriteHeader(500)
w.Write([]byte("Please set content-type to application/json or set response-type to application/json\n"))
return
}
// context.Logger.Info("Parsing", "path", r.URL.Path)
if !strings.HasPrefix(r.URL.Path, "/api") {
w.WriteHeader(404)
w.Write([]byte("Path not found"))
return
}
r.URL.Path = strings.Replace(r.URL.Path, "/api", "", 1)
//Login state
context, err := x.createContext(x, r, w)
if err != nil {
contextlessLogoff(w)
return
}
// context.Logger.Info("Parsing", "path", r.URL.Path)
if r.Method == "GET" {
x.handleGets(w, r, context)
x.handleGets(context)
return
}
if r.Method == "POST" {
x.handlePosts(w, r, context)
x.handlePosts(context)
return
}
if r.Method == "DELETE" {
x.handleDeletes(w, r, context)
x.handleDeletes(context)
return
}
if r.Method == "OPTIONS" {
return
}
if r.Method == "OPTIONS" {
return
}
panic("TODO handle method: " + r.Method)
})

View File

@ -58,12 +58,12 @@ func IsValidUUID(u string) bool {
return err == nil
}
func GetIdFromUrl(r *http.Request, target string) (string, error) {
if !r.URL.Query().Has(target) {
func GetIdFromUrl(c *Context, target string) (string, error) {
if !c.R.URL.Query().Has(target) {
return "", errors.New("Query does not have " + target)
}
id := r.URL.Query().Get("id")
id := c.R.URL.Query().Get("id")
if len(id) == 0 {
return "", errors.New("Query is empty for " + target)
}

View File

@ -50,8 +50,6 @@ func main() {
handle.ReadTypesFiles("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"})
handle.ReadTypesFilesApi("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"})
handle.GetHTML("/", AnswerTemplate("index.html", nil, 0))
usersEndpints(db, handle)
HandleModels(handle)

439
users.go
View File

@ -6,7 +6,6 @@ import (
"encoding/hex"
"io"
"net/http"
"time"
"golang.org/x/crypto/bcrypt"
@ -76,187 +75,68 @@ func generateToken(db *sql.DB, email string, password string) (string, bool) {
}
func usersEndpints(db *sql.DB, handle *Handle) {
handle.GetHTML("/login", AnswerTemplate("login.html", nil, 0))
handle.Post("/login", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
type UserLogin struct {
Email string `json:"email"`
Password string `json:"password"`
}
var dat UserLogin
if err := c.ToJSON(r, &dat); err != nil {
return err
}
// TODO Give this to the generateToken function
token, login := generateToken(db, dat.Email, dat.Password)
if !login {
return c.SendJSONStatus(http.StatusUnauthorized, "Email or password are incorrect")
}
user, err := dbtypes.UserFromToken(c.Db, token)
if err != nil {
return c.Error500(err)
}
type UserReturn struct {
Token string `json:"token"`
Id string `json:"id"`
UserType int `json:"user_type"`
Username string `json:"username"`
Email string `json:"email"`
}
userReturn := UserReturn{
Token: token,
Id: user.Id,
UserType: user.UserType,
Username: user.Username,
Email: user.Email,
}
return c.SendJSON(userReturn)
handle.Post("/login", func(c *Context) *Error {
type UserLogin struct {
Email string `json:"email"`
Password string `json:"password"`
}
r.ParseForm()
f := r.Form
var dat UserLogin
if CheckEmpty(f, "email") || CheckEmpty(f, "password") {
LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{
"Submited": true,
}))
return nil
if err := c.ToJSON(&dat); err != nil {
return err
}
email := f.Get("email")
password := f.Get("password")
// TODO Give this to the generateToken function
expiration := time.Now().Add(24 * time.Hour)
token, login := generateToken(db, email, password)
token, login := generateToken(db, dat.Email, dat.Password)
if !login {
LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{
"Submited": true,
"NoUserOrPassword": true,
"Email": email,
}))
return nil
return c.SendJSONStatus(http.StatusUnauthorized, "Email or password are incorrect")
}
cookie := &http.Cookie{Name: "auth", Value: token, HttpOnly: false, Expires: expiration}
http.SetCookie(w, cookie)
user, err := dbtypes.UserFromToken(c.Db, token)
if err != nil {
return c.Error500(err)
}
w.Header().Set("Location", "/")
w.WriteHeader(http.StatusSeeOther)
return nil
type UserReturn struct {
Token string `json:"token"`
Id string `json:"id"`
UserType int `json:"user_type"`
Username string `json:"username"`
Email string `json:"email"`
}
userReturn := UserReturn{
Token: token,
Id: user.Id,
UserType: user.UserType,
Username: user.Username,
Email: user.Email,
}
return c.SendJSON(userReturn)
})
handle.GetHTML("/register", AnswerTemplate("register.html", nil, 0))
handle.Post("/register", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
type UserLogin struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
}
var dat UserLogin
if err := c.ToJSON(r, &dat); err != nil {
return err
}
if len(dat.Username) == 0 || len(dat.Password) == 0 || len(dat.Email) == 0 {
return c.SendJSONStatus(http.StatusBadRequest, "Please provide a valid json")
}
rows, err := db.Query("select username, email from users where username=$1 or email=$2;", dat.Username, dat.Email)
if err != nil {
return c.Error500(err)
}
defer rows.Close()
if rows.Next() {
var db_username string
var db_email string
err = rows.Scan(&db_username, &db_email)
if err != nil {
return c.Error500(err)
}
if db_email == dat.Email {
return c.SendJSONStatus(http.StatusBadRequest, "Email already in use!")
}
if db_username == dat.Username {
return c.SendJSONStatus(http.StatusBadRequest, "Username already in use!")
}
panic("Unrechable")
}
if len([]byte(dat.Password)) > 68 {
return c.JsonBadRequest("Password is to long!")
}
salt := generateSalt()
hash_password, err := hashPassword(dat.Password, salt)
if err != nil {
return c.Error500(err)
}
_, err = db.Exec("insert into users (username, email, salt, password) values ($1, $2, $3, $4);", dat.Username, dat.Email, salt, hash_password)
if err != nil {
return c.Error500(err)
}
// TODO Give this to the generateToken function
token, login := generateToken(db, dat.Email, dat.Password)
if !login {
return c.SendJSONStatus(500, "Could not login after creatting account please try again later")
}
user, err := dbtypes.UserFromToken(c.Db, token)
if err != nil {
return c.Error500(err)
}
type UserReturn struct {
Token string `json:"token"`
Id string `json:"id"`
UserType int `json:"user_type"`
Username string `json:"username"`
Email string `json:"email"`
}
userReturn := UserReturn{
Token: token,
Id: user.Id,
UserType: user.UserType,
Username: user.Username,
Email: user.Email,
}
return c.SendJSON(userReturn)
handle.Post("/register", func(c *Context) *Error {
type UserLogin struct {
Username string `json:"username"`
Email string `json:"email"`
Password string `json:"password"`
}
r.ParseForm()
f := r.Form
var dat UserLogin
if CheckEmpty(f, "email") || CheckEmpty(f, "password") || CheckEmpty(f, "username") {
LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{
"Submited": true,
})
return nil
if err := c.ToJSON(&dat); err != nil {
return err
}
email := f.Get("email")
username := f.Get("username")
password := f.Get("password")
if len(dat.Username) == 0 || len(dat.Password) == 0 || len(dat.Email) == 0 {
return c.SendJSONStatus(http.StatusBadRequest, "Please provide a valid json")
}
rows, err := db.Query("select username, email from users where username=$1 or email=$2;", username, email)
rows, err := db.Query("select username, email from users where username=$1 or email=$2;", dat.Username, dat.Email)
if err != nil {
panic("TODO handle this")
return c.Error500(err)
}
defer rows.Close()
@ -265,85 +145,95 @@ func usersEndpints(db *sql.DB, handle *Handle) {
var db_email string
err = rows.Scan(&db_username, &db_email)
if err != nil {
panic("TODO handle this better")
return c.Error500(err)
}
LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{
"Submited": true,
"Email": email,
"Username": username,
"EmailError": db_email == email,
"UserError": db_username == username,
})
return nil
if db_email == dat.Email {
return c.SendJSONStatus(http.StatusBadRequest, "Email already in use!")
}
if db_username == dat.Username {
return c.SendJSONStatus(http.StatusBadRequest, "Username already in use!")
}
panic("Unrechable")
}
if len([]byte(password)) > 68 {
LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{
"Submited": true,
"Email": email,
"Username": username,
"PasswordToLong": true,
})
return nil
if len([]byte(dat.Password)) > 68 {
return c.JsonBadRequest("Password is to long!")
}
salt := generateSalt()
hash_password, err := hashPassword(password, salt)
hash_password, err := hashPassword(dat.Password, salt)
if err != nil {
return &Error{
Code: http.StatusInternalServerError,
}
return c.Error500(err)
}
_, err = db.Exec("insert into users (username, email, salt, password) values ($1, $2, $3, $4);", username, email, salt, hash_password)
_, err = db.Exec("insert into users (username, email, salt, password) values ($1, $2, $3, $4);", dat.Username, dat.Email, salt, hash_password)
if err != nil {
return &Error{
Code: http.StatusInternalServerError,
}
return c.Error500(err)
}
// TODO Give this to the generateToken function
expiration := time.Now().Add(24 * time.Hour)
token, login := generateToken(db, email, password)
token, login := generateToken(db, dat.Email, dat.Password)
if !login {
msg := "Login failed"
return &Error{
Code: http.StatusInternalServerError,
Msg: &msg,
}
return c.SendJSONStatus(500, "Could not login after creatting account please try again later")
}
cookie := &http.Cookie{Name: "auth", Value: token, HttpOnly: false, Expires: expiration}
http.SetCookie(w, cookie)
w.Header().Set("Location", "/")
w.WriteHeader(http.StatusSeeOther)
return nil
user, err := dbtypes.UserFromToken(c.Db, token)
if err != nil {
return c.Error500(err)
}
type UserReturn struct {
Token string `json:"token"`
Id string `json:"id"`
UserType int `json:"user_type"`
Username string `json:"username"`
Email string `json:"email"`
}
userReturn := UserReturn{
Token: token,
Id: user.Id,
UserType: user.UserType,
Username: user.Username,
Email: user.Email,
}
return c.SendJSON(userReturn)
})
handle.Get("/user/info", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
// TODO allow admin users to update this data
handle.Get("/user/info", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
return c.Error500(nil)
user, err := dbtypes.UserFromToken(c.Db, *c.Token)
if err != nil {
return c.Error500(err)
}
LoadBasedOnAnswer(c.Mode, w, "users/edit.html", c.AddMap(AnyMap{
"Email": c.User.Email,
}))
return nil
type UserReturn struct {
Id string `json:"id"`
UserType int `json:"user_type"`
Username string `json:"username"`
Email string `json:"email"`
}
userReturn := UserReturn{
Id: user.Id,
UserType: user.UserType,
Username: user.Username,
Email: user.Email,
}
return c.SendJSON(userReturn)
})
// Handles updating users
handle.Post("/user/info", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(int(dbtypes.User_Normal), w, r, c) {
handle.Post("/user/info", func(c *Context) *Error {
if !c.CheckAuthLevel(int(dbtypes.User_Normal)) {
return nil
}
if c.Mode != JSON {
return c.Error500(nil)
}
type UserData struct {
Id string `json:"id"`
@ -352,7 +242,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
var dat UserData
if err := c.ToJSON(r, &dat); err != nil {
if err := c.ToJSON(&dat); err != nil {
return err
}
@ -417,108 +307,38 @@ func usersEndpints(db *sql.DB, handle *Handle) {
return c.SendJSON(toReturnUser)
})
handle.Post("/user/info/email", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
handle.Post("/user/info/password", func(c *Context) *Error {
if !c.CheckAuthLevel(1) {
return nil
}
if c.Mode == JSON {
return c.Error500(nil)
var dat struct {
Old_Password string `json:"old_password"`
Password string `json:"password"`
Password2 string `json:"password2"`
}
r.ParseForm()
if CheckEmpty(r.Form, "email") {
return c.Error400(nil, "Email Not provided", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{
"Email": c.User.Email,
}))
if err := c.ToJSON(&dat); err != nil {
return err
}
_, err := c.Db.Exec("update users set email=$1 where id=$2", r.Form.Get("email"), c.User.Id)
if err != nil {
return c.Error500(err)
if dat.Password == "" {
return c.JsonBadRequest("Password can not be empty")
}
LoadBasedOnAnswer(c.Mode, w, "users/edit.html", c.AddMap(AnyMap{
"Email": r.Form.Get("email"),
}))
return nil
})
handle.Post("/user/info/password", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if !CheckAuthLevel(1, w, r, c) {
return nil
}
if c.Mode == JSON {
var dat struct {
Old_Password string `json:"old_password"`
Password string `json:"password"`
Password2 string `json:"password2"`
}
if err := c.ToJSON(r, &dat); err != nil {
return err
}
if dat.Password == "" {
return c.JsonBadRequest("Password can not be empty")
}
if dat.Password != dat.Password2 {
return c.JsonBadRequest("New passwords did not match")
}
c.Logger.Warn("test", "dat", dat)
_, login := generateToken(db, c.User.Email, dat.Old_Password)
if !login {
return c.JsonBadRequest("Password is incorrect");
}
salt := generateSalt()
hash_password, err := hashPassword(dat.Password, salt)
if err != nil {
return c.Error500(err)
}
_, err = db.Exec("update users set salt=$1, password=$2 where id=$3", salt, hash_password, c.User.Id)
if err != nil {
return c.Error500(err)
}
return c.SendJSON(c.User.Id)
if dat.Password != dat.Password2 {
return c.JsonBadRequest("New passwords did not match")
}
r.ParseForm()
f := r.Form
c.Logger.Warn("test", "dat", dat)
if CheckEmpty(f, "old_password") || CheckEmpty(f, "password") || CheckEmpty(f, "password2") {
return c.Error400(nil, "OldPassword, Password or Password2 not provided!", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{
"Email": c.User.Email,
"NoUserOrPassword": true,
}))
}
password := f.Get("password")
password2 := f.Get("password2")
if password != password2 {
return c.Error400(nil, "New passwords did not match", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{
"Email": c.User.Email,
"PasswordNotTheSame": true,
}))
}
_, login := generateToken(db, c.User.Email, f.Get("old_password"))
_, login := generateToken(db, c.User.Email, dat.Old_Password)
if !login {
return c.Error400(nil, "Password was incorrect", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{
"Email": c.User.Email,
"NoUserOrPassword": true,
}))
return c.JsonBadRequest("Password is incorrect")
}
salt := generateSalt()
hash_password, err := hashPassword(password, salt)
hash_password, err := hashPassword(dat.Password, salt)
if err != nil {
return c.Error500(err)
}
@ -528,17 +348,8 @@ func usersEndpints(db *sql.DB, handle *Handle) {
return c.Error500(err)
}
LoadBasedOnAnswer(c.Mode, w, "users/edit.html", c.AddMap(AnyMap{
"email": c.User.Email,
}))
return nil
return c.SendJSON(c.User.Id)
})
handle.Get("/logout", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
if c.Mode == JSON {
panic("TODO handle json")
}
Logoff(c.Mode, w, r)
return nil
})
// TODO create function to remove token
}

View File

@ -1,28 +0,0 @@
{{ define "mainbody" }}
<div class="page404">
<div>
<h1>
404
</h1>
{{ if .NotFoundMessage }}
<h2>
{{ .NotFoundMessage }}
</h2>
{{ if .GoBackLink }}
<div class="description">
<a hx-get="{{ .GoBackLink }}" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
👈 Go back
</a>
</div>
{{ end }}
{{ else }}
<h2>
Page Not found
</h2>
<div class="description">
The page you were looking for does not exist
</div>
{{ end }}
</div>
</div>
{{ end }}

View File

@ -1,6 +0,0 @@
{{ define "head" }}
<title>
Error Page
</title>
{{ end }}
{{ define "body" }}Heyyyyyy Err {{ end }}

View File

@ -1,19 +0,0 @@
{{ if .Full }}
<body>
{{ block "body" . }}
{{ block "header.html" . }} {{end}}
<div class="app">
{{ block "mainbody" . }} {{end}}
</div>
{{end}}
</body>
{{ else }}
{{if .App }}
<div class="app">
{{ block "mainbody" . }} {{end}}
</div>
{{ else }}
{{ block "mainbody" . }} {{end}}
{{end}}
{{end}}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

View File

@ -1,6 +0,0 @@
{{ define "title"}} Home : AI Stuff {{ end }}
{{ define "mainbody" }}
Main Page TODO
{{ end }}

View File

@ -1,150 +0,0 @@
function tabs() {
for (const elm of document.querySelectorAll(".tabs")) {
let count = 0;
let selected = 0;
for (const child of elm.children) {
if (child.tagName == "BUTTON") {
count++;
if (child.classList.contains("selected")) {
selected++;
}
if (!child.getAttribute("data-armed")) {
child.addEventListener("click", () => {
if (!child.classList.contains("selected")) {
for (const elm of child.parentElement.children) {
elm.classList.remove("selected");
}
child.classList.add("selected");
for (const childElm of child.parentElement.children) {
if (childElm.classList.contains("selected")) {
childElm.classList.remove("selected")
}
}
document.querySelector(`.tabs .content[data-tab="${
child.getAttribute("data-tab")
}"]`)?.classList.add("selected");
}
});
child.setAttribute("data-armed", "true")
}
}
}
if (selected > 1 || selected == 0) {
for (const child of elm.children) {
child.classList.remove("selected");
}
for (const child of elm.children) {
if (child.tagName == "BUTTON") {
child.classList.add("selected");
document.querySelector(`.tabs .content[data-tab="${
child.getAttribute("data-tab")
}"]`)?.classList.add("selected");
break;
}
}
}
}
for (const elm of document.querySelectorAll(".tabs-header")) {
let count = 0;
let selected = 0;
for (const child of elm.children[0].children) {
if (child.tagName == "BUTTON") {
count++;
if (child.classList.contains("selected")) {
selected++;
}
if (!child.getAttribute("data-armed")) {
child.addEventListener("click", () => {
if (!child.classList.contains("selected")) {
for (const elm of child.parentElement.children) {
elm.classList.remove("selected");
}
child.classList.add("selected");
for (const childElm of child.parentElement.parentElement.children) {
if (childElm.classList.contains("selected")) {
childElm.classList.remove("selected")
}
}
document.querySelector(`.tabs-header .content[data-tab="${
child.getAttribute("data-tab")
}"]`)?.classList.add("selected");
}
});
child.setAttribute("data-armed", "true")
}
}
}
if (selected > 1 || selected == 0) {
for (const child of elm.children) {
child.classList.remove("selected");
}
for (const child of elm.children[0].children) {
if (child.tagName == "BUTTON") {
child.classList.add("selected");
document.querySelector(`.tabs .content[data-tab="${
child.getAttribute("data-tab")
}"]`)?.classList.add("selected");
break;
}
}
}
}
}
function load() {
for (const elm of document.querySelectorAll("form > button")) {
elm.addEventListener('click', (e) => {
e.target.parentElement.classList.add("submitted");
});
}
for (const elm of document.querySelectorAll("button.icon")) {
elm.addEventListener('click', (e) => {
e.preventDefault()
const input = document.querySelectorAll('form .file-upload input[type="file"]')[0];
if (input) {
input.click();
input.addEventListener('change', (e) => {
const file = input.files[0];
if (!file) return;
elm.setAttribute("disabled", "true");
const spanToReplace = document.querySelector('.file-upload .icon span');
const imgToReplace = document.querySelector('.file-upload .icon img');
if (!imgToReplace || !spanToReplace) return;
if (imgToReplace.getAttribute("replace")) {
const fileReader = new FileReader();
fileReader.onloadend = () => {
imgToReplace.setAttribute("src", fileReader.result)
elm.classList.add("adapt");
}
fileReader.readAsDataURL(file)
}
if (spanToReplace.getAttribute("replace")) {
spanToReplace.innerHTML = spanToReplace.getAttribute("replace")
}
})
}
});
}
tabs();
}
window.onload = load;
htmx.on('htmx:afterSwap', load);
htmx.on('htmx:beforeSwap', (env) => {
if (env.detail.xhr.status === 401) {
window.location = "/login"
} else
// 309 is the code I selected for html to htmlfull change
if (env.detail.xhr.status === 309) {
env.detail.target = htmx.find(".app")
}
});

View File

@ -1,29 +0,0 @@
<!DOCTYPE html>
<html>
<head>
{{ block "header" . }}
<title>
{{ block "title" . }}
Ai stuff
{{ end }}
</title>
{{ end }}
<link rel="stylesheet" href="/styles/main.css" >
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.11.1/font/bootstrap-icons.css">
<link href="https://fonts.googleapis.com/css2?family=Roboto:ital,wght@0,100;0,300;0,400;0,500;0,700;0,900;1,100;1,300;1,400;1,500;1,700;1,900&display=swap" rel="stylesheet">
{{ block "other_imports" . }} {{end}}
</head>
<body>
<script src="https://unpkg.com/htmx.org@1.9.5"></script>
<script src="/js/main.js"></script>
{{ block "js_imports" . }} {{end}}
{{ block "body" . }}
{{ block "header.html" . }} {{end}}
<div class="app">
{{ block "mainbody" . }} {{end}}
</div>
{{end}}
</body>
</html>

View File

@ -1,34 +0,0 @@
{{ define "title"}} Home : AI Stuff {{ end }}
{{ define "mainbody" }}
<div class="login-page">
<div>
<h1>
Login
</h1>
<form method="post" action="/login" {{if .Submited}}class="submitted"{{end}} >
<fieldset>
<label for="email">Email</label>
<input type="email" required name="email" {{if .Email}} value="{{.Email}}" {{end}} />
</fieldset>
<fieldset>
<label for="password">Password</label>
<input required name="password" type="password" />
{{if .NoUserOrPassword}}
<span class="form-msg error">
Either the password or the email are incorrect
</span>
{{end}}
</fieldset>
<button>
Login
</button>
<div class="spacer"></div>
<a class="simple-link text-center w100 spacer" hx-get="/register" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
Register
</a>
</form>
</div>
</div>
{{ end }}

View File

@ -1,44 +0,0 @@
{{define "title"}}
Create New Model : AI Stuff
{{end}}
{{define "mainbody"}}
<main>
<h1>
Create new Model
</h1>
<form enctype="multipart/form-data" action="/models/add" method="POST" {{if .Submited}}class="submitted"{{end}} >
<fieldset>
<label for="name">Name</label>
<input id="name" name="name" required {{if .Name}} value="{{.Name}}" {{end}} />
{{if .NameFoundError}}
<span class="form-msg error">
You already have a model with that name.
</span>
{{end}}
</fieldset>
<fieldset class="file-upload" >
<label for="file">Base image</label>
<div class="form-msg">
Please provide a base image.<br/>
This image is a sample of the images that you are going to classfiy.
</div>
<div class="icon-holder">
<button class="icon">
<img replace="true" src="/imgs/upload-icon.png" />
<span replace="Image selected">
Upload image
</span>
</button>
<input id="file" name="file" type="file" required accept="image/png,image/jpeg" />
</div>
</fieldset>
<button hx-indicator="#spinner" >
Create
</button>
<button disabled id="spinner" class="htmx-indicator">
Create
</button>
</form>
</main>
{{end}}

View File

@ -1,12 +0,0 @@
{{ define "mainbody" }}
<main>
<h2 class="text-center">
Model {{ .Model.Name }} was deleted!
</h2>
<div class="description text-center">
<a hx-get="/models" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
👈 Go back
</a>
</div>
</main>
{{ end }}

View File

@ -1,556 +0,0 @@
{{ define "title" }}
Model: {{ .Model.Name }}
{{ end }}
{{ define "base-model-card" }}
<div class="card model-card">
<h1>
{{ .Model.Name }}
</h1>
<div class="second-line">
<img src="/savedData/{{ .Model.Id }}/baseimage.png" />
<div class="info">
<div>
<span class="bold bigger">Image Type:</span> {{ .Model.Color_mode }}
</div>
<div>
<span class="bold bigger">Image Size:</span> {{ .Model.Width }}x{{ .Model.Height }}
</div>
</div>
</div>
</div>
{{ end }}
{{ define "delete-model-card" }}
<form hx-delete="/models/delete" hx-headers='{"REQUEST-TYPE": "html"}' hx-swap="outerHTML" {{ if .Error }} class="submitted" {{end}} >
<fieldset>
<label for="name">
To delete this model please type "{{ .Model.Name }}":
</label>
<input name="name" id="name" required />
{{ if .NameDoesNotMatch }}
<span class="form-msg red">
Name does not match "{{ .Model.Name }}"
</span>
{{ end }}
</fieldset>
<input type="hidden" name="id" value="{{ .Model.Id }}" />
<button class="danger">
Delete
</button>
</form>
{{ end }}
{{/* Is called from a diffrent endpoint so that it does not matter where this is from :) which means that . can mean what ever I want */}}
{{ define "data-model-create-class-table-table" }}
<div>
<table>
<thead>
<tr>
<th>
File Path
</th>
<th>
Mode
</th>
<th>
<!-- Img -->
</th>
<th>
<!-- Status -->
</th>
</tr>
</thead>
<tbody>
{{range .List}}
<tr>
<td>
{{ if eq .FilePath "id://" }}
Managed
{{ else }}
{{.FilePath}}
{{ end }}
</td>
<td>
{{ if (eq .Mode 2) }}
Testing
{{ else }}
Training
{{ end }}
</td>
<td class="text-center">
{{ if startsWith .FilePath "id://" }}
<img src="/savedData/{{ $.Model.Id }}/data/{{ .Id }}.{{ $.Model.Format }}" height="30px" width="30px" style="object-fit: contain;" />
{{ else }}
TODO
img {{ .FilePath }}
{{ end }}
</td>
<td class="text-center">
{{ if eq .Status 1 }}
<span class="bi bi-check-circle-fill" style="color: green"></span>
{{ else }}
<span class="bi bi-exclamation-triangle-fill" style="color: red"></span>
{{ end }}
</td>
</tr>
{{end}}
</tbody>
</table>
<div class="flex justify-center align-center">
<div class="grow-1 flex justify-end align-center ">
{{ if gt .Page 0 }}
<button
hx-get="/models/data/list?id={{ .Id }}&page={{ add .Page -1 }}"
hx-target=".content[data-tab='{{ .Name }}']"
hx-swap="innerHTML"
hx-headers='{"REQUEST-TYPE": "html"}'
data-tab="{{ .Name }}">
Prev
</button>
{{ end }}
</div>
<div style="padding: 10px;">
{{ .Page }}
</div>
<div class="grow-1 flex justify-start align-center">
{{ if .ShowNext }}
<button
hx-get="/models/data/list?id={{ .Id }}&page={{ add .Page 1 }}"
hx-target=".content[data-tab='{{ .Name }}']"
hx-swap="innerHTML"
hx-headers='{"REQUEST-TYPE": "html"}'
data-tab="{{ .Name }}">
Next
</button>
{{ end }}
</div>
</div>
</div>
{{ end }}
{{ define "data-model-create-class-table" }}
{{ if eq (len .Classes) 0 }}
TODO CREATE TABLE
{{else}}
<div class="tabs-header">
{{/* Handle the case where there are to many buttons */}}
<div class="header">
{{ range .Classes }}
{{/* TODO Auto Load 1st */}}
<button
hx-get="/models/data/list?id={{ .Id }}"
hx-target=".content[data-tab='{{ .Name }}']"
hx-swap="innerHTML"
hx-headers='{"REQUEST-TYPE": "html"}'
hx-trigger="click"
class="tab"
data-tab="{{ .Name }}">
{{ .Name }}
</button>
{{ end }}
</div>
{{ range $i, $a := .Classes }}
{{ if eq $i 0}}
<div
hx-get="/models/data/list?id={{ .Id }}"
hx-target=".content[data-tab='{{ $a.Name }}']"
hx-swap="innerHTML"
hx-headers='{"REQUEST-TYPE": "html"}'
hx-trigger="load"
class="content"
data-tab="{{ $a.Name }}">
</div>
{{ else }}
<div
class="content"
data-tab="{{ $a.Name }}" >
</div>
{{ end }}
{{ end }}
</div>
{{end}}
{{ end }}
{{ define "data-model-card" }}
<div class="card">
<h3>
Training data
</h3>
{{ if eq (len .Classes) 0 }}
<p>
You need to upload data so the model can train.
</p>
<div class="tabs">
<button class="tab" data-tab="upload">
Upload
</button>
<button class="tab" data-tab="create_class">
Create Class
</button>
<button class="tab" data-tab="api">
Api
</button>
<div class="content" data-tab="upload">
<form hx-headers='{"REQUEST-TYPE": "htmlfull"}' enctype="multipart/form-data" hx-post="/models/data/upload">
<input type="hidden" name="id" value={{.Model.Id}} />
<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>
<div class="icon-holder">
<button class="icon">
<img replace="icon" src="/imgs/upload-icon.png" />
<span replace="File Selected">
Upload Zip File
</span>
</button>
<input id="file" name="file" type="file" required accept="application/zip" />
</div>
</fieldset>
<button hx-indicator="#upload-notifier" onClick="window.requestAnimationFrame(() => this.remove())">
Add
</button>
<button disabled id="upload-notifier" class="htmx-indicator">
Uploading
</button>
</form>
</div>
<div class="content" data-tab="create_class">
{{ template "data-model-create-class-table" . }}
</div>
<div class="content" data-tab="api">
TODO
</div>
</div>
{{ else }}
<p>
You need to upload data so the model can train.
</p>
{{ if gt .NumberOfInvalidImages 0 }}
<p class="danger">
There are images {{ .NumberOfInvalidImages }} that were loaded that do not have the correct format. These images will be delete when the model trains.
</p>
{{ end }}
<div class="tabs">
<button class="tab" data-tab="create_class">
Create Class
</button>
<button class="tab" data-tab="api">
Api
</button>
<div class="content" data-tab="create_class">
{{ template "data-model-create-class-table" . }}
</div>
<div class="content" data-tab="api">
TODO
</div>
</div>
{{ end }}
</div>
{{ end }}
{{ define "train-model-card" }}
<form hx-post="/models/train" hx-headers='{"REQUEST-TYPE": "html"}' hx-swap="outerHTML" {{ if .Error }} class="submitted" {{end}} >
{{ if .HasData }}
{{ if .NumberOfInvalidImages }}
{{ if gt .NumberOfInvalidImages 0 }}
<p class="danger">
There are images {{ .NumberOfInvalidImages }} that were loaded that do not have the correct format. These images will be delete when the model trains.
</p>
<input type="hidden" value="{{ .NumberOfInvalidImages }}" name="id" />
{{ end }}
{{ end }}
{{ if .ErrorMessage }}
<p class="danger">
{{ .ErrorMessage }}
</p>
{{ end }}
{{/* TODO expading mode */}}
<input type="hidden" value="{{ .Model.Id }}" name="id" />
<fieldset>
<legend>
Model Type
</legend>
<div class="input-radial">
<input id="model_type_simple" value="simple" name="model_type" type="radio" checked />
<label for="model_type_simple">Simple</label><br/>
<input id="model_type_expandable" value="expandable" name="model_type" type="radio" />
<label for="model_type_expandable">Expandable</label>
</div>
</fieldset>
{{/* TODO allow more models to be created */}}
<fieldset>
<label for="number_of_models">Number of Models</label>
<input id="number_of_models" type="number" name="number_of_models" value="1" />
</fieldset>
{{/* TODO to Change the acc */}}
<fieldset>
<label for="accuracy">Target accuracy</label>
<input id="accuracy" type="number" name="accuracy" value="95" />
</fieldset>
{{/* TODO allow to chose the base of the model */}}
{{/* TODO allow to change the shape of the model */}}
<button>
Train
</button>
{{ else }}
<h2>
To train the model please provide data to the model first
</h2>
{{ end }}
</form>
{{ end }}
{{ define "run-model-card" }}
<form hx-headers='{"REQUEST-TYPE": "html"}' enctype="multipart/form-data" hx-post="/models/run" hx-swap="outerHTML">
<input type="hidden" name="id" value={{.Model.Id}} />
<fieldset class="file-upload" >
<label for="file">Image</label>
<div class="form-msg">
Run image through them model and get the result
</div>
<div class="icon-holder">
<button class="icon">
<img replace="icon" src="/imgs/upload-icon.png" />
<span replace="File Selected">
Image File
</span>
</button>
{{ if .ImageError }}
<span class="form-msg error">
The provided image was not valid for this model
</span>
{{ end }}
<input id="file" name="file" type="file" required accept="image/png" />
</div>
</fieldset>
<button>
Run
</button>
{{ if .NotFound }}
<div class="result">
<h1>
The class was not found
</h1>
</div>
{{ else if .Result }}
<div>
<h1>
Result
</h1>
The image was classified as {{.Result}}
</div>
{{ end }}
</form>
{{ end }}
{{ define "mainbody" }}
<main>
{{ if (eq .Model.Status 1) }}
<div>
<h1 class="text-center">
{{ .Model.Name }}
</h1>
<!-- TODO add cool animation -->
<h2 class="text-center">
Preparing the model
</h2>
</div>
{{ else if (eq .Model.Status -1) }}
<div>
<h1 class="text-center">
{{ .Model.Name }}
</h1>
<!-- TODO improve message -->
<h2 class="text-center">
Failed to prepare model
</h2>
<form hx-delete="/models/delete">
<input type="hidden" name="id" value="{{ .Model.Id }}" />
<button class="danger">
Delete
</button>
</form>
</div>
{{/* PRE TRAINING STATUS */}}
{{ else if (eq .Model.Status 2) }}
{{ template "base-model-card" . }}
{{ template "data-model-card" . }}
{{ template "train-model-card" . }}
{{ template "delete-model-card" . }}
{{/* FAILED TO PROCCESS THE ZIPFILE */}}
{{ else if (eq .Model.Status -2)}}
{{ template "base-model-card" . }}
<form hx-delete="/models/data/delete-zip-file" hx-headers='{"REQUEST-TYPE": "html"}' hx-swap="outerHTML">
Failed to proccess the zip file.<br/>
Delete file and proccess again.<br/>
<br/>
<div class="spacer" ></div>
<input type="hidden" name="id" value="{{ .Model.Id }}" />
<button class="danger">
Delete Zip File
</button>
</form>
{{ template "delete-model-card" . }}
{{/* PROCCESS THE ZIPFILE */}}
{{ else if (eq .Model.Status 3)}}
{{ template "base-model-card" . }}
<div class="card" hx-get="/models/edit?id={{ .Model.Id }}" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push="true" hx-swap="outerHTML" hx-target=".app" hx-trigger="load delay:2s" >
{{/* TODO improve this */}}
Processing zip file...
</div>
{{/* FAILED TO Prepare for training */}}
{{ else if or (eq .Model.Status -3) (eq .Model.Status -4)}}
{{ template "base-model-card" . }}
<form hx-delete="/models/train/reset" hx-headers='{"REQUEST-TYPE": "html"}' hx-swap="outerHTML">
Failed Prepare for training.<br/>
<div class="spacer" ></div>
<input type="hidden" name="id" value="{{ .Model.Id }}" />
<button class="danger">
Try Again
</button>
</form>
{{ template "delete-model-card" . }}
{{ else if (eq .Model.Status 4)}}
{{ template "base-model-card" . }}
<div class="card" hx-get="/models/edit?id={{ .Model.Id }}" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push="true" hx-swap="outerHTML" hx-target=".app" hx-trigger="load delay:2s" >
{{/* TODO improve this */}}
Training the model...<br/>
{{/* TODO Add progress status on definitions */}}
<table>
<thead>
<tr>
<th>
Done Progress
</th>
<th>
Training Round Progress
</th>
<th>
Accuracy
</th>
<th>
Status
</th>
</tr>
</thead>
<tbody>
{{ range .Defs}}
<tr>
<td>
{{.Epoch}}
</td>
<td>
{{.EpochProgress}}/20
</td>
<td>
{{.Accuracy}}%
</td>
<td style="text-align: center;">
{{ if (eq .Status 2) }}
<span class="bi bi-book" style="color: green;"></span>
{{ else if (eq .Status 3) }}
<span class="bi bi-book-half" style="color: green;"></span>
{{ else if (eq .Status 6) }}
<span class="bi bi-book-half" style="color: orange;"></span>
{{ else if (eq .Status -3) }}
<span class="bi bi-book-half" style="color: red;"></span>
{{ else }}
{{.Status}}
{{ end }}
</td>
</tr>
{{ if (eq .Status 3) }}
<tr>
<td colspan="4">
<svg viewBox="0 200 1000 600">
{{ range $i, $layer := $.Layers }}
{{ if (eq $layer.LayerType 1)}}
<polygon
points="50,450 200,250 200,550 50,750"
stroke="black"
stroke-width="2"
fill="green"></polygon>
{{ else if (eq $layer.LayerType 4)}}
<polygon
points="{{add 50 (mul $i $.SepMod) }},450 {{ add 200 (mul $i $.SepMod) }},250 {{add 200 (mul $i $.SepMod)}},550 {{ add 50 (mul $i $.SepMod) }},750"
stroke="black"
stroke-width="2"
fill="orange">
</polygon>
{{ else if (eq $layer.LayerType 3)}}
<polygon
points="{{add 50 (mul $i $.SepMod) }},450 {{ add 200 (mul $i $.SepMod) }},250 {{add 200 (mul $i $.SepMod)}},550 {{ add 50 (mul $i $.SepMod) }},750"
stroke="black"
stroke-width="2"
fill="red">
</polygon>
{{ else if (eq $layer.LayerType 2)}}
<polygon
points="{{add 50 (mul $i $.SepMod) }},550 {{ add 200 (mul $i $.SepMod) }},350 {{add 200 (mul $i $.SepMod)}},450 {{ add 50 (mul $i $.SepMod) }},650"
stroke="black"
stroke-width="2"
fill="blue">
</polygon>
{{ else }}
<div>
{{ .LayerType }}
{{ .Shape }}
</div>
{{ end }}
{{ end }}
</svg>
</td>
</tr>
{{ end }}
{{ end }}
</tbody>
</table>
{{/* TODO Add ability to stop training */}}
</div>
{{/* Model Ready */}}
{{ else if (eq .Model.Status 5)}}
{{ template "base-model-card" . }}
{{ template "run-model-card" . }}
{{ template "delete-model-card" . }}
{{ else }}
<h1>
Unknown Status of the model.
</h1>
{{ end }}
</main>
{{ end }}

View File

@ -1,52 +0,0 @@
{{define "title"}}
Models : AI Stuff
{{end}}
{{define "mainbody"}}
<main>
{{ if (lt 0 (len .List)) }}
<div class="list-header">
<h2>My Models</h2>
<div class="expand"></div>
<a class="button" hx-get="/models/add" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
New
</a>
</div>
<table>
<thead>
<tr>
<th>
Name
</th>
<th>
<!-- Open Button -->
</th>
</tr>
</thead>
<tbody>
{{range .List}}
<tr>
<td>
{{.Name}}
</td>
<td class="text-center">
<a class="button simple" hx-get="/models/edit?id={{.Id}}" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
Edit
</a>
</td>
</tr>
{{end}}
</tbody>
</table>
{{else}}
<h2 class="text-center">
You don't have any models
</h2>
<div class="text-center">
<a class="button padded" hx-get="/models/add" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
Create a new model
</a>
</div>
{{end}}
</main>
{{end}}

View File

@ -1,33 +0,0 @@
<nav>
<ul>
<li>
<a hx-get="/" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
Index
</a>
</li>
{{ if .Context.User }}
<li>
<a hx-get="/models" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
Models
</a>
</li>
{{end}}
<li class="expand"></li>
{{ if .Context.User }}
<li>
<a hx-get="/user/info" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
User Info
</a>
<a hx-get="/logout" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
Logout
</a>
</li>
{{else}}
<li>
<a hx-get="/login" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
Login
</a>
</li>
{{end}}
</ul>
</nav>

View File

@ -1,49 +0,0 @@
{{define "title"}}
Register : AI Stuff
{{end}}
{{define "mainbody"}}
<div class="login-page">
<div>
<h1>
Register
</h1>
<form method="post" action="/register" {{if .Submited}}class="submitted"{{end}} >
<fieldset>
<label for="username">Username</label>
<input required name="username" value="{{.Username}}" />
{{if .UserError}}
<span class="form-msg error">
Username already in use
</span>
{{end}}
</fieldset>
<fieldset>
<label for="email">Email</label>
<input type="email" required name="email" value="{{.Email}}" />
{{if .EmailError}}
<span class="form-msg error">
Email already in use
</span>
{{end}}
</fieldset>
<fieldset>
<label for="password">Password</label>
<input required name="password" type="password" />
{{if .PasswordToLong}}
<span class="form-msg error">
Password is to long
</span>
{{end}}
</fieldset>
<button>
Register
</button>
<div class="spacer"></div>
<a class="simple-link text-center w100" hx-get="/login" hx-headers='{"REQUEST-TYPE": "htmlfull"}' hx-push-url="true" hx-swap="outerHTML" hx-target=".app">
Login
</a>
</form>
</div>
</div>
{{end}}

View File

@ -1,424 +0,0 @@
*{box-sizing: border-box;font-family: 'Roboto', sans-serif;}
:root {
--white: #ffffff;
--grey: #dbdcde;
--light-grey: #fafafa;
--main: #fca311;
--sec: #14213d;
--black: #000000;
--red: 212, 38, 38;
--green: 92, 199, 30;
}
body {
margin: 0;
padding: 0;
}
main {
padding: 20px 15vw;
}
.flex {
display: flex;
}
.justify-center {
justify-content: center;
}
.justify-start {
justify-content: start;
}
.justify-end {
justify-content: end;
}
.align-center {
align-items: center;
}
.grow-1 {
flex-grow: 1;
}
.w100 {
width: 100%;
display: block;
}
.text-center {
text-align: center;
}
.simple-link {
color: var(--sec);
text-decoration: none;
}
.bold {
font-weight: bold;
}
.bigger {
font-size: 1.1rem;
}
.danger {
color: red;
}
/* Generic */
.button,
button {
border-radius: 10px;
text-align: center;
padding: 3px 6px;
border: none;
box-shadow: 0 2px 8px 1px #66666655;
background: var(--main);
color: var(--black);
}
.button.padded,
button.padded {
padding: 10px;
}
.button.danger,
button.danger {
background: rgb(var(--red));
color: white;
font-weight: bold;
}
/* Nav bar */
nav {
background: #ececec;
margin: 0;
box-shadow: 0 0 8px 1px #888888ef;
height: 60px;
}
nav ul {
display: flex;
margin: 0;
padding: 20px 40px;
}
nav ul li {
list-style: none;
padding-left: 10px;
}
nav ul li:first-child {
padding: 0;
}
nav ul .expand {
flex-grow: 1
}
nav ul li a {
text-decoration: none;
color: black;
}
/* 404 page */
.page404 {
display: grid;
place-items: center;
height: calc(100vh - 60px);
text-align: center;
}
.page404 h1 {
font-size: 10em;
margin: 0;
}
.page404 h2 {
font-size: 5em;
margin: 0;
margin-bottom: 0.3em;
}
.page404 div.description {
font-size: 1.5em;
}
/* Login Page */
.login-page {
display: grid;
place-items: center;
margin-bottom: 40px;
}
.login-page > div {
width: 40vw;
}
.login-page h1 {
text-align: center;
}
/* forms */
a {
cursor: pointer;
}
form {
padding: 30px;
margin: 20px 0;
border-radius: 10px;
box-shadow: 2px 5px 8px 2px #66666655;
}
.card form {
padding: 0;
border-radius: none;
box-shadow: none;
}
form label,
form fieldset legend {
display: block;
padding-bottom: 5px;
font-size: 1.2rem;
}
form input {
border: none;
box-shadow: 0 2px 5px 1px #66666655;
border-radius: 5px;
padding: 10px;
width: 100%;
}
form input:invalid:focus,
form.submitted input:invalid {
box-shadow: 0 2px 5px 1px rgba(var(--red), 0.2);
}
form.submitted input:valid {
box-shadow: 0 2px 5px 1px rgba(var(--green), 0.2);
}
form .spacer {
padding-bottom: 10px;
}
form fieldset {
padding-bottom: 15px;
border: none;
}
form fieldset .form-msg {
font-size: 0.9rem;
}
form fieldset .error {
color: rgb(var(--red))
}
form button {
font-size: 1.2rem;
margin-left: 50%;
width: 50%;
transform: translateX(-50%);
padding: 10px;
}
form .input-radial input[type="radio"] {
width: auto;
box-shadow: none;
}
form .input-radial label {
display: inline;
font-size: 1rem;
}
/* Upload files */
form fieldset.file-upload input[type="file"] {
height: 1px;
width: 1px;
padding: 0;
box-shadow: none;
}
form fieldset.file-upload .icon-holder {
padding: 10px;
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
font-size: 1rem;
}
form fieldset.file-upload .icon-holder .icon {
height: 150px;
width: 150px;
padding: 20px;
border-radius: 10px;
background: none;
transform: none;
margin: 0;
font-size: 1rem;
transition: all 1s;
}
form fieldset.file-upload .icon-holder .icon.adapt {
width: auto;
height: auto;
max-width: 80%;
max-height: 80%;
min-height: 150px;
min-width: 150px;
padding: 20px;
}
form fieldset.file-upload:has(input[type="file"]:invalid:focus) .icon,
form.submitted fieldset.file-upload:has(input[type="file"]:invalid:focus) .icon {
box-shadow: 0 2px 5px 1px rgba(var(--red), 0.2);
}
form.submitted fieldset.file-upload:has(input[type="file"]:valid:focus) .icon{
box-shadow: 0 2px 5px 1px rgba(var(--green), 0.2);
}
form fieldset.file-upload .icon-holder .icon img {
display: block;
width: 100%;
height: 80%;
object-fit: contain;
text-align: center;
transition: all 1s;
}
form fieldset.file-upload .icon-holder .icon span {
display: block;
width: 100%;
padding-top: 10px;
text-align: center;
}
/* Lists */
.list-header {
display: flex;
padding-bottom: 10px;
}
.list-header h2 {
margin: 0;
padding: 10px 5px;
}
.list-header .expand {
flex-grow: 1;
}
.list-header .button,
.list-header button {
padding: 10px 10px;
height: calc(100% - 20px);
margin-top: 5px;
}
/* Table */
table {
width: 100%;
box-shadow: 0 2px 8px 1px #66666622;
border-radius: 10px;
border-collapse: collapse;
overflow: hidden;
}
table thead {
background: #60606022;
}
table tr td,
table tr th {
border-left: 1px solid #22222244;
padding: 15px;
}
table tr td:first-child,
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;
}
.card {
box-shadow: 0 2px 5px 1px #66666655;
padding: 20px;
border-radius: 10px;
margin: 20px 0;
}
.card h3 {
margin-top: 0;
}
/* Model stuff */
.model-card h1 {
margin: 0;
padding-bottom: 10px;
}
.model-card img {
width: 25%;
height: 100%;
object-fit: contain;
}
.model-card .second-line {
display: flex;
gap: 20px;
}
/* Tabs code */
.tabs {
border-radius: 5px;
overflow: hidden;
display: flex;
flex-wrap: wrap;
box-shadow: 0 2px 8px 1px #66666655;
gap: 0 5px;
}
.tabs-header .header {
display: flex;
overflow-x: scroll;
}
.tabs .content {
display: none;
padding: 5px;
width: 100%;
}
.tabs .tab {
padding: 5px;
background: var(--light-grey);
border-radius: 5px 5px 0 0;
box-shadow: none;
font-size: 1.1rem;
}
.tabs .tab.selected {
box-shadow: inset 0 2px 8px 1px #66666655;
}
.tabs .content.selected {
display: block;
box-shadow: 0 2px 2px 1px #66666655;
}

View File

@ -1,50 +0,0 @@
{{ define "title" }}
User Info
{{ end }}
{{define "mainbody"}}
<div class="login-page">
<div>
<h1>
User Infomation
</h1>
<form method="post" action="/user/info/email" {{if .Submited}}class="submitted"{{end}} >
<fieldset>
<label for="email">Email</label>
<input type="email" required name="email" {{if .Email}} value="{{.Email}}" {{end}} />
</fieldset>
<button>
Update
</button>
</form>
<form method="post" action="/user/info/password" {{if .Submited}}class="submitted"{{end}} >
<fieldset>
<label for="old_password">Old Password</label>
<input required name="old_password" type="password" />
{{if .NoUserOrPassword}}
<span class="form-msg error">
Either the password is incorrect
</span>
{{end}}
</fieldset>
<fieldset>
<label for="password">New Password</label>
<input required name="password" type="password" />
</fieldset>
<fieldset>
<label for="password2">Repeat New Password</label>
<input required name="password2" type="password" />
{{if .PasswordNotTheSame}}
<span class="form-msg error">
Either the passwords are not the same
</span>
{{end}}
</fieldset>
<button>
Update
</button>
</form>
</div>
</div>
{{end}}