From 0d2a887618095f2a8b6616dca4bfc74f482ca456 Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 10 May 2018 09:45:21 -0500 Subject: [PATCH] Split up timer ops --- timer_ops_add.go | 128 ++++++++++++++++++++++++++++++ timer_ops_other.go | 190 +++++++++++++++++++++++++++++++++++++++++++++ timer_ops_print.go | 167 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 485 insertions(+) create mode 100644 timer_ops_add.go create mode 100644 timer_ops_other.go create mode 100644 timer_ops_print.go diff --git a/timer_ops_add.go b/timer_ops_add.go new file mode 100644 index 0000000..a4d5929 --- /dev/null +++ b/timer_ops_add.go @@ -0,0 +1,128 @@ +package main + +import ( + "fmt" + "strings" + "time" + + gime "git.bullercodeworks.com/brian/gime-lib" +) + +// cmdStartTimer takes a list of arguments and returns the return code +// to be passed along to os.Exit +func cmdStartTimer(args []string) int { + var err error + var entry *gime.TimeEntry + // By default we start the timer now + tm := time.Now() + tags, rem := pullTagsFromArgs(args) + + if len(rem) > 0 { + // Check if the first argument looks like a date/time + tm, err = parseFuzzyTime(rem[0]) + } + + tc := new(gime.TagCollection) + for i := range tags { + tc.Push(tags[i]) + } + + if entry, err = gime.CreateTimeEntry(tm, time.Time{}, tc); err != nil { + fmt.Println(err) + return 1 + } + + if err = gdb.SaveTimeEntry(entry); err != nil { + fmt.Println(err) + return 1 + } + + fmt.Println("Started:", TimerToString(entry)) + return 0 +} + +func cmdAddTimer(args []string) int { + var err error + var entry *gime.TimeEntry + // By default we start the timer now + tags, rem := pullTagsFromArgs(args) + var beg, end time.Time + for _, opt := range rem { + var tmpBeg, tmpEnd time.Time + if strings.Contains(opt, "-") { + pts := strings.Split(opt, "-") + if len(pts[0]) > 0 { + // This should be the starting date + tmpBeg, err = parseFuzzyTime(pts[0]) + if err != nil { + continue + } + } + if len(pts[1]) > 0 { + // This should be the ending date + tmpEnd, err = parseFuzzyTime(pts[1]) + if err != nil { + continue + } + } + } + if !tmpBeg.IsZero() || !tmpEnd.IsZero() { + beg, end = tmpBeg, tmpEnd + } + } + if end.IsZero() { + end = time.Now() + } + tc := new(gime.TagCollection) + for i := range tags { + tc.Push(tags[i]) + } + if entry, err = gime.CreateTimeEntry(beg, end, tc); err != nil { + fmt.Println(err) + return 1 + } + if err = gdb.SaveTimeEntry(entry); err != nil { + fmt.Println(err) + return 1 + } + fmt.Println("Added Time Entry:", TimerToString(entry)) + return 0 +} + +func cmdContinueTimer(args []string) int { + // Get the last running timer and start a new one with the same tags + te, err := getMostRecentTimeEntry() + if err != nil { + fmt.Println(err.Error()) + return 1 + } + tagColl := te.GetTags() + var tags []string + for i := 0; i < tagColl.Length(); i++ { + tags = append(tags, "+"+tagColl.Get(i)) + } + args = append(args, tags...) + return cmdStartTimer(args) +} + +// switchTimer performs a stop on any currently running timers +// and starts a new timer with the given arguments +func cmdSwitchTimer(args []string) int { + 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 timers + return 1 + } + return cmdStartTimer(args) +} diff --git a/timer_ops_other.go b/timer_ops_other.go new file mode 100644 index 0000000..546996f --- /dev/null +++ b/timer_ops_other.go @@ -0,0 +1,190 @@ +package main + +import ( + "fmt" + "strconv" + "strings" + "time" + + "git.bullercodeworks.com/brian/gime-lib" +) + +func cmdModifyTimer(args []string) int { + // If no timer id is specified, edit the most recent one + modId := "@0" + var tags, remTags, rem []string + tags, rem = pullTagsFromArgs(args) + remTags, rem = pullRemoveTagsFromArgs(rem) + for i := range rem { + if rem[i][0] == '@' { + modId = rem[i] + } + if strings.HasPrefix(rem, "start:") { + + } + } + timerId, err := strconv.Atoi(modId[1:]) + if err != nil { + fmt.Println("error parsing timer id:", err.Error()) + return 1 + } + tmr, _, err := findTimerById(timerId) + if err != nil { + fmt.Println(err.Error()) + return 1 + } + for i := range tags { + tmr.AddTag(tags[i]) + } + for i := range remTags { + tmr.RemoveTag(remTags[i]) + } + if err = gdb.SaveTimeEntry(tmr); err != nil { + fmt.Println("Error saving modified timer:", err.Error()) + return 1 + } + fmt.Println("Modified Timer:") + fmt.Println(" ", TimerToString(tmr)) + return 0 +} + +// cmdStopTimer takes parameters that describe which times to stop +func cmdStopTimer(args []string) int { + // args[0] should be a timer id (starting with '@') + var err error + tm := time.Now() + actTimers := gdb.LoadTimeEntryCollection(gime.TypeCurrent) + var tmr *gime.TimeEntry + stopId := "@0" // By default, stop the first timer + for i := range args { + if args[i][0] == '@' { + stopId = args[i] + continue + } + tmpTm, err := parseFuzzyTime(args[i]) + if err == nil { + // We found a time + tm = tmpTm + continue + } + } + if stopId != "@all" { + // Find the timer that we're stopping + timerId, err := strconv.Atoi(stopId[1:]) + if err != nil { + fmt.Println("Error parsing timer id:", err.Error()) + return 1 + } + tmr = actTimers.Get(timerId) + if timerId >= actTimers.Length() || timerId < 0 || tmr == nil { + fmt.Println("Error finding timer with id:", timerId) + return 1 + } + } + + stopTimer := func(tmr *gime.TimeEntry, at time.Time) int { + tmr.SetEnd(at) + if err = gdb.UpdateTimeEntry(tmr); err != nil { + fmt.Println(err.Error()) + return 1 + } + fmt.Println("Stopped:", InferTimerDetailString(tmr)) + return 0 + } + if stopId == "@all" { + var ret int + for i := 0; i < actTimers.Length(); i++ { + ret += stopTimer(actTimers.Get(i), tm) + } + if ret > 0 { + return 1 // One or more stop operations failed + } + return 0 + } + // Just stop the one timer + return stopTimer(tmr, tm) +} + +// cmdDeleteTimer takes parameters that describe the timers to be deleted. +func cmdDeleteTimer(args []string) int { + var err error + if len(args) < 1 || args[0][0] != '@' { + fmt.Println("Couldn't determine which timer(s) to delete") + return 1 + } + + // We've got a timer id to delete + timerId, err := strconv.Atoi(args[0][1:]) + if err != nil { + fmt.Println("Error parsing timer id: " + err.Error()) + return 1 + } + tmr, tp, err := findTimerById(timerId) + if err != nil { + fmt.Println(err.Error()) + return 1 + } + if gdb.RemoveTimeEntry(tmr) != nil { + fmt.Println("Error removing entry " + gime.TypeToString(tp) + "." + tmr.GetUUID()) + return 1 + } + fmt.Println("Deleted Time Entry: " + TimerToString(tmr)) + return 0 +} + +func cmdDoArchive(args []string) int { + if len(args) == 0 { + fmt.Println("Nothing to do") + return 1 + } + + var tags []string + tags, args = pullTagsFromArgs(args) + var bef time.Time + var err error + if len(args) > 0 { + bef, err = parseFuzzyTime(args[0]) + } + if bef.IsZero() && len(tags) == 0 { + fmt.Println("Couldn't figure out what to archive") + return 1 + } + ret := 0 + loadActiveAndRecentTimeEntries() + tagFilter := func(t *gime.TimeEntry) bool { + for i := range tags { + if !t.HasTag(tags[i]) { + return false + } + } + return true + } + if len(tags) > 0 { + timeEntries = filterTimerCollection(timeEntries, tagFilter) + } + fmt.Print("Archive all timers ") + if !bef.IsZero() { + fmt.Print("before ", bef, " ") + } + if len(tags) > 0 { + fmt.Print("with tags ", tags) + } + for i := 0; i < timeEntries.Length(); i++ { + tst := timeEntries.Get(i) + archIt := false + if !bef.IsZero() { + archIt = tst.GetEnd().Before(bef) + } else { + archIt = true + } + if archIt { + fmt.Print(".") + if err = gdb.ArchiveTimeEntry(tst); err != nil { + fmt.Print("Error archiving entry (", tst.GetUUID(), ")", err.Error()) + ret = 1 + } + } + } + fmt.Println("Done") + return ret +} diff --git a/timer_ops_print.go b/timer_ops_print.go new file mode 100644 index 0000000..b9a4dba --- /dev/null +++ b/timer_ops_print.go @@ -0,0 +1,167 @@ +package main + +import ( + "fmt" + "strconv" + "strings" + "time" + + gime "git.bullercodeworks.com/brian/gime-lib" +) + +func cmdPrintList(args []string) int { + useDefaultFilter := true + var showIds bool + var beg, end time.Time + tags, rem := pullTagsFromArgs(args) + loadType := gime.TypeNoArchive + for _, opt := range rem { + var tmpBeg, tmpEnd time.Time + // Check for command modifiers + if strings.HasPrefix(opt, ":") { + switch opt { + case ":ids": + showIds = true + // Special durations + case ":day": + beg, _ = parseFuzzyTime("00:00") + end, _ = parseFuzzyTime("23:59") + case ":week": + currDoW := time.Now().Weekday() + beg = time.Now().AddDate(0, 0, int(currDoW)*-1) + beg = time.Date(beg.Year(), beg.Month(), beg.Day(), 0, 0, 0, 0, beg.Location()) + case ":month": + currDoM := time.Now().Day() + beg = time.Now().AddDate(0, 0, int(currDoM)*-1) + beg = time.Date(beg.Year(), beg.Month(), beg.Day(), 0, 0, 0, 0, beg.Location()) + case ":year": + yr := strconv.Itoa(time.Now().Year()) + beg, _ = parseFuzzyTime(yr + "0101T00:00") + end, _ = parseFuzzyTime(yr + "1231T23:59") + } + continue + } + + // Do our best to figure out what timers the user wants to list + var err error + if strings.Contains(opt, "-") { + useDefaultFilter = false + pts := strings.Split(opt, "-") + if len(pts[0]) > 0 { + // This should be the starting date + tmpBeg, err = parseFuzzyTime(pts[0]) + if err != nil { + continue + } + } + if len(pts[1]) > 0 { + // This should be the ending date + tmpEnd, err = parseFuzzyTime(pts[1]) + if err != nil { + continue + } + } + } + if !tmpBeg.IsZero() || !tmpEnd.IsZero() { + beg, end = tmpBeg, tmpEnd + } + + // Was 'archive' requested? + if strings.HasPrefix(opt, "arch") { + loadType = gime.TypeArchive + useDefaultFilter = false + } + } + if end.IsZero() { + end = time.Now() + } + + timeEntries = gdb.LoadTimeEntryCollection(loadType) + + // By default, list all entries ending today or still running + defaultFilter := func(t *gime.TimeEntry) bool { + return t.EndsToday() || t.IsRunning() + } + + timeSpanFilter := func(t *gime.TimeEntry) bool { + return t.GetStart().After(beg) && t.GetEnd().Before(end) + } + tagFilter := func(t *gime.TimeEntry) bool { + for i := range tags { + if !t.HasTag(tags[i]) { + return false + } + } + return true + } + + compoundFilter := func(t *gime.TimeEntry) bool { + // If we didn't get any other filter specifications, just use the default + if useDefaultFilter { + return defaultFilter(t) + } + // Otherwise we want to filter timespan and tags + return timeSpanFilter(t) && tagFilter(t) + } + + dayStr := "" + timers := filterTimerCollection(timeEntries, compoundFilter) + var str string + var currId int + var err error + if timers.Length() == 0 { + if useDefaultFilter { + fmt.Println("No timers found for today") + } else { + begFmt := friendlyFormatForTime(beg) + endFmt := friendlyFormatForTime(end) + useFmt := endFmt + if len(begFmt) > len(endFmt) { + useFmt = begFmt + } + fmt.Println("No timers found in period " + beg.Format(useFmt) + " - " + end.Format(useFmt)) + } + return 0 + } + + // Get day totals + dayTotals := make(map[string]time.Duration) + for i := 0; i < timers.Length(); i++ { + wrk := timers.Get(i) + dur := wrk.GetEnd().Sub(wrk.GetStart()) + if wrk.GetEnd().IsZero() { + dur = time.Now().Sub(wrk.GetStart()) + } + dayTotals[wrk.GetStart().Format("2006/01/02")] += dur + } + for i := 0; i < timers.Length(); i++ { + wrk := timers.Get(i) + oldDayStr := dayStr + dayStr = wrk.GetStart().Format("2006/01/02") + if dayStr != oldDayStr { + wrkDur := dayTotals[dayStr].Round(GetRoundToDuration()) + fmtStr := dayStr + " ( %.2f )\n" + str += fmt.Sprintf(fmtStr, DurationToDecimal(wrkDur)) + } + id := "" + if showIds { + if currId, err = findIdOfTimer(wrk); err == nil { + id = fmt.Sprintf("%3s", fmt.Sprintf("@%d", currId)) + } else { + id = "@!" + } + } + str += fmt.Sprintf(" %s %s\n", id, TimerToString(timers.Get(i))) + } + fmt.Println(str) + return 0 +} + +func cmdPrintDetail(args []string) int { + fmt.Println("Not implemented yet.") + return 1 +} + +func cmdDoExport(args []string) int { + return 0 +}