diff --git a/go.mod b/go.mod
index bd54c89..1b0929f 100644
--- a/go.mod
+++ b/go.mod
@@ -21,6 +21,9 @@ require (
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.19.0 // indirect
github.com/goccy/go-json v0.10.2 // indirect
+ github.com/jackc/pgpassfile v1.0.0 // indirect
+ github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
+ github.com/jackc/pgx/v5 v5.5.5 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
diff --git a/go.sum b/go.sum
index ff10af9..7d6727e 100644
--- a/go.sum
+++ b/go.sum
@@ -10,6 +10,7 @@ github.com/charmbracelet/log v0.2.5 h1:1yVvyKCKVV639RR4LIq1iy1Cs1AKxuNO+Hx2LJtk7
github.com/charmbracelet/log v0.2.5/go.mod h1:nQGK8tvc4pS9cvVEH/pWJiZ50eUq1aoXUOjGpXvdD0k=
github.com/charmbracelet/log v0.3.1 h1:TjuY4OBNbxmHWSwO3tosgqs5I3biyY8sQPny/eCMTYw=
github.com/charmbracelet/log v0.3.1/go.mod h1:OR4E1hutLsax3ZKpXbgUqPtTjQfrh1pG3zwHGWuuq8g=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/galeone/tensorflow/tensorflow/go v0.0.0-20221023090153-6b7fa0680c3e h1:9+2AEFZymTi25FIIcDwuzcOPH04z9+fV6XeLiGORPDI=
@@ -34,6 +35,12 @@ github.com/google/uuid v1.3.1 h1:KjJaJ9iWZ3jOFZIf1Lqf4laDRCasjl0BCmnEGxkdLb4=
github.com/google/uuid v1.3.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
+github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a h1:bbPeKD0xmW/Y25WS6cokEszi5g+S0QxI/d45PkRi7Nk=
+github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM=
+github.com/jackc/pgx/v5 v5.5.5 h1:amBjrZVmksIdNjxGW/IiIMzxMKZFelXbUoPNb+8sjQw=
+github.com/jackc/pgx/v5 v5.5.5/go.mod h1:ez9gk+OAat140fv9ErkZDYFWmXLfV+++K0uAOiwgm1A=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
@@ -53,11 +60,15 @@ github.com/muesli/reflow v0.3.0 h1:IFsN6K9NfGtjeggFP+68I4chLZV2yIKsXJFNZ+eWh6s=
github.com/muesli/reflow v0.3.0/go.mod h1:pbwTDkVPibjO2kyvBQRBxTWEEGDGq0FlB1BIKtnHY/8=
github.com/muesli/termenv v0.15.2 h1:GohcuySI0QmI3wN8Ok9PtKGkgkFIk7y6Vpb5PvrY+Wo=
github.com/muesli/termenv v0.15.2/go.mod h1:Epx+iuz8sNs7mNKhxzH4fWXGNpZwUaJKRS1noLXviQ8=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rivo/uniseg v0.1.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.2.0 h1:S1pD9weZBuJdFmowNwbpi7BJ8TNftyUImj/0WQi72jY=
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
github.com/rivo/uniseg v0.4.6 h1:Sovz9sDSwbOz9tgUy8JpT+KgCkPYJEN/oYzlJiYTNLg=
github.com/rivo/uniseg v0.4.6/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
+github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
golang.org/x/crypto v0.13.0 h1:mvySKfSWJ+UKUii46M40LOvyWfN0s2U+46/jDd0e6Ck=
golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
golang.org/x/crypto v0.18.0 h1:PGVlW0xEltQnzFZ55hkuX5+KLyrMYhHld1YHO4AKcdc=
@@ -87,3 +98,5 @@ google.golang.org/protobuf v1.28.1 h1:d0NfwRgPtno5B1Wa6L2DAG+KivqkdutMf1UhdNx175
google.golang.org/protobuf v1.28.1/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I=
google.golang.org/protobuf v1.32.0 h1:pPC6BG5ex8PDFnkbrGU3EixyhKcQ2aDuBS36lqK/C7I=
google.golang.org/protobuf v1.32.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
diff --git a/logic/db_types/types.go b/logic/db_types/types.go
index 29d60a8..7200369 100644
--- a/logic/db_types/types.go
+++ b/logic/db_types/types.go
@@ -68,35 +68,25 @@ type BaseModel struct {
Status int
Id string
- ModelType int
- ImageMode int
- Width int
- Height int
- Format string
+ ModelType int `db:"model_type"`
+ ImageModeRaw string `db:"color_mode"`
+ ImageMode int `db:"0"`
+ Width int
+ Height int
+ Format string
+ CanTrain int `db:"can_train"`
}
var ModelNotFoundError = errors.New("Model not found error")
func GetBaseModel(db *sql.DB, id string) (base *BaseModel, err error) {
- rows, err := db.Query("select name, status, id, width, height, color_mode, format, model_type from models where id=$1;", id)
+ var model BaseModel
+ err = GetDBOnce(db, &model, "models where id=$1", id)
if err != nil {
return
}
- defer rows.Close()
-
- if !rows.Next() {
- return nil, ModelNotFoundError
- }
-
- base = &BaseModel{}
- var colorMode string
- err = rows.Scan(&base.Name, &base.Status, &base.Id, &base.Width, &base.Height, &colorMode, &base.Format, &base.ModelType)
- if err != nil {
- return nil, err
- }
-
- base.ImageMode = StringToImageMode(colorMode)
- return
+ model.ImageMode = StringToImageMode(model.ImageModeRaw)
+ return &model, nil
}
func (m BaseModel) CanEval() bool {
diff --git a/logic/db_types/user.go b/logic/db_types/user.go
index 735a443..ee91e64 100644
--- a/logic/db_types/user.go
+++ b/logic/db_types/user.go
@@ -7,7 +7,8 @@ import (
type UserType int
const (
- User_Not_Auth UserType = iota
+ User_Deleted UserType = iota - 1
+ User_Not_Auth
User_Normal
User_Admin
)
diff --git a/logic/models/data.go b/logic/models/data.go
index bdf5d77..47112a0 100644
--- a/logic/models/data.go
+++ b/logic/models/data.go
@@ -439,6 +439,8 @@ func handleDataUpload(handle *Handle) {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return c.Error500(err)
+ } else if model.CanTrain == 0 {
+ return c.JsonBadRequest("Model can not be trained!")
}
// TODO mk this path configurable
@@ -468,6 +470,10 @@ func handleDataUpload(handle *Handle) {
model, err := GetBaseModel(c.Db, obj.Id)
if err == ModelNotFoundError {
return c.JsonBadRequest("Model not found")
+ } else if err != nil {
+ return c.E500M("Failed to get model information", err)
+ } else if model.CanTrain == 0 {
+ return c.JsonBadRequest("Model can not be trained!")
}
if model.ModelType != 2 && model.Status != CONFIRM_PRE_TRAINING || model.ModelType == 2 && model.Status != CONFIRM_PRE_TRAINING && model.Status != READY {
@@ -516,6 +522,8 @@ func handleDataUpload(handle *Handle) {
return c.JsonBadRequest("Could not find the model")
} else if err != nil {
return c.E500M("Error getting model information", err)
+ } else if model.CanTrain == 0 {
+ return c.JsonBadRequest("Model can not be trained!")
}
// TODO make this work for zip files as well
@@ -622,6 +630,8 @@ func handleDataUpload(handle *Handle) {
return c.SendJSONStatus(http.StatusNotFound, "Model not found")
} else if err != nil {
return c.Error500(err)
+ } else if model.CanTrain == 0 {
+ return c.JsonBadRequest("Model can not be trained!")
}
// TODO work in allowing the model to add new in the pre ready moment
diff --git a/logic/models/delete.go b/logic/models/delete.go
index e7fc286..ed0bf27 100644
--- a/logic/models/delete.go
+++ b/logic/models/delete.go
@@ -9,20 +9,27 @@ import (
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
-func deleteModelJSON(c *Context, id string) *Error {
- c.Logger.Warnf("Removing model with id: %s", id)
- _, err := c.Db.Exec("delete from models where id=$1;", id)
+func DeleteModel(c BasePack, id string) (err error) {
+ c.GetLogger().Warnf("Removing model with id: %s", id)
+ _, err = c.GetDb().Exec("delete from models where id=$1;", id)
if err != nil {
- return c.E500M("Failed to delete models", err)
+ return
}
model_path := path.Join("./savedData", id)
- c.Logger.Warnf("Removing folder of model with id: %s at %s", id, model_path)
+ c.GetLogger().Warnf("Removing folder of model with id: %s at %s", id, model_path)
err = os.RemoveAll(model_path)
if err != nil {
- return c.E500M("Failed to remove data", err)
+ return
}
+ return
+}
+func deleteModelJSON(c *Context, id string) *Error {
+ err := DeleteModel(c, id)
+ if err != nil {
+ return c.E500M("Failed to delete models", err)
+ }
return c.SendJSON(id)
}
diff --git a/logic/models/run.go b/logic/models/run.go
index eda033c..8991cd9 100644
--- a/logic/models/run.go
+++ b/logic/models/run.go
@@ -120,7 +120,7 @@ func runModelExp(base BasePack, model *BaseModel, def_id string, inputImage *tf.
func ClassifyTask(base BasePack, task Task) (err error) {
task.UpdateStatusLog(base, TASK_RUNNING, "Runner running task")
- model, err := GetBaseModel(base.GetDb(), task.ModelId)
+ model, err := GetBaseModel(base.GetDb(), *task.ModelId)
if err != nil {
task.UpdateStatusLog(base, TASK_FAILED_RUNNING, "Failed to obtain the model")
return err
diff --git a/logic/models/train/train.go b/logic/models/train/train.go
index af51834..e695267 100644
--- a/logic/models/train/train.go
+++ b/logic/models/train/train.go
@@ -1625,7 +1625,7 @@ func trainExpandable(c *Context, model *BaseModel) {
func RunTaskTrain(b BasePack, task Task) (err error) {
l := b.GetLogger()
- model, err := GetBaseModel(b.GetDb(), task.ModelId)
+ model, err := GetBaseModel(b.GetDb(), *task.ModelId)
if err != nil {
task.UpdateStatusLog(b, TASK_FAILED_RUNNING, "Failed to get model information")
l.Error("Failed to get model information", "err", err)
@@ -1683,7 +1683,7 @@ func RunTaskTrain(b BasePack, task Task) (err error) {
}
func RunTaskRetrain(b BasePack, task Task) (err error) {
- model, err := GetBaseModel(b.GetDb(), task.ModelId)
+ model, err := GetBaseModel(b.GetDb(), *task.ModelId)
if err != nil {
return err
} else if model.Status != READY_RETRAIN {
@@ -1759,6 +1759,8 @@ func handleTrain(handle *Handle) {
return c.JsonBadRequest("Model not found")
} else if err != nil {
return c.E500M("Failed to get model information", err)
+ } else if model.CanTrain == 0 {
+ return c.JsonBadRequest("Model can not be trained!")
}
if model.Status != CONFIRM_PRE_TRAINING {
@@ -1813,6 +1815,8 @@ func handleTrain(handle *Handle) {
return c.E500M("Faield to get model", err)
} else if model.Status != READY && model.Status != READY_RETRAIN_FAILED && model.Status != READY_ALTERATION_FAILED {
return c.JsonBadRequest("Model in invalid status for re-training")
+ } else if model.CanTrain == 0 {
+ return c.JsonBadRequest("Model can not be trained!")
}
c.Logger.Info("Expanding definitions for models", "id", model.Id)
diff --git a/logic/tasks/handleUpload.go b/logic/tasks/handleUpload.go
index 615a30a..962287d 100644
--- a/logic/tasks/handleUpload.go
+++ b/logic/tasks/handleUpload.go
@@ -55,6 +55,8 @@ func handleUpload(handler *Handle) {
model, err := GetBaseModel(c.Db, requestData.ModelId)
if err != nil {
return c.Error500(err)
+ } else if model.CanTrain == 0 {
+ return c.JsonBadRequest("Model can not be trained!")
}
switch model.Status {
diff --git a/logic/tasks/runner/runner.go b/logic/tasks/runner/runner.go
index 6f5737a..8bae0da 100644
--- a/logic/tasks/runner/runner.go
+++ b/logic/tasks/runner/runner.go
@@ -14,6 +14,7 @@ import (
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models/train"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/utils"
+ . "git.andr3h3nriqu3s.com/andr3/fyp/logic/users"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
@@ -71,6 +72,14 @@ func runner(config Config, db *sql.DB, task_channel chan Task, index int, back_c
logger.Error("Failed to tain the model", "error", err)
}
+ back_channel <- index
+ continue
+ } else if task.TaskType == int(TASK_TYPE_DELETE_USER) {
+ logger.Warn("User deleting Task")
+ if err = DeleteUser(base, task); err != nil {
+ logger.Error("Failed to tain the model", "error", err)
+ }
+
back_channel <- index
continue
}
@@ -168,16 +177,23 @@ func RunnerOrchestrator(db *sql.DB, config Config) {
}
if task_to_dispatch == nil {
- var task Task
- err := GetDBOnce(db, &task, "tasks where status=$1 limit 1", TASK_TODO)
+ var task TaskT
+ err := GetDBOnce(db, &task, "tasks as t "+
+ // Get depenencies
+ "left join tasks_dependencies as td on t.id=td.main_id "+
+ // Get the task that the depencey resolves to
+ "left join tasks as t2 on t2.id=td.dependent_id "+
+ "where t.status=1 "+
+ "group by t.id having count(td.id) filter (where t2.status in (0,1,2,3)) = 0;")
if err != NotFoundError && err != nil {
- log.Error("Failed to get tasks from db")
+ log.Error("Failed to get tasks from db", "err", err)
continue
}
if err == NotFoundError {
task_to_dispatch = nil
} else {
- task_to_dispatch = &task
+ temp := Task(task)
+ task_to_dispatch = &temp
}
}
diff --git a/logic/tasks/utils/utils.go b/logic/tasks/utils/utils.go
index 57caa2d..8a7f105 100644
--- a/logic/tasks/utils/utils.go
+++ b/logic/tasks/utils/utils.go
@@ -10,7 +10,7 @@ import (
type Task struct {
Id string `db:"id" json:"id"`
UserId string `db:"user_id" json:"user_id"`
- ModelId string `db:"model_id" json:"model_id"`
+ ModelId *string `db:"model_id" json:"model_id"`
Status int `db:"status" json:"status"`
StatusMessage string `db:"status_message" json:"status_message"`
UserConfirmed int `db:"user_confirmed" json:"user_confirmed"`
@@ -21,6 +21,27 @@ type Task struct {
CreatedOn time.Time `db:"created_on" json:"created"`
}
+// Find better way todo this
+type TaskT struct {
+ Id string `db:"t.id" json:"id"`
+ UserId string `db:"t.user_id" json:"user_id"`
+ ModelId *string `db:"t.model_id" json:"model_id"`
+ Status int `db:"t.status" json:"status"`
+ StatusMessage string `db:"t.status_message" json:"status_message"`
+ UserConfirmed int `db:"t.user_confirmed" json:"user_confirmed"`
+ Compacted int `db:"t.compacted" json:"compacted"`
+ TaskType int `db:"t.task_type" json:"type"`
+ ExtraTaskInfo string `db:"t.extra_task_info" json:"extra_task_info"`
+ Result string `db:"t.result" json:"result"`
+ CreatedOn time.Time `db:"t.created_on" json:"created"`
+}
+
+type TaskDependents struct {
+ Id string `db:"id" json:"id"`
+ MainId string `db:"main_id" json:"main_id"`
+ DependentId string `db:"dependent_id" json:"dependent_id"`
+}
+
type TaskStatus int
const (
@@ -39,6 +60,7 @@ const (
TASK_TYPE_CLASSIFICATION TaskType = 1 + iota
TASK_TYPE_TRAINING
TASK_TYPE_RETRAINING
+ TASK_TYPE_DELETE_USER
)
type TaskAgreement int
@@ -82,3 +104,15 @@ func (t Task) SetResult(base BasePack, result any) (err error) {
_, err = base.GetDb().Exec("update tasks set result=$1 where id=$2", text, t.Id)
return
}
+
+func (t Task) Depend(base BasePack, depend_id string) (err error) {
+ var dependency = struct {
+ Main string `db:"main_id"`
+ Dependent string `db:"dependent_id"`
+ }{
+ Main: t.Id,
+ Dependent: depend_id,
+ }
+ _, err = InsertReturnId(base.GetDb(), &dependency, "tasks_dependencies", "id")
+ return
+}
diff --git a/users.go b/logic/users/users.go
similarity index 76%
rename from users.go
rename to logic/users/users.go
index acdf69d..084842c 100644
--- a/users.go
+++ b/logic/users/users.go
@@ -1,4 +1,4 @@
-package main
+package users
import (
"crypto/rand"
@@ -12,6 +12,8 @@ import (
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
dbtypes "git.andr3h3nriqu3s.com/andr3/fyp/logic/db_types"
+ . "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
+ . "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/utils"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
@@ -80,7 +82,31 @@ func generateToken(db *sql.DB, email string, password string, name string) (stri
return token, true
}
-func usersEndpints(db *sql.DB, handle *Handle) {
+func DeleteUser(base BasePack, task Task) (err error) {
+ ids, err := GetDbMultitple[JustId](base.GetDb(), "models where user_id=$1;", task.ExtraTaskInfo)
+ if err != nil {
+ task.UpdateStatusLog(base, TASK_FAILED_RUNNING, "Could not get models list")
+ return
+ }
+
+ for i := range ids {
+ err = DeleteModel(base, ids[i].Id)
+ if err != nil {
+ base.GetLogger().Error("Could not delete model", "err", err)
+ }
+ }
+
+ _, err = base.GetDb().Exec("delete from users where id=$1", task.ExtraTaskInfo)
+ if err != nil {
+ task.UpdateStatusLog(base, TASK_FAILED_RUNNING, "Could not delete user")
+ return
+ }
+
+ task.UpdateStatusLog(base, TASK_DONE, "User deleted with success")
+ return
+}
+
+func UsersEndpints(db *sql.DB, handle *Handle) {
type UserLogin struct {
Email string `json:"email"`
@@ -98,6 +124,10 @@ func usersEndpints(db *sql.DB, handle *Handle) {
return c.E500M("Failed to get user from token", err)
}
+ if user.UserType == int(User_Deleted) {
+ return c.JsonBadRequest("Your user is in the process of being deleted!")
+ }
+
type UserReturn struct {
Token string `json:"token"`
Id string `json:"id"`
@@ -381,4 +411,82 @@ func usersEndpints(db *sql.DB, handle *Handle) {
return c.SendJSON("Ok")
})
+
+ type DeleteUser struct {
+ Id string `json:"id" validate:"required"`
+ Password string `json:"password" validate:"required"`
+ }
+ DeleteAuthJson(handle, "/user/delete", User_Normal, func(c *Context, obj *DeleteUser) *Error {
+ // TODO let admin delete users
+ if c.User.Id != obj.Id {
+ return c.E500M("TODO implement this for admins", nil)
+ }
+
+ // TODO let admin delete users
+ // Verify the user password
+ _, generated := generateToken(c.Db, c.User.Email, obj.Password, "User Verification")
+ if !generated {
+ return c.JsonBadRequest("Password provided is incorrect")
+ }
+
+ // Disable the ability for the user to create new tasks
+ _, err := c.Db.Exec(
+ "update models as m set can_train=0 "+
+ "from users as u "+
+ "where u.id=$1 and u.id=m.user_id;",
+ obj.Id,
+ )
+ if err != nil {
+ return c.E500M("Failed to stop the models", err)
+ }
+
+ type CreateNewTask struct {
+ UserId string `db:"user_id"`
+ TaskType int `db:"task_type"`
+ Status int `db:"status"`
+ ExtraTaskInfo string `db:"extra_task_info"`
+ }
+
+ newTask := CreateNewTask{
+ UserId: c.Handle.Config.ServiceUser.UserId,
+ // TODO move this to an enum
+ TaskType: int(TASK_TYPE_DELETE_USER),
+ Status: TASK_PREPARING,
+ ExtraTaskInfo: obj.Id,
+ }
+ t_id, err := InsertReturnId(c, &newTask, "tasks", "id")
+ if err != nil {
+ return c.E500M("Failed to create task", err)
+ }
+
+ task := Task{Id: t_id}
+
+ type taskId struct {
+ Id string `db:"t.id"`
+ }
+ tasks, err := GetDbMultitple[taskId](c, "tasks as t "+
+ "left join models as m on m.id=t.model_id "+
+ "where (t.user_id=$1 or m.user_id=$1) and t.status in (0,1,2,3) "+
+ "group by t.id;",
+ obj.Id)
+
+ for i := range tasks {
+ err = task.Depend(c, tasks[i].Id)
+ if err != nil {
+ c.Logger.Error("Failed to mark task as depency", "err", err)
+ }
+ }
+
+ err = task.UpdateStatus(c, TASK_TODO, "Task ready")
+ if err != nil {
+ return c.E500M("Failed to mark task as ready", err)
+ }
+
+ _, err = c.Db.Exec("update users set user_type=-1 where id=$1", obj.Id)
+ if err != nil {
+ return c.E500M("Failed to delete user", err)
+ }
+
+ return c.SendJSON("User to be deleted")
+ })
}
diff --git a/logic/utils/config.go b/logic/utils/config.go
index 4cbc7d5..4c9fbee 100644
--- a/logic/utils/config.go
+++ b/logic/utils/config.go
@@ -97,7 +97,7 @@ func (c *Config) Cleanup(db *sql.DB) {
failLog(err)
_, err = db.Exec("update models set status=$1 where status=$2", FAILED_PREPARING, PREPARING)
failLog(err)
- _, err = db.Exec("update tasks set status=$1 where status=$2", TASK_PICKED_UP, TASK_TODO)
+ _, err = db.Exec("update tasks set status=$1 where status=$2", TASK_TODO, TASK_PICKED_UP)
failLog(err)
tasks, err := GetDbMultitple[Task](db, "tasks where status=$1", TASK_RUNNING)
diff --git a/main.go b/main.go
index 047b436..cf53776 100644
--- a/main.go
+++ b/main.go
@@ -10,6 +10,7 @@ import (
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/models"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/tasks/runner"
+ . "git.andr3h3nriqu3s.com/andr3/fyp/logic/users"
. "git.andr3h3nriqu3s.com/andr3/fyp/logic/utils"
)
@@ -49,7 +50,7 @@ func main() {
// TODO Handle this in other way
handle.ReadTypesFilesApi("/savedData/", ".", []string{".png", ".jpeg"}, []string{"image/png", "image/jpeg"})
- usersEndpints(db, handle)
+ UsersEndpints(db, handle)
HandleModels(handle)
HandleTasks(handle)
diff --git a/sql/models.sql b/sql/models.sql
index 26d9ba4..130291e 100644
--- a/sql/models.sql
+++ b/sql/models.sql
@@ -10,13 +10,14 @@ create table if not exists models (
-- -1: failed preparing
-- 1: preparing
status integer default 1,
-
+
-- Types:
-- 0: Unset
-- 1: simple
-- 2: expandable
model_type integer default 0,
+ can_train integer default 1,
width integer,
height integer,
color_mode varchar (20),
@@ -51,7 +52,7 @@ create table if not exists model_data_point (
status_message text
);
--- drop table if exists model_definition;
+-- drop table if exists model_definition;
create table if not exists model_definition (
id uuid primary key default gen_random_uuid(),
model_id uuid references models (id) on delete cascade,
@@ -82,10 +83,10 @@ create table if not exists model_definition_layer (
-- ei 28,28,1
-- a 28x28 grayscale image
shape text not null,
-
+
-- Type based on the expandability
-- 0: not expandalbe model
- -- 1: fixed
+ -- 1: fixed
-- 2: head
exp_type integer default 0
);
@@ -111,4 +112,3 @@ create table if not exists exp_model_head (
created_on timestamp default current_timestamp,
epoch_progress integer default 0
);
-
diff --git a/sql/tasks.sql b/sql/tasks.sql
index 0bcb6d3..8248ade 100644
--- a/sql/tasks.sql
+++ b/sql/tasks.sql
@@ -16,19 +16,25 @@ create table if not exists tasks (
result text default '',
extra_task_info text default '',
-
+
-- -1: user said task is wrong
-- 0: no user input
-- 1: user said task is ok
user_confirmed integer default 0,
-
+
-- Tells the user if the file has been already compacted into
-- embendings
compacted integer default 0,
-
+
-- TODO move the training tasks to here
-- 1: Classification
task_type integer,
created_on timestamp default current_timestamp
-)
+);
+
+create table if not exists tasks_dependencies (
+ id uuid primary key default gen_random_uuid(),
+ main_id uuid references tasks (id) on delete cascade not null,
+ dependent_id uuid references tasks (id) on delete cascade not null
+);
diff --git a/webpage/src/NavBar.svelte b/webpage/src/NavBar.svelte
index 6c87ba6..1bd3bae 100644
--- a/webpage/src/NavBar.svelte
+++ b/webpage/src/NavBar.svelte
@@ -1,4 +1,5 @@
@@ -26,6 +27,8 @@
+
+
diff --git a/webpage/src/lib/NotificationsStore.svelte.ts b/webpage/src/lib/NotificationsStore.svelte.ts
new file mode 100644
index 0000000..d58f6bf
--- /dev/null
+++ b/webpage/src/lib/NotificationsStore.svelte.ts
@@ -0,0 +1,68 @@
+type NotificationType = 'danger' | 'success' | 'info';
+type Notification = {
+ type?: NotificationType;
+ timeToLive?: number;
+ message: string;
+ html?: string;
+};
+
+export function createNotificationStore(defaultTimetoLive: number) {
+ type InternalNotifications = {
+ notification: Notification;
+ endDate: Date;
+ };
+ let notifications = $state([]);
+ let timeout = $state();
+
+ function clearNotifications() {
+ const now = new Date().getTime();
+ let min: number | undefined = undefined;
+ notifications = notifications.filter((a) => {
+ const t = a.endDate.getTime();
+ if (t <= now) {
+ return false;
+ }
+ if (min == undefined) {
+ min = t;
+ } else if (min > t) {
+ min = t;
+ }
+ return true;
+ });
+ if (min != undefined) {
+ timeout = setTimeout(clearNotifications, now - min);
+ } else {
+ timeout = undefined;
+ }
+ }
+
+ function add(noti: Notification) {
+ if (!timeout) {
+ timeout = setTimeout(clearNotifications, noti.timeToLive ?? defaultTimetoLive);
+ }
+ const now = new Date();
+ notifications.push({
+ notification: noti,
+ endDate: new Date(now.getTime() + (noti.timeToLive ?? defaultTimetoLive))
+ });
+ }
+
+ return {
+ get notifications() {
+ return notifications.map((a) => a.notification);
+ },
+ clear() {
+ notifications.forEach((a) => clearInterval(a.timeout));
+ notifications = [];
+ },
+ add,
+ notify(message: string, type: NotificationType = 'danger') {
+ add({ message, type });
+ },
+ display(message: string) {
+ add({ message, type: 'danger' });
+ }
+ };
+}
+
+export const notificationStore = createNotificationStore(5000);
diff --git a/webpage/src/lib/requests.svelte.ts b/webpage/src/lib/requests.svelte.ts
index afa9520..a1ce757 100644
--- a/webpage/src/lib/requests.svelte.ts
+++ b/webpage/src/lib/requests.svelte.ts
@@ -108,7 +108,11 @@ export async function showMessage(
if (e == null) {
return false;
} else if (e instanceof Response) {
- messages.display(await e.json());
+ try {
+ messages.display(await e.json());
+ } catch (ex) {
+ showMessage(ex, messages, message);
+ }
return true;
} else {
console.error(e);
diff --git a/webpage/src/routes/models/edit/+page.svelte b/webpage/src/routes/models/edit/+page.svelte
index b1bae34..f3681f4 100644
--- a/webpage/src/routes/models/edit/+page.svelte
+++ b/webpage/src/routes/models/edit/+page.svelte
@@ -25,7 +25,7 @@
diff --git a/webpage/src/routes/user-deleted/+page.svelte b/webpage/src/routes/user-deleted/+page.svelte
new file mode 100644
index 0000000..9070217
--- /dev/null
+++ b/webpage/src/routes/user-deleted/+page.svelte
@@ -0,0 +1,8 @@
+Your User has been deleted
+
+
diff --git a/webpage/src/routes/user/info/+page.svelte b/webpage/src/routes/user/info/+page.svelte
index 6330695..280753b 100644
--- a/webpage/src/routes/user/info/+page.svelte
+++ b/webpage/src/routes/user/info/+page.svelte
@@ -7,6 +7,7 @@
import { post } from 'src/lib/requests.svelte';
import MessageSimple, { type DisplayFn } from 'src/lib/MessageSimple.svelte';
import TokenTable from './TokenTable.svelte';
+ import DeleteUser from './DeleteUser.svelte';
onMount(() => {
if (!userStore.isLogin()) {
@@ -110,8 +111,8 @@
-
+
diff --git a/webpage/src/routes/user/info/DeleteUser.svelte b/webpage/src/routes/user/info/DeleteUser.svelte
new file mode 100644
index 0000000..4cf9ade
--- /dev/null
+++ b/webpage/src/routes/user/info/DeleteUser.svelte
@@ -0,0 +1,34 @@
+
+
+
diff --git a/webpage/src/styles/app.css b/webpage/src/styles/app.css
index 7b90b45..9550691 100644
--- a/webpage/src/styles/app.css
+++ b/webpage/src/styles/app.css
@@ -13,6 +13,7 @@
--warning: #fca311;
--warning-transparent: #fca31144;
+ --danger-ligther: #ffe0de;
--danger: #ff8282;
--danger-transparent: #ff828244;
@@ -171,3 +172,20 @@ a.button {
background-color: var(--danger-transparent);
border: 1px solid var(--danger);
}
+
+.box {
+ padding: 30px;
+ margin: 20px 0;
+ border-radius: 10px;
+ box-shadow: 2px 5px 8px 2px #66666655;
+}
+
+.no-top-margin {
+ margin-top: 0px;
+}
+
+.box.danger-bg,
+form.danger-bg {
+ background-color: var(--danger-transparent);
+ border: 1px solid var(--danger);
+}