group tasks

main
alex 2023-04-23 21:33:09 +02:00
parent 1e184cc5ce
commit 2b8be9ebf3
11 changed files with 390 additions and 89 deletions

View File

@ -18,7 +18,7 @@
"name": "Bild zu Label konvertieren",
"onFinish": "pause",
"undoPossible": false,
"scriptPath": "",
"scriptPath": "test1.py",
"parameter": [
{
"parameterName": "labelformat",
@ -39,7 +39,23 @@
"name": "Label drucken",
"onFinish": "nextStep",
"undoPossible": false,
"scriptPath": "",
"scriptPath": "test2.py",
"parameter": [],
"feedback": ""
},
{
"name": "Label 1",
"onFinish": "nextStep",
"undoPossible": false,
"scriptPath": "test2.py",
"parameter": [],
"feedback": ""
},
{
"name": "Label 2",
"onFinish": "nextStep",
"undoPossible": false,
"scriptPath": "test2.py",
"parameter": [],
"feedback": ""
}

View File

@ -0,0 +1,11 @@
import time
time.sleep(3)
print("hello this is test1")
try:
x = 5 / 0 # Hier wird ein Fehler ausgelöst
except ZeroDivisionError:
print("Ein Fehler ist aufgetreten: Division durch Null.")
raise SystemExit(0)

View File

@ -0,0 +1,5 @@
import time
time.sleep(5)
print("hello this is test2 lul")

View File

@ -2,70 +2,23 @@ package grouptasks
import (
"encoding/json"
"fmt"
"janex/admin-dashboard-backend/modules/cache"
"janex/admin-dashboard-backend/modules/database"
"janex/admin-dashboard-backend/modules/structs"
"janex/admin-dashboard-backend/modules/utils"
"janex/admin-dashboard-backend/socketclients"
"os"
"os/exec"
"time"
"github.com/google/uuid"
"github.com/rs/zerolog/log"
)
// read from file structure
var CategoryGroups []CategoryGroup
type CategoryGroup struct {
Category string `json:"category"`
Groups []Group `json:"groups"`
}
type Group struct {
Category string `json:"category"`
Id string `json:"id"`
Name string `json:"name"`
GlobalInputs []GlobalInputs `json:"globalInputs"`
Tasks []Task `json:"tasks"`
}
type GlobalInputs struct {
ParameterName string `json:"parameterName"`
Type string `json:"type"`
DisplayName string `json:"displayName"`
}
type Task struct {
Name string `json:"name"`
OnFinish string `json:"onFinish"`
UndoPossible bool `json:"undoPossible"`
ScriptPath string `json:"scriptPath"`
Parameter []TaskParameter `json:"parameter"`
Feedback string `json:"feedback"`
}
type TaskParameter struct {
ParameterName string `json:"parameterName"`
Type string `json:"type"`
DisplayName string `json:"displayName"`
Global bool `json:"global"`
}
func addCategoryGroup(group Group) {
for index, categoryGroup := range CategoryGroups {
if categoryGroup.Category == group.Category {
CategoryGroups[index].Groups = append(CategoryGroups[index].Groups, group)
return
}
}
categoryGroup := CategoryGroup{
Category: group.Category,
}
categoryGroup.Groups = append(categoryGroup.Groups, group)
CategoryGroups = append(CategoryGroups, categoryGroup)
}
var root = "./groupTasks/groups/"
func ReadGroups() {
root := "./groupTasks/groups/"
entries, err := os.ReadDir(root)
if err != nil {
@ -94,7 +47,7 @@ func ReadGroups() {
return
}
var group Group
var group structs.Group
json.Unmarshal(content, &group)
@ -102,8 +55,210 @@ func ReadGroups() {
log.Info().Msgf("Group: %s", group)
addCategoryGroup(group)
cache.AddCategoryGroup(group)
}
}
}
}
const (
RunGroupTaskStartTypeNormal = 0
RunGroupTaskStartTypeTryAgain = 1
)
type RunGroupTaskArgs struct {
StartType uint8
GroupTaskId string
Category string
GroupId string
Step uint8
TaskStepId string
GlobalInputs []interface{}
}
func RunGroupTask(args RunGroupTaskArgs) {
log.Debug().Msgf("global input: %v", args.GlobalInputs)
categoryGroup := GetCategoryGroupTaskByCategoryAndGroupId(args.Category, args.GroupId)
log.Debug().Msgf("RunGroupTask %s", categoryGroup)
log.Debug().Msgf("script path %s", root+categoryGroup.Id+"/"+categoryGroup.Tasks[args.Step-1].ScriptPath)
groupTaskStep := structs.GroupTaskSteps{
GroupTasksId: args.GroupTaskId,
Step: args.Step,
Status: structs.GroupTasksStatusRunning,
StartedAt: time.Now(),
}
if args.StartType == RunGroupTaskStartTypeNormal {
groupTaskStep.Id = uuid.New().String()
database.DB.Create(&groupTaskStep)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdNewGroupTaskStep,
Body: groupTaskStep,
})
} else if args.StartType == RunGroupTaskStartTypeTryAgain {
groupTaskStep.Id = args.TaskStepId
database.DB.Model(&structs.GroupTaskSteps{}).Where("id = ?", groupTaskStep.Id).Updates(groupTaskStep)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdUpdateGroupTaskStep,
Body: groupTaskStep,
})
}
// set group task to running
database.DB.Model(&structs.GroupTasks{}).Where("id = ?", groupTaskStep.GroupTasksId).Updates(structs.GroupTasks{
Status: structs.GroupTasksStatusRunning,
})
var dbGroupTask structs.GroupTasks
database.DB.First(&dbGroupTask, "id = ?", groupTaskStep.GroupTasksId)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdUpdateGroupTask,
Body: dbGroupTask,
})
// execute script
cmd, err := exec.Command("python3", root+categoryGroup.Id+"/"+categoryGroup.Tasks[args.Step-1].ScriptPath).Output()
cmdLog := string(cmd)
if err != nil {
if exitErr, ok := err.(*exec.ExitError); ok {
exitCode := exitErr.ExitCode()
log.Error().Msgf("exit code %d", exitCode)
cmdLog += fmt.Sprintf("\nExit code: %v", exitCode)
}
log.Error().Msgf("error exec command %s", err.Error())
groupTaskStep.Status = structs.GroupTasksStatusFailed
} else {
groupTaskStep.Status = structs.GroupTasksStatusFinished
}
fmt.Println(cmdLog)
groupTaskStep.Log = cmdLog
groupTaskStep.EndedAt = time.Now()
/*
if args.StartType == RunGroupTaskStartTypeNormal {
database.DB.Model(&structs.GroupTaskSteps{}).Where("id = ?", groupTaskStep.Id).Updates(groupTaskStep)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdUpdateGroupTaskStep,
Body: groupTaskStep,
})
} else if args.StartType == RunGroupTaskStartTypeTryAgain {
log.Debug().Msgf("update", groupTaskStep.Id, groupTaskStep.Status)
database.DB.Model(&structs.GroupTaskSteps{}).Where("id = ?", groupTaskStep.Id).Updates(groupTaskStep)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdUpdateGroupTaskStep,
Body: groupTaskStep,
})
}*/
database.DB.Model(&structs.GroupTaskSteps{}).Where("id = ?", groupTaskStep.Id).Updates(groupTaskStep)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdUpdateGroupTaskStep,
Body: groupTaskStep,
})
log.Info().Msgf("run next task")
if int(args.Step) < len(categoryGroup.Tasks) {
if groupTaskStep.Status == structs.GroupTasksStatusFailed {
// set group task to failed
database.DB.Model(&structs.GroupTasks{}).Where("id = ?", groupTaskStep.GroupTasksId).Updates(structs.GroupTasks{
Status: structs.GroupTasksStatusFailed,
})
var dbGroupTask structs.GroupTasks
database.DB.First(&dbGroupTask, "id = ?", groupTaskStep.GroupTasksId)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdUpdateGroupTask,
Body: dbGroupTask,
})
} else {
args.StartType = RunGroupTaskStartTypeNormal
args.Step = args.Step + 1
database.DB.Model(&structs.GroupTasks{}).Where("id = ?", groupTaskStep.GroupTasksId).Updates(structs.GroupTasks{
CurrentTasksStep: args.Step,
})
var dbGroupTask structs.GroupTasks
database.DB.First(&dbGroupTask, "id = ?", groupTaskStep.GroupTasksId)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdUpdateGroupTask,
Body: dbGroupTask,
})
log.Debug().Msgf("RUN NEXT TASK %s", groupTaskStep)
RunGroupTask(args)
}
} else {
// set group task to finished
database.DB.Model(&structs.GroupTasks{}).Where("id = ?", groupTaskStep.GroupTasksId).Updates(structs.GroupTasks{
Status: structs.GroupTasksStatusFinished,
EndedAt: time.Now(),
})
var dbGroupTask structs.GroupTasks
database.DB.First(&dbGroupTask, "id = ?", groupTaskStep.GroupTasksId)
socketclients.BroadcastMessage(structs.SendSocketMessage{
Cmd: utils.SentCmdUpdateGroupTask,
Body: dbGroupTask,
})
log.Info().Msg("SET TO FINISHED")
}
}
func GetAllGroupTasks() []structs.GroupTasks {
var groupTasks []structs.GroupTasks
database.DB.Find(&groupTasks)
return groupTasks
}
func GetAllGroupTasksSteps() []structs.GroupTaskSteps {
var groupTaskStepsLogs []structs.GroupTaskSteps
database.DB.Find(&groupTaskStepsLogs)
return groupTaskStepsLogs
}
func GetCategoryGroupTaskByCategoryAndGroupId(category string, groupId string) structs.Group {
for _, categoryGroup := range cache.GetCategoryGroups() {
if categoryGroup.Category == category {
for _, group := range categoryGroup.Groups {
if group.Id == groupId {
return group
}
}
}
}
return structs.Group{}
}

