From d1714e646f61e55ff7d69ca9bde9f81eca5bd16c Mon Sep 17 00:00:00 2001 From: Clay Gomera Date: Thu, 15 Aug 2024 09:41:43 -0400 Subject: [PATCH] First implementation of subprojects and subtasks, needs testing --- cmd/edit.go | 26 ++++++ cmd/list.go | 156 +++++++++++++++++++++++++-------- cmd/new.go | 75 +++++++++++----- cmd/remove.go | 26 ++++-- cmd/toggle.go | 13 ++- pkg/models/project.go | 1 + pkg/models/task.go | 1 + pkg/repository/project_repo.go | 68 +++++++++++--- pkg/repository/repository.go | 6 +- pkg/repository/task_repo.go | 50 +++++++++-- 10 files changed, 334 insertions(+), 88 deletions(-) diff --git a/cmd/edit.go b/cmd/edit.go index 3367f4a..e3a95a3 100644 --- a/cmd/edit.go +++ b/cmd/edit.go @@ -49,6 +49,8 @@ func init() { editCmd.Flags().StringP("name", "n", "", "New name") editCmd.Flags().StringP("description", "d", "", "New description") + editCmd.Flags().StringP("project", "p", "", "New parent project name or ID") + editCmd.Flags().StringP("task", "t", "", "New parent task ID for subtasks") editCmd.Flags().StringP("due", "D", "", "New due date for task (format: YYYY-MM-DD HH:MM)") editCmd.Flags(). IntP("priority", "r", 0, "New priority for task (1: High, 2: Medium, 3: Low, 4: None)") @@ -63,6 +65,7 @@ func editProject(cmd *cobra.Command, repo *repository.Repository, id int) { name, _ := cmd.Flags().GetString("name") description, _ := cmd.Flags().GetString("description") + parentProjectIdentifier, _ := cmd.Flags().GetString("project") if name != "" { project.Name = name @@ -70,6 +73,19 @@ func editProject(cmd *cobra.Command, repo *repository.Repository, id int) { if description != "" { project.Description = description } + if parentProjectIdentifier != "" { + if utils.IsNumeric(parentProjectIdentifier) { + parentID, _ := strconv.Atoi(parentProjectIdentifier) + project.ParentProjectId = &parentID + } else { + parentProject, err := repo.GetProjectByName(parentProjectIdentifier) + if err != nil || parentProject == nil { + fmt.Printf("Parent project '%s' not found.\n", parentProjectIdentifier) + return + } + project.ParentProjectId = &parentProject.ID + } + } err = repo.UpdateProject(project) if err != nil { @@ -91,6 +107,7 @@ func editTask(cmd *cobra.Command, repo *repository.Repository, id int) { description, _ := cmd.Flags().GetString("description") dueDateStr, _ := cmd.Flags().GetString("due") priority, _ := cmd.Flags().GetInt("priority") + parentTaskIdentifier, _ := cmd.Flags().GetString("task") if name != "" { task.Name = name @@ -113,6 +130,15 @@ func editTask(cmd *cobra.Command, repo *repository.Repository, id int) { fmt.Println("Invalid priority. Keeping the existing priority.") } } + if parentTaskIdentifier != "" { + if utils.IsNumeric(parentTaskIdentifier) { + parentID, _ := strconv.Atoi(parentTaskIdentifier) + task.ParentTaskId = &parentID + } else { + fmt.Println("Parent task must be identified by a numeric ID.") + return + } + } err = repo.UpdateTask(task) if err != nil { diff --git a/cmd/list.go b/cmd/list.go index aae0bc7..979d199 100644 --- a/cmd/list.go +++ b/cmd/list.go @@ -5,6 +5,7 @@ import ( "fmt" "os" "strconv" + "strings" "github.com/d4r1us-drk/clido/pkg/models" "github.com/d4r1us-drk/clido/pkg/repository" @@ -31,13 +32,14 @@ var listCmd = &cobra.Command{ defer repo.Close() outputJSON, _ := cmd.Flags().GetBool("json") + treeView, _ := cmd.Flags().GetBool("tree") switch args[0] { case "projects": - listProjects(repo, outputJSON) + listProjects(repo, outputJSON, treeView) case "tasks": projectFilter, _ := cmd.Flags().GetString("project") - listTasks(repo, projectFilter, outputJSON) + listTasks(repo, projectFilter, outputJSON, treeView) default: fmt.Println("Invalid option. Use 'list projects' or 'list tasks'.") } @@ -48,9 +50,10 @@ func init() { rootCmd.AddCommand(listCmd) listCmd.Flags().StringP("project", "p", "", "Filter tasks by project name or ID") listCmd.Flags().BoolP("json", "j", false, "Output list in JSON format") + listCmd.Flags().BoolP("tree", "t", false, "Display projects or tasks in a tree-like structure") } -func listProjects(repo *repository.Repository, outputJSON bool) { +func listProjects(repo *repository.Repository, outputJSON bool, treeView bool) { projects, err := repo.GetAllProjects() if err != nil { fmt.Printf("Error listing projects: %v\n", err) @@ -64,25 +67,14 @@ func listProjects(repo *repository.Repository, outputJSON bool) { return } fmt.Println(string(jsonData)) + } else if treeView { + printProjectTree(repo, projects, nil, 0) } else { - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{"ID", "Name", "Description"}) - table.SetRowLine(true) - - for _, project := range projects { - table.Append([]string{ - strconv.Itoa(project.ID), - utils.WrapText(project.Name, 30), - utils.WrapText(project.Description, 50), - }) - } - - fmt.Println("Projects:") - table.Render() + printProjectTable(repo, projects) } } -func listTasks(repo *repository.Repository, projectFilter string, outputJSON bool) { +func listTasks(repo *repository.Repository, projectFilter string, outputJSON bool, treeView bool) { var tasks []*models.Task var err error @@ -128,32 +120,120 @@ func listTasks(repo *repository.Repository, projectFilter string, outputJSON boo return } fmt.Println(string(jsonData)) + } else if treeView { + printTaskTree(repo, tasks, nil, 0) } else { - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader([]string{ - "ID", "Name", "Description", "Due Date", "Completed", "Past Due", "Priority", "Project", + printTaskTable(repo, tasks) + } +} + +func printProjectTable(repo *repository.Repository, projects []*models.Project) { + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{"ID", "Name", "Description", "Type", "Child Of"}) + table.SetRowLine(true) + + for _, project := range projects { + typeField := "Parent" + parentChildField := "None" + if project.ParentProjectId != nil { + typeField = "Child" + parentProject, _ := repo.GetProjectByID(*project.ParentProjectId) + if parentProject != nil { + parentChildField = parentProject.Name + } + } else { + subprojects, _ := repo.GetSubprojects(project.ID) + if len(subprojects) > 0 { + typeField = "Parent" + } + } + + table.Append([]string{ + strconv.Itoa(project.ID), + utils.WrapText(project.Name, 30), + utils.WrapText(project.Description, 50), + typeField, + parentChildField, }) - table.SetRowLine(true) + } - for _, task := range tasks { - project, _ := repo.GetProjectByID(task.ProjectID) - projectName := "" - if project != nil { - projectName = project.Name + fmt.Println("Projects:") + table.Render() +} + +func printTaskTable(repo *repository.Repository, tasks []*models.Task) { + table := tablewriter.NewWriter(os.Stdout) + table.SetHeader([]string{ + "ID", "Name", "Description", "Due Date", "Completed", "Past Due", "Priority", "Project", "Type", "Parent/Child Of", + }) + table.SetRowLine(true) + + for _, task := range tasks { + typeField := "Parent" + parentChildField := "None" + if task.ParentTaskId != nil { + typeField = "Child" + parentTask, _ := repo.GetTaskByID(*task.ParentTaskId) + if parentTask != nil { + parentChildField = parentTask.Name } + } else { + subtasks, _ := repo.GetSubtasks(task.ID) + if len(subtasks) > 0 { + typeField = "Parent" + } + } + + project, _ := repo.GetProjectByID(task.ProjectID) + projectName := "" + if project != nil { + projectName = project.Name + } + + table.Append([]string{ + strconv.Itoa(task.ID), + utils.WrapText(task.Name, 20), + utils.WrapText(task.Description, 30), + utils.FormatDate(task.DueDate), + fmt.Sprintf("%v", task.TaskCompleted), + utils.ColoredPastDue(task.DueDate, task.TaskCompleted), + utils.GetPriorityString(task.Priority), + utils.WrapText(projectName, 20), + typeField, + parentChildField, + }) + } - table.Append([]string{ - strconv.Itoa(task.ID), - utils.WrapText(task.Name, 20), - utils.WrapText(task.Description, 30), - utils.FormatDate(task.DueDate), - fmt.Sprintf("%v", task.TaskCompleted), - utils.ColoredPastDue(task.DueDate, task.TaskCompleted), - utils.GetPriorityString(task.Priority), - utils.WrapText(projectName, 20), - }) + table.Render() +} + +func printProjectTree(repo *repository.Repository, projects []*models.Project, parentID *int, level int) { + indent := strings.Repeat("│ ", level) + for i, project := range projects { + if (parentID == nil && project.ParentProjectId == nil) || (parentID != nil && project.ParentProjectId != nil && *project.ParentProjectId == *parentID) { + prefix := "├──" + if i == len(projects)-1 { + prefix = "└──" + } + fmt.Printf("%s%s %s (ID: %d)\n", indent, prefix, project.Name, project.ID) + printProjectTree(repo, projects, &project.ID, level+1) } + } +} - table.Render() +func printTaskTree(repo *repository.Repository, tasks []*models.Task, parentID *int, level int) { + indent := strings.Repeat("│ ", level) + for i, task := range tasks { + if (parentID == nil && task.ParentTaskId == nil) || (parentID != nil && task.ParentTaskId != nil && *task.ParentTaskId == *parentID) { + prefix := "├──" + if i == len(tasks)-1 { + prefix = "└──" + } + fmt.Printf("%s%s %s (ID: %d)\n", indent, prefix, task.Name, task.ID) + fmt.Printf("%s Description: %s\n", indent, task.Description) + fmt.Printf("%s Due Date: %s, Completed: %v, Priority: %s\n", + indent, utils.FormatDate(task.DueDate), task.TaskCompleted, utils.GetPriorityString(task.Priority)) + printTaskTree(repo, tasks, &task.ID, level+1) + } } } diff --git a/cmd/new.go b/cmd/new.go index 34e63ec..9a39cf9 100644 --- a/cmd/new.go +++ b/cmd/new.go @@ -44,7 +44,8 @@ func init() { newCmd.Flags().StringP("name", "n", "", "Name of the project or task") newCmd.Flags().StringP("description", "d", "", "Description of the project or task") - newCmd.Flags().StringP("project", "p", "", "Project name or ID for the task") + newCmd.Flags().StringP("project", "p", "", "Parent project name or ID for subprojects or tasks") + newCmd.Flags().StringP("task", "t", "", "Parent task ID for subtasks") newCmd.Flags().StringP("due", "D", "", "Due date for the task (format: YYYY-MM-DD HH:MM)") newCmd.Flags(). IntP("priority", "r", 4, "Priority of the task (1: High, 2: Medium, 3: Low, 4: None)") @@ -53,15 +54,32 @@ func init() { func createProject(cmd *cobra.Command, repo *repository.Repository) { name, _ := cmd.Flags().GetString("name") description, _ := cmd.Flags().GetString("description") + parentProjectIdentifier, _ := cmd.Flags().GetString("project") if name == "" { fmt.Println("Project name is required.") return } + var parentProjectID *int + if parentProjectIdentifier != "" { + if utils.IsNumeric(parentProjectIdentifier) { + id, _ := strconv.Atoi(parentProjectIdentifier) + parentProjectID = &id + } else { + parentProject, err := repo.GetProjectByName(parentProjectIdentifier) + if err != nil || parentProject == nil { + fmt.Printf("Parent project '%s' not found.\n", parentProjectIdentifier) + return + } + parentProjectID = &parentProject.ID + } + } + project := &models.Project{ - Name: name, - Description: description, + Name: name, + Description: description, + ParentProjectId: parentProjectID, } err := repo.CreateProject(project) @@ -77,27 +95,43 @@ func createTask(cmd *cobra.Command, repo *repository.Repository) { name, _ := cmd.Flags().GetString("name") description, _ := cmd.Flags().GetString("description") projectIdentifier, _ := cmd.Flags().GetString("project") + parentTaskIdentifier, _ := cmd.Flags().GetString("task") dueDateStr, _ := cmd.Flags().GetString("due") priority, _ := cmd.Flags().GetInt("priority") - if name == "" || projectIdentifier == "" { - fmt.Println("Task name and project identifier are required.") + if name == "" { + fmt.Println("Task name is required.") return } - var project *models.Project - var err error + var projectID int + var parentTaskID *int - if utils.IsNumeric(projectIdentifier) { - projectID, _ := strconv.Atoi(projectIdentifier) - project, err = repo.GetProjectByID(projectID) + if projectIdentifier != "" { + if utils.IsNumeric(projectIdentifier) { + id, _ := strconv.Atoi(projectIdentifier) + projectID = id + } else { + project, err := repo.GetProjectByName(projectIdentifier) + if err != nil || project == nil { + fmt.Printf("Project '%s' not found.\n", projectIdentifier) + return + } + projectID = project.ID + } } else { - project, err = repo.GetProjectByName(projectIdentifier) + fmt.Println("Task must be associated with a project.") + return } - if err != nil || project == nil { - fmt.Printf("Project '%s' not found.\n", projectIdentifier) - return + if parentTaskIdentifier != "" { + if utils.IsNumeric(parentTaskIdentifier) { + id, _ := strconv.Atoi(parentTaskIdentifier) + parentTaskID = &id + } else { + fmt.Println("Parent task must be identified by a numeric ID.") + return + } } var dueDate *time.Time @@ -111,14 +145,15 @@ func createTask(cmd *cobra.Command, repo *repository.Repository) { } task := &models.Task{ - Name: name, - Description: description, - ProjectID: project.ID, - DueDate: dueDate, - Priority: priority, + Name: name, + Description: description, + ProjectID: projectID, + DueDate: dueDate, + Priority: priority, + ParentTaskId: parentTaskID, } - err = repo.CreateTask(task) + err := repo.CreateTask(task) if err != nil { fmt.Printf("Error creating task: %v\n", err) return diff --git a/cmd/remove.go b/cmd/remove.go index 23c050d..83ba7cb 100644 --- a/cmd/remove.go +++ b/cmd/remove.go @@ -10,8 +10,8 @@ import ( var removeCmd = &cobra.Command{ Use: "remove [project|task] ", - Short: "Remove a project or task", - Long: `Remove an existing project or task identified by its ID.`, + Short: "Remove a project or task along with all its subprojects or subtasks", + Long: `Remove an existing project or task identified by its ID. This will also remove all associated subprojects or subtasks.`, Run: func(cmd *cobra.Command, args []string) { if len(args) < 2 { fmt.Println("Insufficient arguments. Use 'remove project ' or 'remove task '.") @@ -47,33 +47,43 @@ func init() { } func removeProject(repo *repository.Repository, id int) { - project, err := repo.GetProjectByID(id) + // First remove all subprojects + subprojects, err := repo.GetSubprojects(id) if err != nil { - fmt.Printf("Error retrieving project: %v\n", err) + fmt.Printf("Error retrieving subprojects: %v\n", err) return } + for _, subproject := range subprojects { + removeProject(repo, subproject.ID) + } + // Now remove the parent project err = repo.DeleteProject(id) if err != nil { fmt.Printf("Error removing project: %v\n", err) return } - fmt.Printf("Project '%s' (ID: %d) removed successfully.\n", project.Name, id) + fmt.Printf("Project (ID: %d) and all its subprojects removed successfully.\n", id) } func removeTask(repo *repository.Repository, id int) { - task, err := repo.GetTaskByID(id) + // First remove all subtasks + subtasks, err := repo.GetSubtasks(id) if err != nil { - fmt.Printf("Error retrieving task: %v\n", err) + fmt.Printf("Error retrieving subtasks: %v\n", err) return } + for _, subtask := range subtasks { + removeTask(repo, subtask.ID) + } + // Now remove the parent task err = repo.DeleteTask(id) if err != nil { fmt.Printf("Error removing task: %v\n", err) return } - fmt.Printf("Task '%s' (ID: %d) removed successfully.\n", task.Name, id) + fmt.Printf("Task (ID: %d) and all its subtasks removed successfully.\n", id) } diff --git a/cmd/toggle.go b/cmd/toggle.go index 48820e0..b1198cd 100644 --- a/cmd/toggle.go +++ b/cmd/toggle.go @@ -32,15 +32,17 @@ var toggleCmd = &cobra.Command{ return } - toggleTask(repo, id) + recursive, _ := cmd.Flags().GetBool("recursive") + toggleTask(repo, id, recursive) }, } func init() { rootCmd.AddCommand(toggleCmd) + toggleCmd.Flags().BoolP("recursive", "r", false, "Recursively toggle subtasks") } -func toggleTask(repo *repository.Repository, id int) { +func toggleTask(repo *repository.Repository, id int, recursive bool) { task, err := repo.GetTaskByID(id) if err != nil { fmt.Printf("Error retrieving task: %v\n", err) @@ -68,4 +70,11 @@ func toggleTask(repo *repository.Repository, id int) { } fmt.Printf("Task '%s' (ID: %d) marked as %s.\n", task.Name, id, status) + + if recursive { + subtasks, _ := repo.GetSubtasks(id) + for _, subtask := range subtasks { + toggleTask(repo, subtask.ID, recursive) + } + } } diff --git a/pkg/models/project.go b/pkg/models/project.go index 351b562..8d00b34 100644 --- a/pkg/models/project.go +++ b/pkg/models/project.go @@ -8,4 +8,5 @@ type Project struct { Description string CreationDate time.Time LastModifiedDate time.Time + ParentProjectId *int } diff --git a/pkg/models/task.go b/pkg/models/task.go index 5b230de..a634515 100644 --- a/pkg/models/task.go +++ b/pkg/models/task.go @@ -13,4 +13,5 @@ type Task struct { CreationDate time.Time LastUpdatedDate time.Time Priority int + ParentTaskId *int } diff --git a/pkg/repository/project_repo.go b/pkg/repository/project_repo.go index d624cdd..11d5e10 100644 --- a/pkg/repository/project_repo.go +++ b/pkg/repository/project_repo.go @@ -9,29 +9,40 @@ import ( func (r *Repository) CreateProject(project *models.Project) error { project.CreationDate = time.Now() project.LastModifiedDate = time.Now() - result, err := r.db.Exec( - `INSERT INTO Projects (Name, Description, CreationDate, LastModifiedDate) VALUES (?, ?, ?, ?)`, + + // Find the lowest unused ID + var id int + err := r.db.QueryRow(` + SELECT COALESCE(MIN(p1.ID + 1), 1) + FROM Projects p1 + LEFT JOIN Projects p2 ON p1.ID + 1 = p2.ID + WHERE p2.ID IS NULL`).Scan(&id) + if err != nil { + return err + } + + // Insert the project with the found ID + _, err = r.db.Exec( + `INSERT INTO Projects (ID, Name, Description, CreationDate, LastModifiedDate, ParentProjectId) VALUES (?, ?, ?, ?, ?, ?)`, + id, project.Name, project.Description, project.CreationDate, project.LastModifiedDate, + project.ParentProjectId, ) if err != nil { return err } - id, err := result.LastInsertId() - if err != nil { - return err - } - project.ID = int(id) + project.ID = id return nil } func (r *Repository) GetProjectByID(id int) (*models.Project, error) { project := &models.Project{} - err := r.db.QueryRow(`SELECT ID, Name, Description, CreationDate, LastModifiedDate FROM Projects WHERE ID = ?`, id). - Scan(&project.ID, &project.Name, &project.Description, &project.CreationDate, &project.LastModifiedDate) + err := r.db.QueryRow(`SELECT ID, Name, Description, CreationDate, LastModifiedDate, ParentProjectId FROM Projects WHERE ID = ?`, id). + Scan(&project.ID, &project.Name, &project.Description, &project.CreationDate, &project.LastModifiedDate, &project.ParentProjectId) if err != nil { return nil, err } @@ -40,8 +51,8 @@ func (r *Repository) GetProjectByID(id int) (*models.Project, error) { func (r *Repository) GetProjectByName(name string) (*models.Project, error) { project := &models.Project{} - err := r.db.QueryRow(`SELECT ID, Name, Description, CreationDate, LastModifiedDate FROM Projects WHERE Name = ?`, name). - Scan(&project.ID, &project.Name, &project.Description, &project.CreationDate, &project.LastModifiedDate) + err := r.db.QueryRow(`SELECT ID, Name, Description, CreationDate, LastModifiedDate, ParentProjectId FROM Projects WHERE Name = ?`, name). + Scan(&project.ID, &project.Name, &project.Description, &project.CreationDate, &project.LastModifiedDate, &project.ParentProjectId) if err != nil { return nil, err } @@ -50,7 +61,36 @@ func (r *Repository) GetProjectByName(name string) (*models.Project, error) { func (r *Repository) GetAllProjects() ([]*models.Project, error) { rows, err := r.db.Query( - `SELECT ID, Name, Description, CreationDate, LastModifiedDate FROM Projects`, + `SELECT ID, Name, Description, CreationDate, LastModifiedDate, ParentProjectId FROM Projects`, + ) + if err != nil { + return nil, err + } + defer rows.Close() + + var projects []*models.Project + for rows.Next() { + project := &models.Project{} + err := rows.Scan( + &project.ID, + &project.Name, + &project.Description, + &project.CreationDate, + &project.LastModifiedDate, + &project.ParentProjectId, + ) + if err != nil { + return nil, err + } + projects = append(projects, project) + } + return projects, nil +} + +func (r *Repository) GetSubprojects(parentProjectID int) ([]*models.Project, error) { + rows, err := r.db.Query( + `SELECT ID, Name, Description, CreationDate, LastModifiedDate, ParentProjectId FROM Projects WHERE ParentProjectId = ?`, + parentProjectID, ) if err != nil { return nil, err @@ -66,6 +106,7 @@ func (r *Repository) GetAllProjects() ([]*models.Project, error) { &project.Description, &project.CreationDate, &project.LastModifiedDate, + &project.ParentProjectId, ) if err != nil { return nil, err @@ -78,10 +119,11 @@ func (r *Repository) GetAllProjects() ([]*models.Project, error) { func (r *Repository) UpdateProject(project *models.Project) error { project.LastModifiedDate = time.Now() _, err := r.db.Exec( - `UPDATE Projects SET Name = ?, Description = ?, LastModifiedDate = ? WHERE ID = ?`, + `UPDATE Projects SET Name = ?, Description = ?, LastModifiedDate = ?, ParentProjectId = ? WHERE ID = ?`, project.Name, project.Description, project.LastModifiedDate, + project.ParentProjectId, project.ID, ) return err diff --git a/pkg/repository/repository.go b/pkg/repository/repository.go index 06eb028..a0228cf 100644 --- a/pkg/repository/repository.go +++ b/pkg/repository/repository.go @@ -57,7 +57,9 @@ func (r *Repository) init() error { Name TEXT NOT NULL UNIQUE, Description TEXT, CreationDate DATETIME NOT NULL, - LastModifiedDate DATETIME NOT NULL + LastModifiedDate DATETIME NOT NULL, + ParentProjectId INTEGER, + FOREIGN KEY (ParentProjectID) REFERENCES Projects(ID) );` createTaskTable := ` @@ -72,6 +74,8 @@ func (r *Repository) init() error { CreationDate DATETIME NOT NULL, LastUpdatedDate DATETIME NOT NULL, Priority INTEGER NOT NULL DEFAULT 4, + ParentTaskId INTEGER, + FOREIGN KEY (ParentTaskId) REFERENCES Tasks(ID), FOREIGN KEY (ProjectID) REFERENCES Projects(ID) );` diff --git a/pkg/repository/task_repo.go b/pkg/repository/task_repo.go index 0a56392..f50e57b 100644 --- a/pkg/repository/task_repo.go +++ b/pkg/repository/task_repo.go @@ -23,7 +23,7 @@ func (r *Repository) CreateTask(task *models.Task) error { // Insert the task with the found ID _, err = r.db.Exec( - `INSERT INTO Tasks (ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, + `INSERT INTO Tasks (ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority, ParentTaskId) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`, id, task.Name, task.Description, @@ -34,6 +34,7 @@ func (r *Repository) CreateTask(task *models.Task) error { task.CreationDate, task.LastUpdatedDate, task.Priority, + task.ParentTaskId, ) if err != nil { return err @@ -45,8 +46,8 @@ func (r *Repository) CreateTask(task *models.Task) error { func (r *Repository) GetTaskByID(id int) (*models.Task, error) { task := &models.Task{} - err := r.db.QueryRow(`SELECT ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority FROM Tasks WHERE ID = ?`, id). - Scan(&task.ID, &task.Name, &task.Description, &task.ProjectID, &task.TaskCompleted, &task.DueDate, &task.CompletionDate, &task.CreationDate, &task.LastUpdatedDate, &task.Priority) + err := r.db.QueryRow(`SELECT ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority, ParentTaskId FROM Tasks WHERE ID = ?`, id). + Scan(&task.ID, &task.Name, &task.Description, &task.ProjectID, &task.TaskCompleted, &task.DueDate, &task.CompletionDate, &task.CreationDate, &task.LastUpdatedDate, &task.Priority, &task.ParentTaskId) if err != nil { return nil, err } @@ -55,7 +56,7 @@ func (r *Repository) GetTaskByID(id int) (*models.Task, error) { func (r *Repository) GetAllTasks() ([]*models.Task, error) { rows, err := r.db.Query( - `SELECT ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority FROM Tasks`, + `SELECT ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority, ParentTaskId FROM Tasks`, ) if err != nil { return nil, err @@ -76,6 +77,7 @@ func (r *Repository) GetAllTasks() ([]*models.Task, error) { &task.CreationDate, &task.LastUpdatedDate, &task.Priority, + &task.ParentTaskId, ) if err != nil { return nil, err @@ -87,7 +89,7 @@ func (r *Repository) GetAllTasks() ([]*models.Task, error) { func (r *Repository) GetTasksByProjectID(projectID int) ([]*models.Task, error) { rows, err := r.db.Query( - `SELECT ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority FROM Tasks WHERE ProjectID = ?`, + `SELECT ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority, ParentTaskId FROM Tasks WHERE ProjectID = ?`, projectID, ) if err != nil { @@ -109,6 +111,41 @@ func (r *Repository) GetTasksByProjectID(projectID int) ([]*models.Task, error) &task.CreationDate, &task.LastUpdatedDate, &task.Priority, + &task.ParentTaskId, + ) + if err != nil { + return nil, err + } + tasks = append(tasks, task) + } + return tasks, nil +} + +func (r *Repository) GetSubtasks(parentTaskID int) ([]*models.Task, error) { + rows, err := r.db.Query( + `SELECT ID, Name, Description, ProjectID, TaskCompleted, DueDate, CompletionDate, CreationDate, LastUpdatedDate, Priority, ParentTaskId FROM Tasks WHERE ParentTaskId = ?`, + parentTaskID, + ) + if err != nil { + return nil, err + } + defer rows.Close() + + var tasks []*models.Task + for rows.Next() { + task := &models.Task{} + err := rows.Scan( + &task.ID, + &task.Name, + &task.Description, + &task.ProjectID, + &task.TaskCompleted, + &task.DueDate, + &task.CompletionDate, + &task.CreationDate, + &task.LastUpdatedDate, + &task.Priority, + &task.ParentTaskId, ) if err != nil { return nil, err @@ -121,7 +158,7 @@ func (r *Repository) GetTasksByProjectID(projectID int) ([]*models.Task, error) func (r *Repository) UpdateTask(task *models.Task) error { task.LastUpdatedDate = time.Now() _, err := r.db.Exec( - `UPDATE Tasks SET Name = ?, Description = ?, ProjectID = ?, TaskCompleted = ?, DueDate = ?, CompletionDate = ?, LastUpdatedDate = ?, Priority = ? WHERE ID = ?`, + `UPDATE Tasks SET Name = ?, Description = ?, ProjectID = ?, TaskCompleted = ?, DueDate = ?, CompletionDate = ?, LastUpdatedDate = ?, Priority = ?, ParentTaskId = ? WHERE ID = ?`, task.Name, task.Description, task.ProjectID, @@ -130,6 +167,7 @@ func (r *Repository) UpdateTask(task *models.Task) error { task.CompletionDate, task.LastUpdatedDate, task.Priority, + task.ParentTaskId, task.ID, ) return err