diff --git a/main.go b/main.go index 4383061..26355e9 100644 --- a/main.go +++ b/main.go @@ -31,11 +31,14 @@ func main() { initialize() var parms []string - if len(os.Args) > 1 { + if len(os.Args) > 1 && !strings.HasPrefix(os.Args[1], ":") { parms = os.Args[1:] } else { // If no parameters were passed, just print the status parms = append(parms, "status") + if strings.HasPrefix(os.Args[1], ":") { + parms = append(parms, os.Args[1:]...) + } } if fn, ok := opFuncs[parms[0]]; ok { @@ -162,7 +165,8 @@ func initialize() { opFuncs["cont"] = cmdContinueTimer validOperations["cont"] = []string{ - "cont [time] - Continue the last stopped timer", + "cont [time] [+tags] - Continue the last stopped timer", + " Any tags given will be added", } opFuncs["config"] = cmdDoConfig diff --git a/model.go b/model.go index aa7397c..93334d6 100644 --- a/model.go +++ b/model.go @@ -4,6 +4,7 @@ import ( "encoding/json" "fmt" "strconv" + "strings" "time" scribble "github.com/nanobox-io/golang-scribble" @@ -44,6 +45,19 @@ func SaveTimer(entry *Timer) error { return SaveMetadata(m) } +// LoadTimerFromPartialUUID gets the most recent Timer that has a UUID +// starting with `upart` +func LoadTimerFromPartialUUID(upart string) *Timer { + tm := LoadActiveTimers() + tm = append(tm, LoadTimers(time.Time{}, time.Now())...) + for _, v := range tm { + if strings.HasPrefix(v.GetUUID(), upart) { + return &v + } + } + return nil +} + func LoadTimer(uuid string) *Timer { db, err := scribble.New(cfg.Get("dbdir"), nil) timer := Timer{} @@ -95,7 +109,7 @@ func LoadTimers(st, end time.Time) []Timer { if m.HasYear(i) { tmrs := LoadTimersFromDb(strconv.Itoa(i)) for _, v := range tmrs { - if v.GetStart().After(st) && v.GetStart().Before(end) { + if v.GetStart().After(st) && v.GetEnd().Before(end) { ret = append(ret, v) } } @@ -104,6 +118,21 @@ func LoadTimers(st, end time.Time) []Timer { return ret } +func LoadTimersWithTags(tags []string) []Timer { + tm := LoadActiveTimers() + tm = append(tm, LoadTimers(time.Time{}, time.Now())...) + var ret []Timer + for _, v := range tm { + for _, tg := range tags { + if v.HasTag(tg) { + ret = append(ret, v) + break + } + } + } + return ret +} + func LoadTimersFromDb(coll string) []Timer { db, _ := scribble.New(cfg.Get("dbdir"), nil) raw, _ := db.ReadAll(coll) diff --git a/model_meta.go b/model_meta.go index bb9e509..13e6fb4 100644 --- a/model_meta.go +++ b/model_meta.go @@ -7,8 +7,9 @@ import ( ) type Meta struct { - Years []int - Tags []Tag + Years []int + Tags []Tag + MostRecent string } func LoadMetadata() (*Meta, error) { @@ -107,3 +108,11 @@ func (m *Meta) GetTagsByName(nms []string) []Tag { } return ret } + +func (m *Meta) SetLastStopped(tid string) { + m.MostRecent = tid +} + +func (m *Meta) GetLastStopped() string { + return m.MostRecent +} diff --git a/ops_timer.go b/ops_timer.go index 9f04582..2005a21 100644 --- a/ops_timer.go +++ b/ops_timer.go @@ -78,7 +78,25 @@ func cmdAddTimer(args []string) int { func cmdContinueTimer(args []string) int { // Get the last running timer and start a new one with the same tags - return 0 + m, err := LoadMetadata() + if err != nil { + return 1 + } + + tid := m.GetLastStopped() + for i := range args { + if strings.HasPrefix(args[i], "@") { + tid = strings.TrimLeft(args[i], "@") + } + } + t := LoadTimer(tid) + + fmt.Println("Last Tags:", t.Tags) + for i := range t.Tags { + t.Tags[i] = "+" + t.Tags[i] + } + args = append(args, t.Tags...) + return cmdStartTimer(args) } func cmdStopTimer(args []string) int { @@ -86,7 +104,7 @@ func cmdStopTimer(args []string) int { tm := time.Now() actTimers := LoadActiveTimers() var tmr *Timer - stopId := "@0" // By default, stop the first timer + stopId := "@-" // By default, stop the first timer for i := range args { if args[i][0] == '@' { stopId = args[i] @@ -113,12 +131,18 @@ func cmdStopTimer(args []string) int { } } + m, err := LoadMetadata() + if err != nil { + fmt.Println("Unable to load metadata, 'Most Recent' timer may be incorrect") + } stopTimer := func(tmr *Timer, at time.Time) int { tmr.SetEnd(at) if err = SaveTimer(tmr); err != nil { fmt.Println(err.Error()) return 1 } + + m.SetLastStopped(tmr.GetUUID()) fmt.Println("Stopped:", tmr.InferDetailString()) return 0 } @@ -127,13 +151,16 @@ func cmdStopTimer(args []string) int { for _, v := range actTimers { ret += stopTimer(&v, tm) } + SaveMetadata(m) if ret > 0 { return 1 } return 0 } - return stopTimer(tmr, tm) + ret := stopTimer(tmr, tm) + SaveMetadata(m) + return ret } func cmdDeleteTimer(args []string) int { @@ -160,16 +187,78 @@ func cmdDeleteTimer(args []string) int { } func cmdModifyTimer(args []string) int { + var err error + // searchUUID is the [partial] uuid of the timer we're modifying + var searchUUID string + var edtTmr *Timer + + var tags, remTags, rem []string + tags, rem = pullTagsFromArgs(args) + remTags, rem = pullRemoveTagsFromArgs(rem) + var start, end time.Time + + for i := range rem { + if strings.HasPrefix(rem[i], "@") { + searchUUID = strings.TrimLeft(searchUUID, "@") + edtTmr = LoadTimerFromPartialUUID(searchUUID) + } + if strings.HasPrefix(rem[i], "start=") { + stStr := strings.TrimPrefix(rem[i], "start=") + start, err = parseFuzzyTime(stStr) + if err != nil { + fmt.Println("Unable to parse start time: " + stStr) + return 1 + } + } + if strings.HasPrefix(rem[i], "end=") { + endStr := strings.TrimPrefix(rem[i], "end=") + end, err = parseFuzzyTime(endStr) + if err != nil { + fmt.Println("Unable to parse end time: " + endStr) + return 1 + } + } + } + if edtTmr == nil { + fmt.Println("Error finding timer:", searchUUID) + return 1 + } + fmt.Println(edtTmr.String()) + + _, _, _, _ = tags, remTags, start, end + fmt.Println(tags, remTags, start, end) return 0 } func cmdSwitchTimer(args []string) int { - return 0 + stopId := "@all" + stopIdIdx := -1 + for i := range args { + // See if we have a timer id in the args + if args[i][0] == '@' { + stopId = args[i] + stopIdIdx = i + } + } + if stopIdIdx >= 0 { + args = append(args[:stopIdIdx], args[stopIdIdx+1:]...) + } + if cmdStopTimer([]string{stopId}) != 0 { + // Error while stopping tiemrs + return 1 + } + return cmdStartTimer(args) } func cmdPrintStatus(args []string) int { tmrs := LoadActiveTimers() curr := time.Now() + var showId bool + for i := range args { + if strings.HasPrefix(args[i], ":id") { + showId = true + } + } fmt.Println("Current Time:", curr.Format(time.Stamp)) if len(tmrs) == 0 { fmt.Println("No timer running") @@ -185,10 +274,14 @@ func cmdPrintStatus(args []string) int { } for i, v := range tmrs { + var id string + if showId { + id = " (" + v.GetUUID() + ")" + } if short { - fmt.Printf(" @%d %s\n", i, v.DetailString()) + fmt.Printf(" @%d %s%s\n", i, v.DetailString(), id) } else { - fmt.Printf(" @%d %s\n", i, v.LongDetailString()) + fmt.Printf(" @%d %s%s\n", i, v.LongDetailString(), id) } } } @@ -196,11 +289,9 @@ func cmdPrintStatus(args []string) int { } func cmdPrintList(args []string) int { - useDefaultFilter := true - var showIds bool + var showIds, showActiveTimers, onlyShowActive bool var beg, end time.Time tags, rem := pullTagsFromArgs(args) - _ = tags for _, opt := range rem { var tmpBeg, tmpEnd time.Time // Check for command modifiers @@ -230,7 +321,7 @@ func cmdPrintList(args []string) int { // Do our best to figure out what timers the user wants to list var err error if strings.Contains(opt, "-") { - useDefaultFilter = false + onlyShowActive = false pts := strings.Split(opt, "-") if len(pts[0]) > 0 { // This should be the starting date @@ -253,15 +344,31 @@ func cmdPrintList(args []string) int { } if end.IsZero() { end = time.Now() + showActiveTimers = true } // By default, list all entries ending today or still running dayStr := "" - tmrs := LoadActiveTimers() - tmrs = append(tmrs, LoadTimers(beg, end)...) + var tmrs []Timer + if showActiveTimers { + tmrs = LoadActiveTimers() + } + if !onlyShowActive { + tmrs = append(tmrs, LoadTimers(beg, end)...) + } + if len(tags) > 0 { + // Filter the tags + var filtered []Timer + for _, tg := range tags { + FilterTimers(tmrs, func(t *Timer) bool { + return t.HasTag(tg) + }) + } + tmrs = filtered + } var str string if len(tmrs) == 0 { - if useDefaultFilter { + if onlyShowActive { fmt.Println("No timers found for today") } else { begFmt := friendlyFormatForTime(beg) @@ -292,9 +399,11 @@ func cmdPrintList(args []string) int { fmtStr := dayStr + " ( %.2f )\n" str += fmt.Sprintf(fmtStr, DurationToDecimal(vDur)) } - _ = showIds - id := "" - str += fmt.Sprintf(" %s %s\n", id, v.FriendlyString()) + if showIds { + str += fmt.Sprintf(" %s (%s)\n", v.FriendlyString(), v.GetUUID()) + } else { + str += fmt.Sprintf(" %s\n", v.FriendlyString()) + } } fmt.Println(str) return 0