summaryrefslogtreecommitdiff
path: root/internal/task
diff options
context:
space:
mode:
authorPaul Bütow <1224732+snonux@users.noreply.github.com>2025-06-20 09:40:56 +0300
committerPaul Bütow <1224732+snonux@users.noreply.github.com>2025-06-20 09:40:56 +0300
commit13f7678a9fd092ac20eec10e4d2196f5bd1ae107 (patch)
tree512a6b9f8017f8cf0cc24158acc154034c121225 /internal/task
parent13ec0a6ec615b4c7e7ddc461a7a6a623109826f9 (diff)
Add cell navigation, editor hotkey, stats and filter
Diffstat (limited to 'internal/task')
-rw-r--r--internal/task/stats.go40
-rw-r--r--internal/task/stats_test.go28
-rw-r--r--internal/task/task.go8
3 files changed, 74 insertions, 2 deletions
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 <filter> 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