diff --git a/backend/db/migrations/init.sql b/backend/db/migrations/init.sql index 132f1b8..dd0d026 100644 --- a/backend/db/migrations/init.sql +++ b/backend/db/migrations/init.sql @@ -57,7 +57,7 @@ CREATE TABLE IF NOT EXISTS task ( start_date timestamp, end_date timestamp, notes varchar, - repeating BOOLEAN, + repeating BOOLEAN DEFAULT FALSE, repeating_interval varchar, repeating_end_date timestamp, task_status task_status NOT NULL, diff --git a/backend/docs/docs.go b/backend/docs/docs.go index cfa17f4..5878a3a 100644 --- a/backend/docs/docs.go +++ b/backend/docs/docs.go @@ -64,6 +64,43 @@ const docTemplate = `{ } } }, + "/tasks/assigned": { + "get": { + "description": "get tasks assigned to given users", + "tags": [ + "tasks" + ], + "summary": "Get Tasks Assigned To Given Users", + "parameters": [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "name": "userIDs", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Task" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + } + } + } + }, "/tasks/filtered": { "get": { "description": "get filtered tasks", @@ -122,7 +159,7 @@ const docTemplate = `{ } } }, - "/tasks/{tid}/assignees": { + "/tasks/{tid}/assign": { "post": { "description": "assign users to task", "tags": [ diff --git a/backend/docs/swagger.json b/backend/docs/swagger.json index 70fe58b..29d37ff 100644 --- a/backend/docs/swagger.json +++ b/backend/docs/swagger.json @@ -57,6 +57,43 @@ } } }, + "/tasks/assigned": { + "get": { + "description": "get tasks assigned to given users", + "tags": [ + "tasks" + ], + "summary": "Get Tasks Assigned To Given Users", + "parameters": [ + { + "type": "array", + "items": { + "type": "string" + }, + "collectionFormat": "csv", + "name": "userIDs", + "in": "query" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/models.Task" + } + } + }, + "400": { + "description": "Bad Request", + "schema": { + "type": "string" + } + } + } + } + }, "/tasks/filtered": { "get": { "description": "get filtered tasks", @@ -115,7 +152,7 @@ } } }, - "/tasks/{tid}/assignees": { + "/tasks/{tid}/assign": { "post": { "description": "assign users to task", "tags": [ diff --git a/backend/docs/swagger.yaml b/backend/docs/swagger.yaml index 7cc0f19..66b8caf 100644 --- a/backend/docs/swagger.yaml +++ b/backend/docs/swagger.yaml @@ -115,7 +115,7 @@ paths: summary: Get All Meds tags: - medications - /tasks/{tid}/assignees: + /tasks/{tid}/assign: post: description: assign users to task parameters: @@ -173,6 +173,30 @@ paths: summary: Remove Users From Task tags: - tasks + /tasks/assigned: + get: + description: get tasks assigned to given users + parameters: + - collectionFormat: csv + in: query + items: + type: string + name: userIDs + type: array + responses: + "200": + description: OK + schema: + items: + $ref: '#/definitions/models.Task' + type: array + "400": + description: Bad Request + schema: + type: string + summary: Get Tasks Assigned To Given Users + tags: + - tasks /tasks/filtered: get: description: get filtered tasks diff --git a/backend/main.go b/backend/main.go index ca43b3e..661c2e3 100644 --- a/backend/main.go +++ b/backend/main.go @@ -6,6 +6,7 @@ import ( _ "carewallet/docs" "carewallet/schema/files" "carewallet/schema/medication" + "carewallet/schema/tasks" "fmt" "os" @@ -39,6 +40,7 @@ func main() { { medication.GetMedicationGroup(v1, &medication.PgModel{Conn: conn}) files.GetFileGroup(v1, &files.PgModel{Conn: conn}) + tasks.TaskGroup(v1, &tasks.PgModel{Conn: conn}) } if enviroment == configuration.EnvironmentLocal { diff --git a/backend/schema/tasks/routes.go b/backend/schema/tasks/routes.go index 3368594..80ec410 100644 --- a/backend/schema/tasks/routes.go +++ b/backend/schema/tasks/routes.go @@ -1,7 +1,9 @@ package tasks import ( + "fmt" "net/http" + "strings" "github.com/gin-gonic/gin" "github.com/jackc/pgx" @@ -18,6 +20,7 @@ func TaskGroup(v1 *gin.RouterGroup, c *PgModel) *gin.RouterGroup { tasks.GET("/filtered", c.GetFilteredTasks) tasks.POST("/:tid/assign", c.AssignUsersToTask) tasks.DELETE("/:tid/remove", c.RemoveUsersFromTask) + tasks.GET("/assigned", c.GetTasksByAssignedUsers) } return tasks @@ -45,12 +48,12 @@ type TaskQuery struct { // @router /tasks/filtered [get] func (pg *PgModel) GetFilteredTasks(c *gin.Context) { filterQuery := TaskQuery{ - GroupID: c.Query("GroupID"), - CreatedBy: c.Query("CreatedBy"), - TaskStatus: c.Query("TaskStatus"), - TaskType: c.Query("TaskType"), - StartDate: c.Query("StartDate"), - EndDate: c.Query("EndDate"), + GroupID: c.Query("groupID"), + CreatedBy: c.Query("createdBy"), + TaskStatus: c.Query("taskStatus"), + TaskType: c.Query("taskType"), + StartDate: c.Query("startDate"), + EndDate: c.Query("endDate"), } tasks, err := GetTasksByQueryFromDB(pg.Conn, filterQuery) @@ -79,11 +82,12 @@ type Assignment struct { // // @success 200 {array} models.TaskUser // @failure 400 {object} string -// @router /tasks/{tid}/assignees [post] +// @router /tasks/{tid}/assign [post] func (pg *PgModel) AssignUsersToTask(c *gin.Context) { var requestBody Assignment if err := c.BindJSON(&requestBody); err != nil { + print(err.Error()) c.JSON(http.StatusBadRequest, err.Error()) return } @@ -131,3 +135,35 @@ func (pg *PgModel) RemoveUsersFromTask(c *gin.Context) { c.JSON(http.StatusOK, removedUsers) } + +type AssignedQuery struct { + UserIDs []string `query:"userIDs"` +} + +// GetTasksByAssignedUsers godoc +// +// @summary Get Tasks Assigned To Given Users +// @description get tasks assigned to given users +// @tags tasks +// +// @param _ query AssignedQuery true "Users to return tasks for" +// +// @success 200 {array} models.Task +// @failure 400 {object} string +// @router /tasks/assigned [get] +func (pg *PgModel) GetTasksByAssignedUsers(c *gin.Context) { + userIDs := c.Query("userIDs") + assignedQuery := AssignedQuery{ + UserIDs: strings.Split(userIDs, ","), + } + fmt.Println(assignedQuery.UserIDs) + + tasks, err := GetTasksByAssignedFromDB(pg.Conn, assignedQuery.UserIDs) + + if err != nil { + c.JSON(http.StatusBadRequest, err.Error()) + return + } + + c.JSON(http.StatusOK, tasks) +} diff --git a/backend/schema/tasks/task_test.go b/backend/schema/tasks/task_test.go index b8c3f30..2a9048c 100644 --- a/backend/schema/tasks/task_test.go +++ b/backend/schema/tasks/task_test.go @@ -51,12 +51,12 @@ func TestTaskGroup(t *testing.T) { w := httptest.NewRecorder() query := url.Values{} - query.Set("GroupID", getRequest.GroupID) - query.Set("CreatedBy", getRequest.CreatedBy) - query.Set("TaskStatus", getRequest.TaskStatus) - query.Set("TaskType", getRequest.TaskType) - query.Set("StartDate", getRequest.StartDate) - query.Set("EndDate", getRequest.EndDate) + query.Set("groupID", getRequest.GroupID) + query.Set("createdBy", getRequest.CreatedBy) + query.Set("taskStatus", getRequest.TaskStatus) + query.Set("taskType", getRequest.TaskType) + query.Set("startDate", getRequest.StartDate) + query.Set("endDate", getRequest.EndDate) req, _ := http.NewRequest("GET", "/tasks/filtered?"+query.Encode(), nil) router.ServeHTTP(w, req) @@ -96,32 +96,26 @@ func TestTaskGroup(t *testing.T) { } }) - t.Run("TestAssignUsersToTask", func(t *testing.T) { - type AssignRequest struct { - UserIDs []string `json:"userIDs"` - Assigner string `json:"assigner"` - } - - assignRequest := AssignRequest{ - UserIDs: []string{"user3"}, - Assigner: "user3", + t.Run("TestRemoveUsersFromTask", func(t *testing.T) { + var removeRequest = Removal{ + UserIDs: []string{"user1"}, } - userIdsJSON, err := json.Marshal(assignRequest) + requestJSON, err := json.Marshal(removeRequest) if err != nil { - t.Error("Failed to marshal userIds to JSON") + t.Error("Failed to marshal remove request to JSON") } w := httptest.NewRecorder() - req, _ := http.NewRequest("POST", "/tasks/4/assign", bytes.NewBuffer(userIdsJSON)) + req, _ := http.NewRequest("DELETE", "/tasks/1/remove", bytes.NewBuffer(requestJSON)) router.ServeHTTP(w, req) if http.StatusOK != w.Code { - t.Error("Failed to assign users to task.") + t.Error("Failed to remove users from task.") } - var responseTaskUsers []models.TaskUser - err = json.Unmarshal(w.Body.Bytes(), &responseTaskUsers) + var removeResponse []models.TaskUser + err = json.Unmarshal(w.Body.Bytes(), &removeResponse) if err != nil { t.Error("Failed to unmarshal json") @@ -129,40 +123,37 @@ func TestTaskGroup(t *testing.T) { expectedTaskUsers := []models.TaskUser{ { - TaskID: 4, - UserID: "user3", + TaskID: 1, + UserID: "user1", }, } - if !reflect.DeepEqual(expectedTaskUsers, responseTaskUsers) { + if !reflect.DeepEqual(expectedTaskUsers, removeResponse) { t.Error("Result was not correct") } }) - t.Run("TestRemoveUsersFromTask", func(t *testing.T) { - type RemoveRequest struct { - UserIDs []string `json:"userIDs"` - } - - removeRequest := RemoveRequest{ - UserIDs: []string{"user2"}, + t.Run("TestAssignUsersToTask", func(t *testing.T) { + assignRequest := Assignment{ + UserIDs: []string{"user4"}, + Assigner: "user1", } - userIdsJSON, err := json.Marshal(removeRequest) + requestJSON, err := json.Marshal(assignRequest) if err != nil { - t.Error("Failed to marshal userIds to JSON") + t.Error("Failed to marshal assign request to JSON") } w := httptest.NewRecorder() - req, _ := http.NewRequest("DELETE", "/tasks/4/remove", bytes.NewBuffer(userIdsJSON)) + req, _ := http.NewRequest("POST", "/tasks/2/assign", bytes.NewBuffer(requestJSON)) router.ServeHTTP(w, req) if http.StatusOK != w.Code { - t.Error("Failed to remove users from task.") + t.Error("Failed to assign users to task.") } - var responseTaskUsers []models.TaskUser - err = json.Unmarshal(w.Body.Bytes(), &responseTaskUsers) + var assignResponse []models.TaskUser + err = json.Unmarshal(w.Body.Bytes(), &assignResponse) if err != nil { t.Error("Failed to unmarshal json") @@ -170,14 +161,49 @@ func TestTaskGroup(t *testing.T) { expectedTaskUsers := []models.TaskUser{ { - TaskID: 4, - UserID: "user2", + TaskID: 2, + UserID: "user4", }, } - if !reflect.DeepEqual(expectedTaskUsers, responseTaskUsers) { + if !reflect.DeepEqual(expectedTaskUsers, assignResponse) { t.Error("Result was not correct") } }) + t.Run("TestGetTasksByAssigned", func(t *testing.T) { + w := httptest.NewRecorder() + req, _ := http.NewRequest("GET", "/tasks/assigned?userIDs=user2", nil) + router.ServeHTTP(w, req) + + if http.StatusOK != w.Code { + t.Error("Failed to retrieve tasks by assigned user.") + } + + var responseTasks []models.Task + err = json.Unmarshal(w.Body.Bytes(), &responseTasks) + + if err != nil { + t.Error("Failed to unmarshal json") + } + + note := "Refill water pitcher" + expectedTasks := []models.Task{ + { + TaskID: 4, + GroupID: 4, + CreatedBy: "user1", + CreatedDate: time.Date(2006, 1, 2, 15, 4, 5, 0, time.UTC), + Notes: ¬e, + TaskStatus: "COMPLETE", + TaskType: "other", + }, + } + + fmt.Println("Expected: ", expectedTasks) + fmt.Println("Response: ", responseTasks) + if !reflect.DeepEqual(expectedTasks, responseTasks) { + t.Error("Result was not correct") + } + }) } diff --git a/backend/schema/tasks/transactions.go b/backend/schema/tasks/transactions.go index 5386f38..8be8e97 100644 --- a/backend/schema/tasks/transactions.go +++ b/backend/schema/tasks/transactions.go @@ -17,6 +17,7 @@ func GetTasksByQueryFromDB(pool *pgx.Conn, filterQuery TaskQuery) ([]models.Task filterQuery.TaskType, filterQuery.StartDate, filterQuery.EndDate} + field_names := []string{"group_id", "created_by", "task_status", "task_type", "start_date", "end_date"} var query string var args []interface{} @@ -31,11 +32,10 @@ func GetTasksByQueryFromDB(pool *pgx.Conn, filterQuery TaskQuery) ([]models.Task } } - rows, err := pool.Query("SELECT task_id, group_id, created_by, created_date, task_status, task_type FROM task WHERE "+query+";", args...) + rows, err := pool.Query("SELECT task_id, group_id, created_by, created_date, task_status, task_type FROM task WHERE "+query, args...) if err != nil { print(err, "error selecting tasks by query") - return nil, err } @@ -49,7 +49,6 @@ func GetTasksByQueryFromDB(pool *pgx.Conn, filterQuery TaskQuery) ([]models.Task if err != nil { print(err, "error scanning tasks by query") - return nil, err } @@ -62,24 +61,20 @@ func GetTasksByQueryFromDB(pool *pgx.Conn, filterQuery TaskQuery) ([]models.Task func AssignUsersToTaskInDB(pool *pgx.Conn, users []string, taskID string, assigner string) ([]models.TaskUser, error) { task_id, err := strconv.Atoi(taskID) if err != nil { - print(err, "error converting task ID to int") return nil, err } var assignedUsers []models.TaskUser for _, user := range users { - print(task_id, " ", user) _, err := pool.Exec("INSERT INTO task_assignees (task_id, user_id, assignment_status, assigned_by, assigned_date) VALUES ($1, $2, $3, $4, $5);", task_id, user, "NOTIFIED", assigner, time.Now()) if err != nil { - print(err, "error inserting users into task_assignees") - + print(err.Error(), "error inserting users into task_assignees") return nil, err } assignedUsers = append(assignedUsers, models.TaskUser{TaskID: task_id, UserID: user}) - fmt.Println(assignedUsers) } return assignedUsers, nil @@ -95,12 +90,10 @@ func RemoveUsersFromTaskInDB(pool *pgx.Conn, users []string, taskID string) ([]m var removedUsers []models.TaskUser for _, user := range users { - // Check if the user ID and task ID exist in the table var exists int err := pool.QueryRow("SELECT 1 FROM task_assignees WHERE task_id = $1 AND user_id = $2 LIMIT 1;", task_id, user).Scan(&exists) if err != nil { if err == pgx.ErrNoRows { - // User ID or task ID does not exist, return an error return nil, fmt.Errorf("user not assigned to task") } print(err, "error checking if user and task exist in task_assignees") @@ -118,3 +111,45 @@ func RemoveUsersFromTaskInDB(pool *pgx.Conn, users []string, taskID string) ([]m return removedUsers, nil } + +func GetTasksByAssignedFromDB(pool *pgx.Conn, userIDs []string) ([]models.Task, error) { + var task_ids []int + var tasks []models.Task + + // Get all task IDs assigned to the user + for _, userID := range userIDs { + fmt.Println(userID) + taskIDs, err := pool.Query("SELECT task_id FROM task_assignees WHERE user_id = $1;", userID) + if err != nil { + print(err, "error selecting task assignees") + return nil, err + } + defer taskIDs.Close() + + for taskIDs.Next() { + var task_id int + + err := taskIDs.Scan(&task_id) + if err != nil { + print(err, "error scanning task ID") + return nil, err + } + fmt.Println(task_id) + task_ids = append(task_ids, task_id) + } + } + + // Get all tasks by task ID + var task models.Task + for _, task_id := range task_ids { + err := pool.QueryRow("SELECT * FROM task WHERE task_id = $1;", task_id).Scan(&task.TaskID, &task.GroupID, &task.CreatedBy, &task.CreatedDate, &task.StartDate, &task.EndDate, &task.Notes, &task.Repeating, &task.RepeatingInterval, &task.RepeatingEndDate, &task.TaskStatus, &task.TaskType, &task.TaskInfo) + if err != nil { + print(err, "error querying task by ID") + return nil, err + } + + tasks = append(tasks, task) + } + + return tasks, nil +}