From 13f7678a9fd092ac20eec10e4d2196f5bd1ae107 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Paul=20B=C3=BCtow?= <1224732+snonux@users.noreply.github.com> Date: Fri, 20 Jun 2025 09:40:56 +0300 Subject: Add cell navigation, editor hotkey, stats and filter --- internal/task/stats.go | 40 ++++++++++++++++++++++++++++++++++++++++ internal/task/stats_test.go | 28 ++++++++++++++++++++++++++++ internal/task/task.go | 8 ++++++-- 3 files changed, 74 insertions(+), 2 deletions(-) create mode 100644 internal/task/stats.go create mode 100644 internal/task/stats_test.go (limited to 'internal/task') diff --git a/internal/task/stats.go b/internal/task/stats.go new file mode 100644 index 0000000..c516249 --- /dev/null +++ b/internal/task/stats.go @@ -0,0 +1,40 @@ +package task + +import "time" + +// TotalTasks returns the number of tasks provided. +func TotalTasks(tasks []Task) int { + return len(tasks) +} + +// InProgressTasks returns the number of tasks that have been started and are not completed. +func InProgressTasks(tasks []Task) int { + count := 0 + for _, t := range tasks { + if t.Status == "completed" { + continue + } + if t.Start != "" { + count++ + } + } + return count +} + +// DueTasks returns the number of tasks with a due date that is not in the future. +func DueTasks(tasks []Task, now time.Time) int { + count := 0 + for _, t := range tasks { + if t.Status == "completed" || t.Due == "" { + continue + } + ts, err := time.Parse("20060102T150405Z", t.Due) + if err != nil { + continue + } + if !ts.After(now) { + count++ + } + } + return count +} diff --git a/internal/task/stats_test.go b/internal/task/stats_test.go new file mode 100644 index 0000000..fddbd69 --- /dev/null +++ b/internal/task/stats_test.go @@ -0,0 +1,28 @@ +package task + +import ( + "testing" + "time" +) + +func TestStats(t *testing.T) { + now := time.Now() + tasks := []Task{ + {Description: "t1"}, + {Description: "t2", Start: "20240101T000000Z"}, + {Description: "t3", Due: now.Add(-time.Hour).Format("20060102T150405Z")}, + {Description: "t4", Start: "20240101T000000Z", Due: now.Add(-time.Hour).Format("20060102T150405Z")}, + } + + if TotalTasks(tasks) != 4 { + t.Errorf("total tasks wrong: %d", TotalTasks(tasks)) + } + + if InProgressTasks(tasks) != 2 { + t.Errorf("in progress wrong: %d", InProgressTasks(tasks)) + } + + if DueTasks(tasks, now) != 2 { + t.Errorf("due tasks wrong: %d", DueTasks(tasks, now)) + } +} diff --git a/internal/task/task.go b/internal/task/task.go index 4c33fdb..f7a3f73 100644 --- a/internal/task/task.go +++ b/internal/task/task.go @@ -46,8 +46,12 @@ func Add(description string, tags []string) error { // Export retrieves all tasks using `task export rc.json.array=off` and parses // the JSON output into a slice of Task structs. -func Export() ([]Task, error) { - cmd := exec.Command("task", "export", "rc.json.array=off") +// Export retrieves tasks using `task export rc.json.array=off` and parses +// the JSON output into a slice of Task structs. Optional filter arguments are +// passed directly to the `task` command before `export`. +func Export(filters ...string) ([]Task, error) { + args := append(filters, "export", "rc.json.array=off") + cmd := exec.Command("task", args...) out, err := cmd.Output() if err != nil { return nil, err -- cgit v1.2.3