36
modules/cache/categorygroup.go vendored Normal file
View File

@ -0,0 +1,36 @@
package cache
import (
"janex/admin-dashboard-backend/modules/structs"
"sync"
)
var CategoryGroups []structs.CategoryGroup
var cgMu sync.RWMutex
func AddCategoryGroup(group structs.Group) {
cgMu.Lock()
defer cgMu.Unlock()
for index, categoryGroup := range CategoryGroups {
if categoryGroup.Category == group.Category {
CategoryGroups[index].Groups = append(CategoryGroups[index].Groups, group)
return
}
}
categoryGroup := structs.CategoryGroup{
Category: group.Category,
}
categoryGroup.Groups = append(categoryGroup.Groups, group)
CategoryGroups = append(CategoryGroups, categoryGroup)
}
func GetCategoryGroups() []structs.CategoryGroup {
cgMu.RLock()
defer cgMu.RUnlock()
return CategoryGroups
}

View File

@ -31,7 +31,7 @@ func InitDatabase() {
db.AutoMigrate(&structs.User{})
db.AutoMigrate(&structs.UserSession{})
db.AutoMigrate(&structs.GroupTasks{})
db.AutoMigrate(&structs.GroupTaskStepsLogs{})
db.AutoMigrate(&structs.GroupTaskSteps{})
//createUser()
}

