feat: removed non svelte front page
This commit is contained in:
parent
0d37ba8d59
commit
274d7d22aa
@ -86,14 +86,12 @@ func loadBaseImage(c *Context, id string) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleAdd(handle *Handle) {
|
func handleAdd(handle *Handle) {
|
||||||
handle.GetHTML("/models/add", AnswerTemplate("models/add.html", nil, 1))
|
handle.Post("/models/add", func(c *Context) *Error {
|
||||||
handle.Post("/models/add", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
if !c.CheckAuthLevel(1) {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Mode == JSON {
|
read_form, err := c.R.MultipartReader()
|
||||||
read_form, err := r.MultipartReader()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JsonErrorBadRequest(err, "Please provide a valid multipart Reader request")
|
return c.JsonErrorBadRequest(err, "Please provide a valid multipart Reader request")
|
||||||
}
|
}
|
||||||
@ -164,93 +162,5 @@ func handleAdd(handle *Handle) {
|
|||||||
go loadBaseImage(c, id)
|
go loadBaseImage(c, id)
|
||||||
|
|
||||||
return c.SendJSON(id)
|
return c.SendJSON(id)
|
||||||
}
|
|
||||||
|
|
||||||
read_form, err := r.MultipartReader()
|
|
||||||
if err != nil {
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(nil))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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() {
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "models/add.html", c.AddMap(AnyMap{
|
|
||||||
"NameFoundError": true,
|
|
||||||
"Name": name,
|
|
||||||
}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = handle.Db.Exec("insert into models (user_id, name) values ($1, $2)", c.User.Id, name)
|
|
||||||
if err != nil {
|
|
||||||
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 {
|
|
||||||
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)
|
|
||||||
|
|
||||||
Redirect("/models/edit?id="+id, c.Mode, w, r)
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,23 +1,26 @@
|
|||||||
package model_classes
|
package model_classes
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
|
|
||||||
"git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
"git.andr3h3nriqu3s.com/andr3/fyp/logic/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 {
|
func HandleList(handle *Handle) {
|
||||||
id, err := GetIdFromUrl(r, "id")
|
handle.Get("/models/data/list", func(c *Context) *Error {
|
||||||
|
if !c.CheckAuthLevel(1) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
id, err := GetIdFromUrl(c, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JsonBadRequest("Model Class not found!")
|
return c.JsonBadRequest("Model Class not found!")
|
||||||
}
|
}
|
||||||
|
|
||||||
page := 0
|
page := 0
|
||||||
if r.URL.Query().Has("page") {
|
if c.R.URL.Query().Has("page") {
|
||||||
page_url := r.URL.Query().Get("page")
|
page_url := c.R.URL.Query().Get("page")
|
||||||
page_url_number, err := strconv.Atoi(page_url)
|
page_url_number, err := strconv.Atoi(page_url)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JsonBadRequest("Page is not a number")
|
return c.JsonBadRequest("Page is not a number")
|
||||||
@ -62,87 +65,5 @@ func models_data_list_json(w http.ResponseWriter, r *http.Request, c *Context) *
|
|||||||
Page: page,
|
Page: page,
|
||||||
ShowNext: len(rows) == 11,
|
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) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if c.Mode == JSON {
|
|
||||||
return models_data_list_json(w, r, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := GetIdFromUrl(r, "id")
|
|
||||||
if err != nil {
|
|
||||||
return ErrorCode(err, 400, c.AddMap(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
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 ErrorCode(err, http.StatusBadRequest, c.AddMap(nil))
|
|
||||||
}
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
name := ""
|
|
||||||
model_id := ""
|
|
||||||
if err = class_rows.Scan(&name, &model_id); err != nil {
|
|
||||||
return Error500(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
|
||||||
|
|
||||||
max_len := min(11, len(got))
|
|
||||||
|
|
||||||
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
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -268,13 +268,12 @@ func processZipFileExpand(c *Context, model *BaseModel) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleDataUpload(handle *Handle) {
|
func handleDataUpload(handle *Handle) {
|
||||||
handle.Post("/models/data/upload", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Post("/models/data/upload", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Mode == JSON {
|
read_form, err := c.R.MultipartReader()
|
||||||
read_form, err := r.MultipartReader()
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JsonBadRequest("Please provide a valid form data request!")
|
return c.JsonBadRequest("Please provide a valid form data request!")
|
||||||
}
|
}
|
||||||
@ -305,7 +304,7 @@ func handleDataUpload(handle *Handle) {
|
|||||||
if err == ModelNotFoundError {
|
if err == ModelNotFoundError {
|
||||||
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO mk this path configurable
|
// TODO mk this path configurable
|
||||||
@ -313,7 +312,7 @@ func handleDataUpload(handle *Handle) {
|
|||||||
|
|
||||||
f, err := os.Create(path.Join(dir_path, "base_data.zip"))
|
f, err := os.Create(path.Join(dir_path, "base_data.zip"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
@ -324,74 +323,17 @@ func handleDataUpload(handle *Handle) {
|
|||||||
go processZipFile(c, model)
|
go processZipFile(c, model)
|
||||||
|
|
||||||
return c.SendJSON(model.Id)
|
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
|
// ------ CLASS DATA UPLOAD
|
||||||
// ------
|
// ------
|
||||||
handle.PostJSON("/models/data/class/upload", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Post("/models/data/class/upload", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
read_form, err := r.MultipartReader()
|
read_form, err := c.R.MultipartReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.JsonBadRequest("Please provide a valid form data request!")
|
return c.JsonBadRequest("Please provide a valid form data request!")
|
||||||
}
|
}
|
||||||
@ -424,7 +366,7 @@ func handleDataUpload(handle *Handle) {
|
|||||||
if err == ModelNotFoundError {
|
if err == ModelNotFoundError {
|
||||||
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
||||||
} else if err != nil {
|
} 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
|
// TODO work in allowing the model to add new in the pre ready moment
|
||||||
@ -437,7 +379,7 @@ func handleDataUpload(handle *Handle) {
|
|||||||
|
|
||||||
f, err := os.Create(path.Join(dir_path, "expand_data.zip"))
|
f, err := os.Create(path.Join(dir_path, "expand_data.zip"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
defer f.Close()
|
defer f.Close()
|
||||||
|
|
||||||
@ -450,11 +392,10 @@ func handleDataUpload(handle *Handle) {
|
|||||||
return c.SendJSON(model.Id)
|
return c.SendJSON(model.Id)
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.Delete("/models/data/delete-zip-file", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Delete("/models/data/delete-zip-file", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode == JSON {
|
|
||||||
|
|
||||||
type ModelData struct {
|
type ModelData struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
@ -462,7 +403,7 @@ func handleDataUpload(handle *Handle) {
|
|||||||
|
|
||||||
var dat ModelData
|
var dat ModelData
|
||||||
|
|
||||||
if err := c.ToJSON(r, &dat); err != nil {
|
if err := c.ToJSON(&dat); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -470,7 +411,7 @@ func handleDataUpload(handle *Handle) {
|
|||||||
if err == ModelNotFoundError {
|
if err == ModelNotFoundError {
|
||||||
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
delete_path := "base_data.zip"
|
delete_path := "base_data.zip"
|
||||||
@ -483,13 +424,13 @@ func handleDataUpload(handle *Handle) {
|
|||||||
|
|
||||||
err = os.Remove(path.Join("savedData", model.Id, delete_path))
|
err = os.Remove(path.Join("savedData", model.Id, delete_path))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
if model.Status != READY_FAILED {
|
if model.Status != READY_FAILED {
|
||||||
err = os.RemoveAll(path.Join("savedData", model.Id, "data"))
|
err = os.RemoveAll(path.Join("savedData", model.Id, "data"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
c.Logger.Warn("Handle failed to remove the savedData when deleteing the zip file while expanding")
|
c.Logger.Warn("Handle failed to remove the savedData when deleteing the zip file while expanding")
|
||||||
@ -498,12 +439,12 @@ func handleDataUpload(handle *Handle) {
|
|||||||
if model.Status != READY_FAILED {
|
if model.Status != READY_FAILED {
|
||||||
_, err = handle.Db.Exec("delete from model_classes where model_id=$1;", model.Id)
|
_, err = handle.Db.Exec("delete from model_classes where model_id=$1;", model.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
_, err = handle.Db.Exec("delete from model_classes where model_id=$1 and status=$2;", model.Id, MODEL_CLASS_STATUS_TO_TRAIN)
|
_, 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 {
|
if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -514,51 +455,5 @@ func handleDataUpload(handle *Handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return c.SendJSON(model.Id)
|
return c.SendJSON(model.Id)
|
||||||
}
|
|
||||||
|
|
||||||
f, err := MyParseForm(r)
|
|
||||||
if err != nil {
|
|
||||||
return ErrorCode(err, 400, c.AddMap(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
if !CheckId(f, "id") {
|
|
||||||
return ErrorCode(err, 400, c.AddMap(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
id := f.Get("id")
|
|
||||||
|
|
||||||
model, err := GetBaseModel(handle.Db, id)
|
|
||||||
if err == ModelNotFoundError {
|
|
||||||
return ErrorCode(nil, http.StatusNotFound, AnyMap{
|
|
||||||
"NotFoundMessage": "Model not found",
|
|
||||||
"GoBackLink": "/models",
|
|
||||||
})
|
|
||||||
} else if err != nil {
|
|
||||||
return Error500(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if model.Status != FAILED_PREPARING_ZIP_FILE {
|
|
||||||
// TODO add message
|
|
||||||
return ErrorCode(nil, 400, c.AddMap(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.Remove(path.Join("savedData", id, "base_data.zip"))
|
|
||||||
if err != nil {
|
|
||||||
return Error500(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
err = os.RemoveAll(path.Join("savedData", id, "data"))
|
|
||||||
if err != nil {
|
|
||||||
return Error500(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = handle.Db.Exec("delete from model_classes where model_id=$1;", id)
|
|
||||||
if err != nil {
|
|
||||||
return Error500(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
ModelUpdateStatus(c, id, CONFIRM_PRE_TRAINING)
|
|
||||||
Redirect("/models/edit?id="+id, c.Mode, w, r)
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -4,40 +4,12 @@ import (
|
|||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strconv"
|
|
||||||
|
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||||
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 {
|
func deleteModelJSON(c *Context, id string) *Error {
|
||||||
c.Logger.Warnf("Removing model with id: %s", id)
|
c.Logger.Warnf("Removing model with id: %s", id)
|
||||||
_, err := c.Db.Exec("delete from models where id=$1;", id)
|
_, err := c.Db.Exec("delete from models where id=$1;", id)
|
||||||
@ -56,16 +28,16 @@ func deleteModelJSON(c *Context, id string) *Error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleDelete(handle *Handle) {
|
func handleDelete(handle *Handle) {
|
||||||
handle.Delete("/models/delete", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Delete("/models/delete", func(c *Context) *Error {
|
||||||
|
if c.CheckAuthLevel(1) {
|
||||||
if c.Mode == JSON {
|
return nil
|
||||||
|
}
|
||||||
var dat struct {
|
var dat struct {
|
||||||
Id string `json:"id" validate:"required"`
|
Id string `json:"id" validate:"required"`
|
||||||
Name *string `json:"name,omitempty"`
|
Name *string `json:"name,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err_ := c.ToJSON(r, &dat); err_ != nil {
|
if err_ := c.ToJSON(&dat); err_ != nil {
|
||||||
return err_
|
return err_
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,79 +80,5 @@ func handleDelete(handle *Handle) {
|
|||||||
c.Logger.Warn("Do not know how to handle model in status", "status", model.Status)
|
c.Logger.Warn("Do not know how to handle model in status", "status", model.Status)
|
||||||
return c.JsonBadRequest("Model in invalid status")
|
return c.JsonBadRequest("Model in invalid status")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
// This is required to parse delete forms with bodies
|
|
||||||
f, err := MyParseForm(r)
|
|
||||||
if err != nil {
|
|
||||||
return c.ErrorCode(err, 400, nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
if !CheckId(f, "id") {
|
|
||||||
return c.ErrorCode(nil, http.StatusNotFound, AnyMap{
|
|
||||||
"NotFoundMessage": "Model not found",
|
|
||||||
"GoBackLink": "/models",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
|
||||||
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:
|
|
||||||
deleteModel(handle, id, w, c, model)
|
|
||||||
return nil
|
|
||||||
|
|
||||||
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,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteModel(handle, id, w, c, model)
|
|
||||||
return nil
|
|
||||||
default:
|
|
||||||
panic("Do not know how to handle model in status:" + strconv.Itoa(model.Status))
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
|
||||||
|
|
||||||
model_classes "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/classes"
|
model_classes "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/classes"
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
|
||||||
@ -10,64 +9,15 @@ import (
|
|||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
. "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) {
|
func handleEdit(handle *Handle) {
|
||||||
handle.Get("/models/edit/classes", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Get("/models/edit/classes", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode != JSON {
|
|
||||||
return c.ErrorCode(nil, 400, AnyMap{})
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := GetIdFromUrl(r, "id")
|
model, err_ := c.GetModelFromId("id")
|
||||||
if err != nil {
|
if err_ != nil {
|
||||||
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
return err_
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wrong_number, err := model_classes.GetNumberOfWrongDataPoints(c.Db, model.Id)
|
wrong_number, err := model_classes.GetNumberOfWrongDataPoints(c.Db, model.Id)
|
||||||
@ -75,14 +25,14 @@ func handleEdit(handle *Handle) {
|
|||||||
return c.Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cls, err := model_classes.ListClasses(c, id)
|
cls, err := model_classes.ListClasses(c, model.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return c.Error500(err)
|
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 {
|
if err != nil {
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
type ReturnType struct {
|
type ReturnType struct {
|
||||||
@ -98,24 +48,14 @@ func handleEdit(handle *Handle) {
|
|||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.Get("/models/edit/definitions", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Get("/models/edit/definitions", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode != JSON {
|
|
||||||
return c.ErrorCode(nil, 400, AnyMap{})
|
|
||||||
}
|
|
||||||
|
|
||||||
id, err := GetIdFromUrl(r, "id")
|
model, err_ := c.GetModelFromId("id")
|
||||||
if err != nil {
|
if err_ != nil {
|
||||||
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
return err_
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type defrow struct {
|
type defrow struct {
|
||||||
@ -243,217 +183,39 @@ func handleEdit(handle *Handle) {
|
|||||||
return c.SendJSON(defsToReturn)
|
return c.SendJSON(defsToReturn)
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.Get("/models/edit", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Get("/models/edit", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Mode == JSON {
|
if !c.CheckAuthLevel(1) {
|
||||||
return handleJson(w, r, c)
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
id, err := GetIdFromUrl(r, "id")
|
id, err := GetIdFromUrl(c, "id")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return ErrorCode(nil, http.StatusNotFound, AnyMap{
|
return c.JsonBadRequest("Model not found")
|
||||||
"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",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type rowmodel struct {
|
type rowmodel struct {
|
||||||
Name string
|
Name string `json:"name"`
|
||||||
Status int
|
Status int `json:"status"`
|
||||||
Id string
|
Id string `json:"id"`
|
||||||
Width *int
|
Width *int `json:"width"`
|
||||||
Height *int
|
Height *int `json:"height"`
|
||||||
Color_mode *string
|
Color_mode *string `json:"color_mode"`
|
||||||
Format string
|
Format string `json:"format"`
|
||||||
Type int
|
Model_type int `json:"model_type"`
|
||||||
}
|
}
|
||||||
|
|
||||||
var model rowmodel = rowmodel{}
|
var model rowmodel = rowmodel{}
|
||||||
model.Id = id
|
err = utils.GetDBOnce(c, &model, "models where id=$1 and user_id=$2", id, c.User.Id)
|
||||||
|
if err == NotFoundError {
|
||||||
err = rows.Scan(&model.Name, &model.Status, &model.Width, &model.Height, &model.Color_mode, &model.Format, &model.Type)
|
return c.SendJSONStatus(404, "Model not found")
|
||||||
if err != nil {
|
} else if err != nil {
|
||||||
return 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)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
cls, err := model_classes.ListClasses(c, id)
|
return c.SendJSON(model)
|
||||||
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
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
|
||||||
|
@ -1,66 +1,26 @@
|
|||||||
package models
|
package models
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
|
|
||||||
"git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
"git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||||
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
|
||||||
)
|
)
|
||||||
|
|
||||||
func handleList(handle *Handle) {
|
func handleList(handle *Handle) {
|
||||||
handle.Get("/models", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Get("/models", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
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 {
|
type Row struct {
|
||||||
Name string `json:"name"`
|
Name string `json:"name"`
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
got, err := utils.GetDbMultitple[Row](c, "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 {
|
if err != nil {
|
||||||
c.Logger.Warn("HERE 6")
|
|
||||||
return c.Error500(nil)
|
return c.Error500(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.SendJSON(got)
|
return c.SendJSON(got)
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
Id string
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -2,9 +2,7 @@ package models
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"fmt"
|
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
@ -118,16 +116,14 @@ func runModelExp(c *Context, model *BaseModel, def_id string, inputImage *tf.Ten
|
|||||||
}
|
}
|
||||||
|
|
||||||
func handleRun(handle *Handle) {
|
func handleRun(handle *Handle) {
|
||||||
handle.Post("/models/run", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Post("/models/run", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode == JSON {
|
|
||||||
|
|
||||||
read_form, err := r.MultipartReader()
|
read_form, err := c.R.MultipartReader()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
// TODO improve message
|
return c.JsonBadRequest("Invalid muilpart body")
|
||||||
return ErrorCode(nil, 400, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var id string
|
var id string
|
||||||
@ -249,146 +245,5 @@ func handleRun(handle *Handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return c.SendJSON(returnValue)
|
return c.SendJSON(returnValue)
|
||||||
}
|
|
||||||
|
|
||||||
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 &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 ErrorCode(nil, http.StatusNotFound, AnyMap{
|
|
||||||
"NotFoundMessage": "Model not found",
|
|
||||||
"GoBackLink": "/models",
|
|
||||||
})
|
|
||||||
} else if err != nil {
|
|
||||||
return Error500(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if model.Status != READY {
|
|
||||||
// TODO improve this
|
|
||||||
return ErrorCode(nil, 400, c.AddMap(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
} else if err != nil {
|
|
||||||
return 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 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
|
|
||||||
}
|
|
||||||
|
|
||||||
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 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 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
|
|
||||||
}
|
|
||||||
|
|
||||||
var name string
|
|
||||||
if err = rows.Scan(&name); err != nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadDefineTemplate(w, "/models/edit.html", "run-model-card", c.AddMap(AnyMap{
|
|
||||||
"Model": model,
|
|
||||||
"Result": name,
|
|
||||||
}))
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,4 @@ import (
|
|||||||
func HandleTrainEndpoints(handle *Handle) {
|
func HandleTrainEndpoints(handle *Handle) {
|
||||||
handleTrain(handle)
|
handleTrain(handle)
|
||||||
handleRest(handle)
|
handleRest(handle)
|
||||||
|
|
||||||
//TODO remove
|
|
||||||
handleTest(handle)
|
|
||||||
}
|
}
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
package models_train
|
package models_train
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
|
|
||||||
@ -10,22 +9,21 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
func handleRest(handle *Handle) {
|
func handleRest(handle *Handle) {
|
||||||
handle.Delete("/models/train/reset", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Delete("/models/train/reset", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode == JSON {
|
|
||||||
var dat struct {
|
var dat struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ToJSON(r, &dat); err != nil {
|
if err := c.ToJSON(&dat); err != nil {
|
||||||
return err;
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
model, err := GetBaseModel(c.Db, dat.Id)
|
model, err := GetBaseModel(c.Db, dat.Id)
|
||||||
if err == ModelNotFoundError {
|
if err == ModelNotFoundError {
|
||||||
return c.JsonBadRequest("Model not found");
|
return c.JsonBadRequest("Model not found")
|
||||||
} else if err != nil {
|
} else if err != nil {
|
||||||
// TODO improve response
|
// TODO improve response
|
||||||
return c.Error500(err)
|
return c.Error500(err)
|
||||||
@ -45,47 +43,5 @@ func handleRest(handle *Handle) {
|
|||||||
|
|
||||||
ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING)
|
ModelUpdateStatus(c, model.Id, CONFIRM_PRE_TRAINING)
|
||||||
return c.SendJSON(model.Id)
|
return c.SendJSON(model.Id)
|
||||||
}
|
|
||||||
|
|
||||||
f, err := MyParseForm(r)
|
|
||||||
if err != nil {
|
|
||||||
// TODO improve response
|
|
||||||
return c.ErrorCode(nil, 400, c.AddMap(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if err == ModelNotFoundError {
|
|
||||||
return c.ErrorCode(nil, http.StatusNotFound, AnyMap{
|
|
||||||
"NotFoundMessage": "Model not found",
|
|
||||||
"GoBackLink": "/models",
|
|
||||||
})
|
|
||||||
} 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))
|
|
||||||
}
|
|
||||||
|
|
||||||
os.RemoveAll(path.Join("savedData", model.Id, "defs"))
|
|
||||||
|
|
||||||
_, err = handle.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
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -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
|
|
||||||
})
|
|
||||||
}
|
|
@ -6,12 +6,12 @@ import (
|
|||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
"math"
|
"math"
|
||||||
"net/http"
|
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"path"
|
"path"
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
|
||||||
model_classes "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/classes"
|
model_classes "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/classes"
|
||||||
@ -23,6 +23,19 @@ import (
|
|||||||
const EPOCH_PER_RUN = 20
|
const EPOCH_PER_RUN = 20
|
||||||
const MAX_EPOCH = 100
|
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) {
|
func MakeDefenition(db *sql.DB, model_id string, target_accuracy int) (id string, err error) {
|
||||||
id = ""
|
id = ""
|
||||||
rows, err := db.Query("insert into model_definition (model_id, target_accuracy) values ($1, $2) returning id;", model_id, target_accuracy)
|
rows, err := db.Query("insert into model_definition (model_id, target_accuracy) values ($1, $2) returning id;", model_id, target_accuracy)
|
||||||
@ -706,7 +719,7 @@ func trainModelExp(c *Context, model *BaseModel) {
|
|||||||
|
|
||||||
definitions, err = GetDbMultitple[TrainModelRowUsable](c, "model_definition where status=$1 and model_id=$2", MODEL_DEFINITION_STATUS_INIT, model.Id)
|
definitions, err = GetDbMultitple[TrainModelRowUsable](c, "model_definition where status=$1 and model_id=$2", MODEL_DEFINITION_STATUS_INIT, model.Id)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
failed("Failed to get definitions");
|
failed("Failed to get definitions")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if len(definitions) == 0 {
|
if len(definitions) == 0 {
|
||||||
@ -839,7 +852,7 @@ func trainModelExp(c *Context, model *BaseModel) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, d := range(to_delete) {
|
for _, d := range to_delete {
|
||||||
os.RemoveAll(path.Join("savedData", model.Id, "defs", d.Id))
|
os.RemoveAll(path.Join("savedData", model.Id, "defs", d.Id))
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1293,7 +1306,12 @@ func generateExpandableDefinitions(c *Context, model *BaseModel, target_accuracy
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handle_models_train_json(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
func handleTrain(handle *Handle) {
|
||||||
|
handle.Post("/models/train", func(c *Context) *Error {
|
||||||
|
if !c.CheckAuthLevel(1) {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var dat struct {
|
var dat struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
ModelType string `json:"model_type"`
|
ModelType string `json:"model_type"`
|
||||||
@ -1301,7 +1319,7 @@ func handle_models_train_json(w http.ResponseWriter, r *http.Request, c *Context
|
|||||||
Accuracy int `json:"accuracy"`
|
Accuracy int `json:"accuracy"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err_ := c.ToJSON(r, &dat); err_ != nil {
|
if err_ := c.ToJSON(&dat); err_ != nil {
|
||||||
return err_
|
return err_
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1350,106 +1368,19 @@ func handle_models_train_json(w http.ResponseWriter, r *http.Request, c *Context
|
|||||||
fmt.Println("Failed to update model status")
|
fmt.Println("Failed to update model status")
|
||||||
fmt.Println(err)
|
fmt.Println(err)
|
||||||
// TODO improve this response
|
// TODO improve this response
|
||||||
return Error500(err)
|
return c.Error500(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.SendJSON(model.Id)
|
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) {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if c.Mode == JSON {
|
|
||||||
return handle_models_train_json(w, r, c)
|
|
||||||
}
|
|
||||||
|
|
||||||
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))
|
|
||||||
}
|
|
||||||
|
|
||||||
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!",
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
model, err := GetBaseModel(handle.Db, id)
|
|
||||||
if err == ModelNotFoundError {
|
|
||||||
return ErrorCode(nil, http.StatusNotFound, c.AddMap(AnyMap{
|
|
||||||
"NotFoundMessage": "Model not found",
|
|
||||||
"GoBackLink": "/models",
|
|
||||||
}))
|
|
||||||
} else if err != nil {
|
|
||||||
// TODO improve this response
|
|
||||||
return Error500(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
if model.Status != CONFIRM_PRE_TRAINING {
|
|
||||||
// TODO improve this response
|
|
||||||
return ErrorCode(nil, 400, c.AddMap(nil))
|
|
||||||
}
|
|
||||||
|
|
||||||
if model_type_id == 2 {
|
|
||||||
full_error := generateExpandableDefinitions(c, model, accuracy, number_of_models)
|
|
||||||
if full_error != nil {
|
|
||||||
return full_error
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
full_error := generateDefinitions(c, model, accuracy, number_of_models)
|
|
||||||
if full_error != nil {
|
|
||||||
return full_error
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if model_type_id == 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)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Println("Failed to update model status")
|
|
||||||
fmt.Println(err)
|
|
||||||
// TODO improve this response
|
|
||||||
return Error500(err)
|
|
||||||
}
|
|
||||||
Redirect("/models/edit?id="+model.Id, c.Mode, w, r)
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.Get("/model/epoch/update", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Get("/model/epoch/update", func(c *Context) *Error {
|
||||||
// TODO check auth level
|
f := c.R.URL.Query()
|
||||||
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()
|
|
||||||
|
|
||||||
accuracy := 0.0
|
accuracy := 0.0
|
||||||
|
|
||||||
if !CheckId(f, "model_id") || !CheckId(f, "definition") || CheckEmpty(f, "epoch") || !CheckFloat64(f, "accuracy", &accuracy) {
|
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.JsonBadRequest("Invalid: model_id or definition or epoch or accuracy")
|
||||||
return c.UnsafeErrorCode(nil, 400, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accuracy = accuracy * 100
|
accuracy = accuracy * 100
|
||||||
@ -1458,9 +1389,7 @@ func handleTrain(handle *Handle) {
|
|||||||
def_id := f.Get("definition")
|
def_id := f.Get("definition")
|
||||||
epoch, err := strconv.Atoi(f.Get("epoch"))
|
epoch, err := strconv.Atoi(f.Get("epoch"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger.Warn("Epoch is not a number")
|
return c.JsonBadRequest("Epoch is not a number")
|
||||||
// No need to improve message because this function is only called internaly
|
|
||||||
return c.UnsafeErrorCode(nil, 400, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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)
|
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 {
|
if status != 3 {
|
||||||
c.Logger.Warn("Definition not on status 3(training)", "status", status)
|
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.JsonBadRequest("Definition not on status 3(training)")
|
||||||
return c.UnsafeErrorCode(nil, 400, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Logger.Info("Updated model_definition!", "model", model_id, "progress", epoch, "accuracy", accuracy)
|
c.Logger.Info("Updated model_definition!", "model", model_id, "progress", epoch, "accuracy", accuracy)
|
||||||
@ -1495,21 +1423,13 @@ func handleTrain(handle *Handle) {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.Get("/model/head/epoch/update", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Get("/model/head/epoch/update", func(c *Context) *Error {
|
||||||
// TODO check auth level
|
f := c.R.URL.Query()
|
||||||
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()
|
|
||||||
|
|
||||||
accuracy := 0.0
|
accuracy := 0.0
|
||||||
|
|
||||||
if !CheckId(f, "head_id") || CheckEmpty(f, "epoch") || !CheckFloat64(f, "accuracy", &accuracy) {
|
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.JsonBadRequest("Invalid: model_id or definition or epoch or accuracy")
|
||||||
return c.UnsafeErrorCode(nil, 400, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
accuracy = accuracy * 100
|
accuracy = accuracy * 100
|
||||||
@ -1517,9 +1437,7 @@ func handleTrain(handle *Handle) {
|
|||||||
head_id := f.Get("head_id")
|
head_id := f.Get("head_id")
|
||||||
epoch, err := strconv.Atoi(f.Get("epoch"))
|
epoch, err := strconv.Atoi(f.Get("epoch"))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger.Warn("Epoch is not a number")
|
return c.JsonBadRequest("Epoch is not a number")
|
||||||
// No need to improve message because this function is only called internaly
|
|
||||||
return c.UnsafeErrorCode(nil, 400, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := c.Db.Query("select hd.status from exp_model_head as hd where hd.id=$1;", head_id)
|
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 {
|
if status != 3 {
|
||||||
c.Logger.Warn("Head not on status 3(training)", "status", status)
|
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.JsonBadRequest("Head not on status 3(training)")
|
||||||
return c.UnsafeErrorCode(nil, 400, nil)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
c.Logger.Info("Updated model_head!", "head", head_id, "progress", epoch, "accuracy", accuracy)
|
c.Logger.Info("Updated model_head!", "head", head_id, "progress", epoch, "accuracy", accuracy)
|
||||||
|
@ -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")
|
|
||||||
}
|
|
||||||
}
|
|
@ -13,194 +13,28 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
|
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
|
||||||
|
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/utils"
|
||||||
"github.com/charmbracelet/log"
|
"github.com/charmbracelet/log"
|
||||||
"github.com/goccy/go-json"
|
"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 AnyMap = map[string]interface{}
|
||||||
|
|
||||||
type Error struct {
|
type Error struct {
|
||||||
Code int
|
Code int
|
||||||
Msg *string
|
data any
|
||||||
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")
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type HandleFunc struct {
|
type HandleFunc struct {
|
||||||
path string
|
path string
|
||||||
mode AnswerType
|
fn func(c *Context) *Error
|
||||||
fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handler interface {
|
type Handler interface {
|
||||||
New()
|
New()
|
||||||
Startup()
|
Startup()
|
||||||
Get(fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error)
|
Get(fn func(c *Context) *Error)
|
||||||
Post(fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error)
|
Post(fn func(c *Context) *Error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type Handle struct {
|
type Handle struct {
|
||||||
@ -219,139 +53,91 @@ func decodeBody(r *http.Request) (string, *Error) {
|
|||||||
return string(body[:]), nil
|
return string(body[:]), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func handleError(err *Error, w http.ResponseWriter, context *Context) {
|
func handleError(err *Error, c *Context) {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
data := context.AddMap(err.data)
|
c.Logger.Warn("Responded with error", "code", err.Code, "data", err.data)
|
||||||
if err.Code == http.StatusNotFound {
|
c.Writer.WriteHeader(err.Code)
|
||||||
if context.Mode == HTML {
|
var e *Error
|
||||||
w.WriteHeader(309)
|
if err.data != nil {
|
||||||
context.Mode = HTMLFULL
|
e = c.SendJSON(err.data)
|
||||||
|
} else {
|
||||||
|
e = c.SendJSON(500)
|
||||||
}
|
}
|
||||||
LoadBasedOnAnswer(context.Mode, w, "404.html", data)
|
if e != nil {
|
||||||
return
|
c.Logger.Error("Something went very wront while trying to send and error message")
|
||||||
}
|
c.Writer.Write([]byte("505"))
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Handle) Get(path string, fn func(w http.ResponseWriter, r *http.Request, c *Context) *Error) {
|
func (x *Handle) Get(path string, fn func(c *Context) *Error) {
|
||||||
x.gets = append(x.gets, HandleFunc{path, NORMAL | HTML | HTMLFULL | JSON, fn})
|
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) {
|
func (x *Handle) Post(path string, fn func(c *Context) *Error) {
|
||||||
x.gets = append(x.gets, HandleFunc{path, NORMAL | HTML | HTMLFULL, fn})
|
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) {
|
func (x *Handle) Delete(path string, fn func(c *Context) *Error) {
|
||||||
x.gets = append(x.gets, HandleFunc{path, JSON, fn})
|
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) {
|
func (x *Handle) handleGets(context *Context) {
|
||||||
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) {
|
|
||||||
for _, s := range x.gets {
|
for _, s := range x.gets {
|
||||||
if s.path == r.URL.Path && context.Mode&s.mode != 0 {
|
if s.path == context.R.URL.Path {
|
||||||
handleError(s.fn(w, r, context), w, context)
|
handleError(s.fn(context), context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if context.Mode != HTMLFULL {
|
handleError(&Error{404, "Endpoint not found"}, context)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
}
|
|
||||||
LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Handle) handlePosts(w http.ResponseWriter, r *http.Request, context *Context) {
|
func (x *Handle) handlePosts(context *Context) {
|
||||||
for _, s := range x.posts {
|
for _, s := range x.posts {
|
||||||
if s.path == r.URL.Path && context.Mode&s.mode != 0 {
|
if s.path == context.R.URL.Path {
|
||||||
handleError(s.fn(w, r, context), w, context)
|
handleError(s.fn(context), context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if context.Mode != HTMLFULL {
|
handleError(&Error{404, "Endpoint not found"}, context)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
}
|
|
||||||
LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (x *Handle) handleDeletes(w http.ResponseWriter, r *http.Request, context *Context) {
|
func (x *Handle) handleDeletes(context *Context) {
|
||||||
for _, s := range x.deletes {
|
for _, s := range x.deletes {
|
||||||
if s.path == r.URL.Path && context.Mode&s.mode != 0 {
|
if s.path == context.R.URL.Path {
|
||||||
handleError(s.fn(w, r, context), w, context)
|
handleError(s.fn(context), context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if context.Mode != HTMLFULL {
|
handleError(&Error{404, "Endpoint not found"}, context)
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
}
|
|
||||||
LoadBasedOnAnswer(context.Mode, w, "404.html", context.AddMap(nil))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func CheckAuthLevel(authLevel int, w http.ResponseWriter, r *http.Request, c *Context) bool {
|
func (c *Context) CheckAuthLevel(authLevel int) bool {
|
||||||
if authLevel > 0 {
|
if authLevel > 0 {
|
||||||
if c.requireAuth(w, r) {
|
if c.requireAuth() {
|
||||||
Logoff(c.Mode, w, r)
|
c.Logoff()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if c.User.UserType < authLevel {
|
if c.User.UserType < authLevel {
|
||||||
notAuth(c.Mode, w, r)
|
c.NotAuth()
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
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 {
|
type Context struct {
|
||||||
Token *string
|
Token *string
|
||||||
User *dbtypes.User
|
User *dbtypes.User
|
||||||
Mode AnswerType
|
|
||||||
Logger *log.Logger
|
Logger *log.Logger
|
||||||
Db *sql.DB
|
Db *sql.DB
|
||||||
Writer http.ResponseWriter
|
Writer http.ResponseWriter
|
||||||
|
R *http.Request
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Context) ToJSON(r *http.Request, dat any) *Error {
|
func (c Context) ToJSON(dat any) *Error {
|
||||||
|
|
||||||
decoder := json.NewDecoder(r.Body)
|
decoder := json.NewDecoder(c.R.Body)
|
||||||
|
|
||||||
err := decoder.Decode(dat)
|
err := decoder.Decode(dat)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -400,21 +186,29 @@ func (c Context) JsonErrorBadRequest(err error, dat any) *Error {
|
|||||||
return c.SendJSONStatus(http.StatusBadRequest, dat)
|
return c.SendJSONStatus(http.StatusBadRequest, dat)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Context) Error400(err error, message string, w http.ResponseWriter, path string, base string, data AnyMap) *Error {
|
func (c *Context) GetModelFromId(id_path string) (*BaseModel, *Error) {
|
||||||
c.SetReportCaller(true)
|
|
||||||
c.Logger.Error(message)
|
id, err := GetIdFromUrl(c, id_path)
|
||||||
c.SetReportCaller(false)
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.Logger.Errorf("Something went wrong returning with: %d\n.Err:\n", http.StatusBadRequest)
|
return nil, c.SendJSONStatus(http.StatusNotFound, "Model not found")
|
||||||
c.Logger.Error(err)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.Mode == JSON {
|
model, err := GetBaseModel(c.Db, id)
|
||||||
return &Error{http.StatusBadRequest, nil, c.AddMap(data)}
|
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 model, nil
|
||||||
return 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) {
|
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 {
|
if code == 400 {
|
||||||
c.SetReportCaller(true)
|
c.SetReportCaller(true)
|
||||||
c.Logger.Warn("When returning BadRequest(400) please use context.Error400\n")
|
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.Errorf("Something went wrong returning with: %d\n.Err:\n", code)
|
||||||
c.Logger.Error(err)
|
c.Logger.Error(err)
|
||||||
}
|
}
|
||||||
return &Error{code, nil, c.AddMap(data)}
|
return &Error{code, 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)}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Context) Error500(err error) *Error {
|
func (c Context) Error500(err error) *Error {
|
||||||
return c.ErrorCode(err, http.StatusInternalServerError, nil)
|
return c.ErrorCode(err, http.StatusInternalServerError, nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c Context) AddMap(m AnyMap) AnyMap {
|
func (c *Context) requireAuth() bool {
|
||||||
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 {
|
|
||||||
if c.User == nil {
|
if c.User == nil {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -471,7 +247,7 @@ func (c *Context) requireAuth(w http.ResponseWriter, r *http.Request) bool {
|
|||||||
|
|
||||||
var LogoffError = errors.New("Invalid token!")
|
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
|
var token *string
|
||||||
|
|
||||||
@ -482,27 +258,18 @@ func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request,
|
|||||||
Prefix: r.URL.Path,
|
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")
|
t := r.Header.Get("token")
|
||||||
if t != "" {
|
if t != "" {
|
||||||
token = &t
|
token = &t
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// TODO check that the token is still valid
|
// TODO check that the token is still valid
|
||||||
if token == nil {
|
if token == nil {
|
||||||
return &Context{
|
return &Context{
|
||||||
Mode: mode,
|
|
||||||
Logger: logger,
|
Logger: logger,
|
||||||
Db: handler.Db,
|
Db: handler.Db,
|
||||||
Writer: w,
|
Writer: w,
|
||||||
|
R: r,
|
||||||
}, nil
|
}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -511,52 +278,22 @@ func (x Handle) createContext(handler *Handle, mode AnswerType, r *http.Request,
|
|||||||
return nil, errors.Join(err, LogoffError)
|
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 contextlessLogoff(w http.ResponseWriter) {
|
||||||
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 Logoff(mode AnswerType, w http.ResponseWriter, r *http.Request) {
|
|
||||||
if (mode == JSON) {
|
|
||||||
w.WriteHeader(http.StatusUnauthorized)
|
w.WriteHeader(http.StatusUnauthorized)
|
||||||
w.Write([]byte("\"Not Authorized\""))
|
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 notAuth(mode AnswerType, w http.ResponseWriter, r *http.Request) {
|
func (c *Context) Logoff() { contextlessLogoff(c.Writer) }
|
||||||
if mode == JSON {
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
func (c *Context) NotAuth() {
|
||||||
w.Write([]byte("\"You can not access this resource!\""))
|
c.Writer.WriteHeader(http.StatusUnauthorized)
|
||||||
return
|
e := c.SendJSON("Not Authorized")
|
||||||
}
|
if e != nil {
|
||||||
if mode&(HTMLFULL|HTML) != 0 {
|
c.Writer.WriteHeader(http.StatusInternalServerError)
|
||||||
w.WriteHeader(http.StatusForbidden)
|
c.Writer.Write([]byte("You can not access this resource!"))
|
||||||
w.Write([]byte("You can not access this resource!"))
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(http.StatusForbidden)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -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) {
|
func (x Handle) ReadFiles(pathTest string, baseFilePath string, fileType string, contentType string) {
|
||||||
http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc(pathTest, func(w http.ResponseWriter, r *http.Request) {
|
||||||
user_path := r.URL.Path[len(pathTest):]
|
user_path := r.URL.Path[len(pathTest):]
|
||||||
@ -704,47 +426,45 @@ func NewHandler(db *sql.DB) *Handle {
|
|||||||
x := &Handle{db, gets, posts, deletes}
|
x := &Handle{db, gets, posts, deletes}
|
||||||
|
|
||||||
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
|
||||||
// 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-Origin", "*")
|
||||||
w.Header().Add("Access-Control-Allow-Headers", "*")
|
w.Header().Add("Access-Control-Allow-Headers", "*")
|
||||||
w.Header().Add("Access-Control-Allow-Methods", "*")
|
w.Header().Add("Access-Control-Allow-Methods", "*")
|
||||||
|
|
||||||
|
// Decide answertype
|
||||||
|
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
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
//Login state
|
||||||
context, err := x.createContext(x, ans, r, w)
|
context, err := x.createContext(x, r, w)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
Logoff(ans, w, r)
|
contextlessLogoff(w)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
// context.Logger.Info("Parsing", "path", r.URL.Path)
|
// context.Logger.Info("Parsing", "path", r.URL.Path)
|
||||||
|
|
||||||
if r.Method == "GET" {
|
if r.Method == "GET" {
|
||||||
x.handleGets(w, r, context)
|
x.handleGets(context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if r.Method == "POST" {
|
if r.Method == "POST" {
|
||||||
x.handlePosts(w, r, context)
|
x.handlePosts(context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if r.Method == "DELETE" {
|
if r.Method == "DELETE" {
|
||||||
x.handleDeletes(w, r, context)
|
x.handleDeletes(context)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if r.Method == "OPTIONS" {
|
if r.Method == "OPTIONS" {
|
||||||
|
@ -58,12 +58,12 @@ func IsValidUUID(u string) bool {
|
|||||||
return err == nil
|
return err == nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetIdFromUrl(r *http.Request, target string) (string, error) {
|
func GetIdFromUrl(c *Context, target string) (string, error) {
|
||||||
if !r.URL.Query().Has(target) {
|
if !c.R.URL.Query().Has(target) {
|
||||||
return "", errors.New("Query does not have " + 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 {
|
if len(id) == 0 {
|
||||||
return "", errors.New("Query is empty for " + target)
|
return "", errors.New("Query is empty for " + target)
|
||||||
}
|
}
|
||||||
|
2
main.go
2
main.go
@ -50,8 +50,6 @@ func main() {
|
|||||||
handle.ReadTypesFiles("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"})
|
handle.ReadTypesFiles("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"})
|
||||||
handle.ReadTypesFilesApi("/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)
|
usersEndpints(db, handle)
|
||||||
HandleModels(handle)
|
HandleModels(handle)
|
||||||
|
|
||||||
|
259
users.go
259
users.go
@ -6,7 +6,6 @@ import (
|
|||||||
"encoding/hex"
|
"encoding/hex"
|
||||||
"io"
|
"io"
|
||||||
"net/http"
|
"net/http"
|
||||||
"time"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/bcrypt"
|
"golang.org/x/crypto/bcrypt"
|
||||||
|
|
||||||
@ -76,10 +75,7 @@ func generateToken(db *sql.DB, email string, password string) (string, bool) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func usersEndpints(db *sql.DB, handle *Handle) {
|
func usersEndpints(db *sql.DB, handle *Handle) {
|
||||||
handle.GetHTML("/login", AnswerTemplate("login.html", nil, 0))
|
handle.Post("/login", func(c *Context) *Error {
|
||||||
handle.Post("/login", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
|
||||||
if c.Mode == JSON {
|
|
||||||
|
|
||||||
type UserLogin struct {
|
type UserLogin struct {
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
Password string `json:"password"`
|
Password string `json:"password"`
|
||||||
@ -87,7 +83,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
|
|
||||||
var dat UserLogin
|
var dat UserLogin
|
||||||
|
|
||||||
if err := c.ToJSON(r, &dat); err != nil {
|
if err := c.ToJSON(&dat); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -119,44 +115,9 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return c.SendJSON(userReturn)
|
return c.SendJSON(userReturn)
|
||||||
}
|
|
||||||
|
|
||||||
r.ParseForm()
|
|
||||||
f := r.Form
|
|
||||||
|
|
||||||
if CheckEmpty(f, "email") || CheckEmpty(f, "password") {
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{
|
|
||||||
"Submited": true,
|
|
||||||
}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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)
|
|
||||||
if !login {
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "login.html", c.AddMap(AnyMap{
|
|
||||||
"Submited": true,
|
|
||||||
"NoUserOrPassword": true,
|
|
||||||
"Email": email,
|
|
||||||
}))
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.GetHTML("/register", AnswerTemplate("register.html", nil, 0))
|
handle.Post("/register", func(c *Context) *Error {
|
||||||
handle.Post("/register", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
|
||||||
if c.Mode == JSON {
|
|
||||||
type UserLogin struct {
|
type UserLogin struct {
|
||||||
Username string `json:"username"`
|
Username string `json:"username"`
|
||||||
Email string `json:"email"`
|
Email string `json:"email"`
|
||||||
@ -165,7 +126,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
|
|
||||||
var dat UserLogin
|
var dat UserLogin
|
||||||
|
|
||||||
if err := c.ToJSON(r, &dat); err != nil {
|
if err := c.ToJSON(&dat); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,112 +199,41 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return c.SendJSON(userReturn)
|
return c.SendJSON(userReturn)
|
||||||
}
|
|
||||||
|
|
||||||
r.ParseForm()
|
|
||||||
f := r.Form
|
|
||||||
|
|
||||||
if CheckEmpty(f, "email") || CheckEmpty(f, "password") || CheckEmpty(f, "username") {
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{
|
|
||||||
"Submited": true,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
email := f.Get("email")
|
|
||||||
username := f.Get("username")
|
|
||||||
password := f.Get("password")
|
|
||||||
|
|
||||||
rows, err := db.Query("select username, email from users where username=$1 or email=$2;", username, email)
|
|
||||||
if err != nil {
|
|
||||||
panic("TODO handle this")
|
|
||||||
}
|
|
||||||
defer rows.Close()
|
|
||||||
|
|
||||||
if rows.Next() {
|
|
||||||
var db_username string
|
|
||||||
var db_email string
|
|
||||||
err = rows.Scan(&db_username, &db_email)
|
|
||||||
if err != nil {
|
|
||||||
panic("TODO handle this better")
|
|
||||||
}
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{
|
|
||||||
"Submited": true,
|
|
||||||
"Email": email,
|
|
||||||
"Username": username,
|
|
||||||
"EmailError": db_email == email,
|
|
||||||
"UserError": db_username == username,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
if len([]byte(password)) > 68 {
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "register.html", AnyMap{
|
|
||||||
"Submited": true,
|
|
||||||
"Email": email,
|
|
||||||
"Username": username,
|
|
||||||
"PasswordToLong": true,
|
|
||||||
})
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
salt := generateSalt()
|
|
||||||
hash_password, err := hashPassword(password, salt)
|
|
||||||
if err != nil {
|
|
||||||
return &Error{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_, err = db.Exec("insert into users (username, email, salt, password) values ($1, $2, $3, $4);", username, email, salt, hash_password)
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return &Error{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO Give this to the generateToken function
|
|
||||||
expiration := time.Now().Add(24 * time.Hour)
|
|
||||||
token, login := generateToken(db, email, password)
|
|
||||||
|
|
||||||
if !login {
|
|
||||||
msg := "Login failed"
|
|
||||||
return &Error{
|
|
||||||
Code: http.StatusInternalServerError,
|
|
||||||
Msg: &msg,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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
|
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.Get("/user/info", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
// TODO allow admin users to update this data
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
handle.Get("/user/info", func(c *Context) *Error {
|
||||||
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode == JSON {
|
|
||||||
return c.Error500(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "users/edit.html", c.AddMap(AnyMap{
|
user, err := dbtypes.UserFromToken(c.Db, *c.Token)
|
||||||
"Email": c.User.Email,
|
if err != nil {
|
||||||
}))
|
return c.Error500(err)
|
||||||
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
|
// Handles updating users
|
||||||
handle.Post("/user/info", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Post("/user/info", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(int(dbtypes.User_Normal), w, r, c) {
|
if !c.CheckAuthLevel(int(dbtypes.User_Normal)) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode != JSON {
|
|
||||||
return c.Error500(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
type UserData struct {
|
type UserData struct {
|
||||||
Id string `json:"id"`
|
Id string `json:"id"`
|
||||||
@ -352,7 +242,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
|
|
||||||
var dat UserData
|
var dat UserData
|
||||||
|
|
||||||
if err := c.ToJSON(r, &dat); err != nil {
|
if err := c.ToJSON(&dat); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -417,38 +307,10 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
return c.SendJSON(toReturnUser)
|
return c.SendJSON(toReturnUser)
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.Post("/user/info/email", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
handle.Post("/user/info/password", func(c *Context) *Error {
|
||||||
if !CheckAuthLevel(1, w, r, c) {
|
if !c.CheckAuthLevel(1) {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if c.Mode == JSON {
|
|
||||||
return c.Error500(nil)
|
|
||||||
}
|
|
||||||
|
|
||||||
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,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
_, 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)
|
|
||||||
}
|
|
||||||
|
|
||||||
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 {
|
var dat struct {
|
||||||
Old_Password string `json:"old_password"`
|
Old_Password string `json:"old_password"`
|
||||||
@ -456,7 +318,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
Password2 string `json:"password2"`
|
Password2 string `json:"password2"`
|
||||||
}
|
}
|
||||||
|
|
||||||
if err := c.ToJSON(r, &dat); err != nil {
|
if err := c.ToJSON(&dat); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -472,7 +334,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
|
|
||||||
_, login := generateToken(db, c.User.Email, dat.Old_Password)
|
_, login := generateToken(db, c.User.Email, dat.Old_Password)
|
||||||
if !login {
|
if !login {
|
||||||
return c.JsonBadRequest("Password is incorrect");
|
return c.JsonBadRequest("Password is incorrect")
|
||||||
}
|
}
|
||||||
|
|
||||||
salt := generateSalt()
|
salt := generateSalt()
|
||||||
@ -487,58 +349,7 @@ func usersEndpints(db *sql.DB, handle *Handle) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return c.SendJSON(c.User.Id)
|
return c.SendJSON(c.User.Id)
|
||||||
}
|
|
||||||
|
|
||||||
r.ParseForm()
|
|
||||||
f := r.Form
|
|
||||||
|
|
||||||
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"))
|
|
||||||
if !login {
|
|
||||||
return c.Error400(nil, "Password was incorrect", w, "users/edit.html", "mainbody", c.AddMap(AnyMap{
|
|
||||||
"Email": c.User.Email,
|
|
||||||
"NoUserOrPassword": true,
|
|
||||||
}))
|
|
||||||
}
|
|
||||||
|
|
||||||
salt := generateSalt()
|
|
||||||
hash_password, err := hashPassword(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)
|
|
||||||
}
|
|
||||||
|
|
||||||
LoadBasedOnAnswer(c.Mode, w, "users/edit.html", c.AddMap(AnyMap{
|
|
||||||
"email": c.User.Email,
|
|
||||||
}))
|
|
||||||
return nil
|
|
||||||
})
|
})
|
||||||
|
|
||||||
handle.Get("/logout", func(w http.ResponseWriter, r *http.Request, c *Context) *Error {
|
// TODO create function to remove token
|
||||||
if c.Mode == JSON {
|
|
||||||
panic("TODO handle json")
|
|
||||||
}
|
|
||||||
Logoff(c.Mode, w, r)
|
|
||||||
return nil
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -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 }}
|
|
@ -1,6 +0,0 @@
|
|||||||
{{ define "head" }}
|
|
||||||
<title>
|
|
||||||
Error Page
|
|
||||||
</title>
|
|
||||||
{{ end }}
|
|
||||||
{{ define "body" }}Heyyyyyy Err {{ end }}
|
|
@ -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 |
@ -1,6 +0,0 @@
|
|||||||
{{ define "title"}} Home : AI Stuff {{ end }}
|
|
||||||
|
|
||||||
{{ define "mainbody" }}
|
|
||||||
Main Page TODO
|
|
||||||
{{ end }}
|
|
||||||
|
|
150
views/js/main.js
150
views/js/main.js
@ -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")
|
|
||||||
}
|
|
||||||
});
|
|
@ -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>
|
|
@ -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 }}
|
|
||||||
|
|
@ -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}}
|
|
@ -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 }}
|
|
@ -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 }}
|
|
@ -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}}
|
|
@ -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>
|
|
@ -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}}
|
|
@ -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;
|
|
||||||
}
|
|
@ -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}}
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user