package main import ( "fmt" "strconv" "strings" "time" "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 cmdModifyTimer(args []string) int { // If no timer id is specified, edit the most recent one modId := "@0" tags, rem := pullTagsFromArgs(args) for i := range rem { if rem[i][0] == '@' { modId = rem[i] } } 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 } if len(tags) <= 0 { fmt.Println("Modify only supports adding tags at this time.") return 1 } for i := range tags { tmr.AddTag(tags[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 } 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) } // 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 cmdPrintList(args []string) int { loadActiveAndRecentTimeEntries() useDefaultFilter := true var showIds bool var beg, end time.Time tags, rem := pullTagsFromArgs(args) 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 } } if end.IsZero() { end = time.Now() } // 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 { 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 } for i := 0; i < timers.Length(); i++ { wrk := timers.Get(i) oldDayStr := dayStr dayStr = wrk.GetStart().Format("2006/01/02") if dayStr != oldDayStr { str += fmt.Sprintln(dayStr) } 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 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 } func cmdDoExport(args []string) int { return 0 }