View File

@ -7,10 +7,11 @@ import (
// structure for database
const (
GroupTasksStatusFinished uint8 = 0
GroupTasksStatusRunning uint8 = 1
GroupTasksStatusFinished uint8 = 1
GroupTasksStatusRunning uint8 = 2
GroupTasksStatusCanceled uint8 = 3
GroupTasksStatusFailed uint8 = 4
GroupTasksInputRequired uint8 = 5
)
type GroupTasks struct {
@ -21,11 +22,14 @@ type GroupTasks struct {
CurrentTasksStep uint8
NumberOfSteps uint8
Status uint8
GlobalInputs string `gorm:"type:json"`
StartedAt time.Time
EndedAt time.Time
RememberId string `gorm:"-"` // used by the web client who requested this to open the modal after the backend sent the NewGroupTaskStarted message
}
type GroupTaskStepsLogs struct {
type GroupTaskSteps struct {
Id string
GroupTasksId string
Step uint8
Status uint8
@ -33,3 +37,40 @@ type GroupTaskStepsLogs struct {
StartedAt time.Time
EndedAt time.Time
}
// read from file structure
type CategoryGroup struct {
Category string `json:"category"`
Groups []Group `json:"groups"`
}
type Group struct {
Category string `json:"category"`
Id string `json:"id"`
Name string `json:"name"`
GlobalInputs []GlobalInputs `json:"globalInputs"`
Tasks []Task `json:"tasks"`
}
type GlobalInputs struct {
ParameterName string `json:"parameterName"`
Type string `json:"type"`
DisplayName string `json:"displayName"`
}
type Task struct {
Name string `json:"name"`
OnFinish string `json:"onFinish"`
UndoPossible bool `json:"undoPossible"`
ScriptPath string `json:"scriptPath"`
Parameter []TaskParameter `json:"parameter"`
Feedback string `json:"feedback"`
}
type TaskParameter struct {
ParameterName string `json:"parameterName"`
Type string `json:"type"`
DisplayName string `json:"displayName"`
Global bool `json:"global"`
}

View File

@ -3,7 +3,6 @@ package structs
import (
"encoding/json"
"errors"
"janex/admin-dashboard-backend/grouptasks"
"sync"
"github.com/gofiber/websocket/v2"
@ -79,9 +78,10 @@ func (socketClient *SocketClient) writeMessage(messageType int, message SendSock
}
type InitUserSocketConnection struct {
User UserData
CategoryGroups []grouptasks.CategoryGroup // config specific group tasks
GroupTasks []GroupTasks // in database saved group tasks
User UserData
CategoryGroups []CategoryGroup // config specific group tasks
GroupTasks []GroupTasks // in database saved group tasks
GroupTasksSteps []GroupTaskSteps
}
type UserData struct {

View File

@ -16,14 +16,18 @@ const (
// commands sent to web clients
const (
SentInitUserSocketConnection = 1
SentCmdUpdateConnectedUsers = 2
SentCmdNewGroupTaskStarted = 3
SentCmdInitUserSocketConnection = 1
SentCmdUpdateConnectedUsers = 2
SentCmdNewGroupTaskStarted = 3
SentCmdNewGroupTaskStep = 4
SentCmdUpdateGroupTaskStep = 5
SentCmdUpdateGroupTask = 6
)
// commands received from web clients
const (
ReceivedStartGroupTasks = 1
ReceivedCmdStartGroupTasks = 1
ReceivedCmdTaskFailedTryAgainRunTaskStep = 2
)
var (

View File

@ -46,14 +46,15 @@ func RunHub() {
database.DB.First(&user, "id = ?", userId)
newSocketClient.SendMessage(structs.SendSocketMessage{
Cmd: utils.SentInitUserSocketConnection,
Cmd: utils.SentCmdInitUserSocketConnection,
Body: structs.InitUserSocketConnection{
User: structs.UserData{
Username: user.Username,
Email: user.Email,
},
CategoryGroups: grouptasks.CategoryGroups,
GroupTasks: GetAllGroupTasks(),
CategoryGroups: cache.CategoryGroups,
GroupTasks: grouptasks.GetAllGroupTasks(),
GroupTasksSteps: grouptasks.GetAllGroupTasksSteps(),
},
})
@ -70,17 +71,37 @@ func RunHub() {
log.Info().Msgf("Received message: %s", receivedMessage, receivedMessage.Cmd)
switch receivedMessage.Cmd {
case utils.ReceivedStartGroupTasks:
log.Debug().Msgf("received start group tasks %s", receivedMessage.Body["id"], receivedMessage.Body["category"])
case utils.ReceivedCmdStartGroupTasks:
log.Debug().Msgf("received start group tasks %s", receivedMessage.Body)
category := receivedMessage.Body["category"].(string)
groupId := receivedMessage.Body["id"].(string)
globalInputs := receivedMessage.Body["globalInputs"]
var globalInputsJsonString string
if globalInputs != nil {
jsonString, err := json.Marshal(globalInputs)
if err != nil {
log.Error().Msgf("Failed to marshal global inputs %s", err)
continue
}
globalInputsJsonString = string(jsonString)
}
groupTaskId := uuid.New().String()
groupTasks := &structs.GroupTasks{
Id: uuid.New().String(),
Category: receivedMessage.Body["category"].(string),
GroupId: receivedMessage.Body["id"].(string),
Id: groupTaskId,
Category: category,
GroupId: groupId,
GroupName: receivedMessage.Body["groupName"].(string),
CurrentTasksStep: 1,
NumberOfSteps: uint8(receivedMessage.Body["numberOfSteps"].(float64)),
Status: structs.GroupTasksStatusRunning,
GlobalInputs: globalInputsJsonString,
StartedAt: time.Now(),
RememberId: receivedMessage.Body["rememberId"].(string),
}
@ -91,6 +112,26 @@ func RunHub() {
Cmd: utils.SentCmdNewGroupTaskStarted,
Body: groupTasks,
})
go grouptasks.RunGroupTask(grouptasks.RunGroupTaskArgs{
StartType: grouptasks.RunGroupTaskStartTypeNormal,
GroupTaskId: groupTaskId,
Category: category,
GroupId: groupId,
Step: 1,
TaskStepId: "",
GlobalInputs: globalInputs.([]interface{}),
})
break
case utils.ReceivedCmdTaskFailedTryAgainRunTaskStep:
go grouptasks.RunGroupTask(grouptasks.RunGroupTaskArgs{
StartType: grouptasks.RunGroupTaskStartTypeTryAgain,
GroupTaskId: receivedMessage.Body["groupTaskId"].(string),
Category: receivedMessage.Body["category"].(string),
GroupId: receivedMessage.Body["groupId"].(string),
Step: uint8(receivedMessage.Body["step"].(float64)),
TaskStepId: receivedMessage.Body["taskStepId"].(string),
})
break
default:
log.Error().Msgf("Received unknown message: %s", receivedMessage)
@ -104,11 +145,3 @@ func RunHub() {
}
}
}
func GetAllGroupTasks() []structs.GroupTasks {
var groupTasks []structs.GroupTasks
database.DB.Find(&groupTasks)
return groupTasks
}