[WIP] Add task editing
This commit is contained in:
@ -3,6 +3,7 @@ package taskwarrior
|
||||
import (
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"math"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -18,19 +19,21 @@ type Task struct {
|
||||
Tags []string `json:"tags"`
|
||||
Depends []string `json:"depends"`
|
||||
Urgency float32 `json:"urgency"`
|
||||
Parent string `json:"parent"`
|
||||
Due string `json:"due"`
|
||||
Wait string `json:"wait"`
|
||||
Scheduled string `json:"scheduled"`
|
||||
Until string `json:"until"`
|
||||
Recur string `json:"recur"`
|
||||
Start string `json:"start"`
|
||||
End string `json:"end"`
|
||||
Entry string `json:"entry"`
|
||||
Modified string `json:"modified"`
|
||||
Parent string `json:"parent"`
|
||||
Recur string `json:"recur"`
|
||||
}
|
||||
|
||||
func (t *Task) Get(field string) string {
|
||||
func (t *Task) GetString(fieldWFormat string) string {
|
||||
field, format, _ := strings.Cut(fieldWFormat, ".")
|
||||
|
||||
switch field {
|
||||
case "id":
|
||||
return strconv.FormatInt(t.Id, 10)
|
||||
@ -49,30 +52,24 @@ func (t *Task) Get(field string) string {
|
||||
case "urgency":
|
||||
return fmt.Sprintf("%.2f", t.Urgency)
|
||||
case "due":
|
||||
return t.Due
|
||||
return formatDate(t.Due, format)
|
||||
case "wait":
|
||||
return t.Wait
|
||||
case "scheduled":
|
||||
return t.Scheduled
|
||||
return formatDate(t.Scheduled, format)
|
||||
case "end":
|
||||
return t.End
|
||||
case "entry":
|
||||
return t.Entry
|
||||
return formatDate(t.Entry, format)
|
||||
case "modified":
|
||||
return t.Modified
|
||||
case "start":
|
||||
return formatDate(t.Start, format)
|
||||
case "until":
|
||||
return formatDate(t.Until, format)
|
||||
// TODO: implement these fields
|
||||
case "start.age":
|
||||
return formatTime(t.Start)
|
||||
case "depends":
|
||||
return strings.Join(t.Depends, ", ")
|
||||
case "entry.age":
|
||||
return formatTime(t.Entry)
|
||||
case "scheduled.countdown":
|
||||
return formatTime(t.Scheduled)
|
||||
case "until.remaining":
|
||||
return formatTime(t.Until)
|
||||
case "due.relative":
|
||||
return formatTime(t.Due)
|
||||
case "recur":
|
||||
return t.Recur
|
||||
default:
|
||||
@ -104,15 +101,75 @@ type Report struct {
|
||||
|
||||
type Reports map[string]*Report
|
||||
|
||||
func formatTime(timeStr string) string {
|
||||
if timeStr == "" {
|
||||
func formatDate(date string, format string) string {
|
||||
if date == "" {
|
||||
return ""
|
||||
}
|
||||
format := "20060102T150405Z"
|
||||
t, err := time.Parse(format, timeStr)
|
||||
|
||||
dtformat := "20060102T150405Z"
|
||||
dt, err := time.Parse(dtformat, date)
|
||||
if err != nil {
|
||||
slog.Error("Failed to parse time:", err)
|
||||
return timeStr
|
||||
return ""
|
||||
}
|
||||
|
||||
switch format {
|
||||
case "formatted", "":
|
||||
return dt.Format("2006-01-02 15:04")
|
||||
// TODO: proper julian date formatting
|
||||
case "julian":
|
||||
return dt.Format("060102.1504")
|
||||
case "epoch":
|
||||
return strconv.FormatInt(dt.Unix(), 10)
|
||||
case "iso":
|
||||
return dt.Format("2006-01-02T150405Z")
|
||||
case "age":
|
||||
return parseDurationVague(time.Since(dt))
|
||||
case "relative":
|
||||
return parseDurationVague(time.Until(dt))
|
||||
// TODO: implement remaining
|
||||
case "remaining":
|
||||
return ""
|
||||
case "countdown":
|
||||
return parseCountdown(time.Since(dt))
|
||||
default:
|
||||
slog.Error(fmt.Sprintf("Date format not implemented: %s", format))
|
||||
return ""
|
||||
}
|
||||
return t.Format("2006-01-02 15:04")
|
||||
}
|
||||
|
||||
func parseDurationVague(d time.Duration) string {
|
||||
dur := d.Round(time.Second).Abs()
|
||||
days := dur.Hours() / 24
|
||||
|
||||
var formatted string
|
||||
if dur >= time.Hour*24*365 {
|
||||
formatted = fmt.Sprintf("%.1fy", days/365)
|
||||
} else if dur >= time.Hour*24*90 {
|
||||
formatted = strconv.Itoa(int(math.Round(days/30))) + "mo"
|
||||
} else if dur >= time.Hour*24*7 {
|
||||
formatted = strconv.Itoa(int(math.Round(days/7))) + "w"
|
||||
} else if dur >= time.Hour*24 {
|
||||
formatted = strconv.Itoa(int(days)) + "d"
|
||||
} else if dur >= time.Hour {
|
||||
formatted = strconv.Itoa(int(dur.Round(time.Hour).Hours())) + "h"
|
||||
} else if dur >= time.Minute {
|
||||
formatted = strconv.Itoa(int(dur.Round(time.Minute).Minutes())) + "min"
|
||||
} else if dur >= time.Second {
|
||||
formatted = strconv.Itoa(int(dur.Round(time.Second).Seconds())) + "s"
|
||||
}
|
||||
|
||||
if d < 0 {
|
||||
formatted = "-" + formatted
|
||||
}
|
||||
|
||||
return formatted
|
||||
}
|
||||
|
||||
func parseCountdown(d time.Duration) string {
|
||||
hours := int(d.Hours())
|
||||
minutes := int(d.Minutes()) % 60
|
||||
seconds := int(d.Seconds()) % 60
|
||||
|
||||
return fmt.Sprintf("%d:%02d:%02d", hours, minutes, seconds)
|
||||
}
|
||||
|
||||
@ -87,6 +87,7 @@ type TaskWarrior interface {
|
||||
|
||||
GetTasks(report *Report, filter ...string) Tasks
|
||||
AddTask(task *Task) error
|
||||
ModifyTask(task *Task)
|
||||
}
|
||||
|
||||
type TaskSquire struct {
|
||||
@ -323,6 +324,24 @@ func (ts *TaskSquire) AddTask(task *Task) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO error handling
|
||||
func (ts *TaskSquire) ModifyTask(task *Task) {
|
||||
ts.mutex.Lock()
|
||||
defer ts.mutex.Unlock()
|
||||
|
||||
jsonStr, err := json.Marshal(Tasks{task})
|
||||
if err != nil {
|
||||
slog.Error("Failed marshalling task:", err)
|
||||
}
|
||||
|
||||
cmd := exec.Command(twBinary, append([]string{"echo", string(jsonStr), "|"}, append(ts.defaultArgs, []string{"import", "-"}...)...)...)
|
||||
out, err := cmd.CombinedOutput()
|
||||
strOut := string(out)
|
||||
if err != nil {
|
||||
slog.Error("Failed modifying task:", err, strOut)
|
||||
}
|
||||
}
|
||||
|
||||
func (ts *TaskSquire) extractConfig() *TWConfig {
|
||||
cmd := exec.Command(twBinary, append(ts.defaultArgs, []string{"_show"}...)...)
|
||||
output, err := cmd.CombinedOutput()
|
||||
|
||||
Reference in New Issue
Block a user