From 655c052f38d846f3556cd34431549fbd9b29dcba Mon Sep 17 00:00:00 2001 From: Brian Buller Date: Thu, 11 Jan 2018 10:52:44 -0600 Subject: [PATCH] Made some great progress --- cmd/gime/helpers.go | 199 ++++++++++++++++++++++++++++++++++- cmd/gime/main.go | 167 +++++++++++++++++++---------- cmd/gime/timer_operations.go | 87 +++++++++------ model.go | 41 +++++--- model_timeentry.go | 29 ++--- timeentry.go | 13 ++- 6 files changed, 412 insertions(+), 124 deletions(-) diff --git a/cmd/gime/helpers.go b/cmd/gime/helpers.go index e28d2de..606209e 100644 --- a/cmd/gime/helpers.go +++ b/cmd/gime/helpers.go @@ -2,11 +2,36 @@ package main import ( "errors" + "fmt" "strconv" + "time" "git.bullercodeworks.com/brian/gime" ) +// filterTimerCollection takes a collection and a function that it runs every entry through +// If the function returns true for the entry, it adds it to a new collection to be returned +func filterTimerCollection(c *gime.TimeEntryCollection, fn func(t *gime.TimeEntry) bool) *gime.TimeEntryCollection { + ret := new(gime.TimeEntryCollection) + for i := 0; i < c.Length(); i++ { + if fn(c.Get(i)) { + ret.Push(c.Get(i)) + } + } + return ret +} + +func TimerCollectionToString(c *gime.TimeEntryCollection) string { + var ret string + for i := 0; i < c.Length(); i++ { + ret += TimerToString(c.Get(i)) + if i < c.Length()-1 { + ret += "\n" + } + } + return ret +} + // TimerToString takes a TimeEntry and gives a nicely formatted string func TimerToString(t *gime.TimeEntry) string { var ret string @@ -37,10 +62,62 @@ func TimerToString(t *gime.TimeEntry) string { return ret } +func InferTimerDetailString(t *gime.TimeEntry) string { + diffEnd := time.Now() + if !t.GetEnd().IsZero() { + diffEnd = t.GetEnd() + } + if int(diffEnd.Sub(t.GetStart())) >= (int(time.Hour) * diffEnd.Hour()) { + return TimerDetailToLongString(t) + } + return TimerDetailToString(t) +} + +func TimerDetailToString(t *gime.TimeEntry) string { + ret := t.GetStart().Format("15:04") + if t.GetEnd().IsZero() { + ret += " (" + padLeft(sinceToString(t.GetStart()), len("00h 00m 00s")) + ") " + } else { + ret += " (" + padLeft(diffToString(t.GetStart(), t.GetEnd()), len("00h 00m 00s")) + ") " + } + if t.GetTags().Length() > 0 { + ret += " [ " + for j := 0; j < t.GetTags().Length(); j++ { + ret += t.GetTags().Get(j) + if j < t.GetTags().Length()-1 { + ret += ", " + } + } + ret += " ] " + } + return ret +} + +// ...ToLongString includes year/month/day +func TimerDetailToLongString(t *gime.TimeEntry) string { + ret := t.GetStart().Format(time.Stamp) + if t.GetEnd().IsZero() { + ret += " (" + padLeft(sinceToString(t.GetStart()), len("0000y 00m 00d 00h 00m 00s")) + ") " + } else { + ret += " (" + padLeft(diffToString(t.GetStart(), t.GetEnd()), len("0000y 00m 00d 00h 00m 00s")) + ") " + } + if t.GetTags().Length() > 0 { + ret += " [ " + for j := 0; j < t.GetTags().Length(); j++ { + ret += t.GetTags().Get(j) + if j < t.GetTags().Length()-1 { + ret += ", " + } + } + ret += " ] " + } + return ret +} + // findTimerById takes a timer id and returns the TimeEntry and the type string // of the entry corresponding to that id // It searches TypeCurrent -> TypeRecent -> TypeArchive -func findTimerById(tmrId int) (*gime.TimeEntry, string, error) { +func findTimerById(tmrId int) (*gime.TimeEntry, int, error) { // Find the timer for this tmrId var prevNum, numLoaded int for i := range gdb.AllTypes { @@ -52,5 +129,123 @@ func findTimerById(tmrId int) (*gime.TimeEntry, string, error) { } prevNum = numLoaded } - return nil, gime.TypeUnknown, errors.New("Unable to find timer with id: " + strconv.Itoa(tmrId)) + return nil, gime.TypeAll, errors.New("Unable to find timer with id: " + strconv.Itoa(tmrId)) +} + +func parseFuzzyTime(t string) (time.Time, error) { + var ret time.Time + var err error + for i := range fuzzyFormats { + ret, err = time.Parse(fuzzyFormats[i], t) + if err == nil { + // Make sure it's in the local timezone + tz := time.Now().Format("Z07:00") + t = ret.Format("2006-01-02T15:04:05") + tz + if ret, err = time.Parse(time.RFC3339, t); err != nil { + return ret, err + } + // Check for zero on year/mo/day + if ret.Year() == 0 && ret.Month() == time.January && ret.Day() == 1 { + ret = ret.AddDate(time.Now().Year(), int(time.Now().Month())-1, time.Now().Day()-1) + } + return ret, nil + } + } + return time.Time{}, errors.New("Unable to parse time: " + t) +} + +func sinceToString(tm time.Time) string { + return diffToString(tm, time.Now()) +} + +func diffToString(tm1, tm2 time.Time) string { + ret := "" + yr, mo, dy, hr, mn, sc := diff(tm1, tm2) + higher := false + + if yr > 0 { + ret += fmt.Sprintf("%4dy ", yr) + higher = true + } + if mo > 0 || higher { + ret += fmt.Sprintf("%2dm ", mo) + higher = true + } + if dy > 0 || higher { + ret += fmt.Sprintf("%2dd ", dy) + higher = true + } + if hr > 0 || higher { + ret += fmt.Sprintf("%2dh ", hr) + higher = true + } + if mn > 0 || higher { + ret += fmt.Sprintf("%2dm ", mn) + higher = true + } + if sc > 0 || higher { + ret += fmt.Sprintf("%2ds", sc) + } + return ret +} + +func padRight(st string, l int) string { + for len(st) < l { + st = st + " " + } + return st +} + +func padLeft(st string, l int) string { + for len(st) < l { + st = " " + st + } + return st +} + +func diff(a, b time.Time) (year, month, day, hour, min, sec int) { + if a.Location() != b.Location() { + b = b.In(a.Location()) + } + if a.After(b) { + a, b = b, a + } + y1, M1, d1 := a.Date() + y2, M2, d2 := b.Date() + + h1, m1, s1 := a.Clock() + h2, m2, s2 := b.Clock() + + year = int(y2 - y1) + month = int(M2 - M1) + day = int(d2 - d1) + hour = int(h2 - h1) + min = int(m2 - m1) + sec = int(s2 - s1) + + // Normalize negative values + if sec < 0 { + sec += 60 + min-- + } + if min < 0 { + min += 60 + hour-- + } + if hour < 0 { + hour += 24 + day-- + } + if day < 0 { + // days in month: + t := time.Date(y1, M1, 32, 0, 0, 0, 0, time.UTC) + day += 32 - t.Day() + month-- + } + if month < 0 { + month += 12 + year-- + } + + return } diff --git a/cmd/gime/main.go b/cmd/gime/main.go index 4bee322..524ecad 100644 --- a/cmd/gime/main.go +++ b/cmd/gime/main.go @@ -3,7 +3,6 @@ package main import ( "fmt" "os" - "strconv" "strings" "time" @@ -19,10 +18,13 @@ const ( ) var validOperations map[string][]string -var activeTimeEntries *gime.TimeEntryCollection +var opFuncs map[string]func([]string) int +var timeEntries *gime.TimeEntryCollection var gdb *gime.GimeDB var cfg *userConfig.Config +var fuzzyFormats []string + func main() { var ret int initialize() @@ -36,30 +38,20 @@ func main() { parms = append(parms, "status") } - switch parms[0] { - case "config": - ret = cmdDoConfig(parms[1:]) - case "delete", "remove": - ret = cmdDeleteTimer(parms[1:]) - case "help": - ret = cmdPrintHelp() - case "status": - ret = cmdPrintStatus() - case "start": - ret = cmdStartTimer(parms[1:]) - case "switch": - ret = cmdSwitchTimer(parms[1:]) - case "end", "stop": - ret = cmdStopTimer(parms[1:]) - case "list", "ls": - ret = cmdPrintList(parms[1:]) - default: + if fn, ok := opFuncs[parms[0]]; ok { + ret = fn(parms[1:]) + } else { fmt.Println("Unknown command") ret = 1 } os.Exit(ret) } +func cmdDoArchive(args []string) int { + fmt.Println("Not implemented yet.") + return 1 +} + func cmdDoConfig(args []string) int { if len(args) == 0 { fmt.Println("Invalid configuration options passed") @@ -103,12 +95,28 @@ func cmdDoConfig(args []string) int { return 0 } -func cmdPrintHelp() int { - fmt.Println("gime - A simple timekeeping application\n") - fmt.Println("Usage: gime [@timerID] [operation] [tags...]") - for _, v := range validOperations { - for vi := range v { - fmt.Println(" ", v[vi]) +func cmdPrintDetail(args []string) int { + fmt.Println("Not implemented yet.") + return 1 +} + +func cmdPrintHelp(args []string) int { + if len(args) == 0 { + fmt.Println("gime - A simple timekeeping application\n") + fmt.Println("Usage: gime [@timerID] [operation] [tags...]") + for _, v := range validOperations { + for vi := range v { + fmt.Println(" ", v[vi]) + } + fmt.Println("") + } + } else { + switch args[0] { + case "formats": + fmt.Println("Supported date/time formats:") + for i := range fuzzyFormats { + fmt.Println(" ", fuzzyFormats[i]) + } } fmt.Println("") } @@ -116,51 +124,48 @@ func cmdPrintHelp() int { } func loadActiveTimeEntries() { - activeTimeEntries = gdb.LoadTimeEntryCollection(gime.TypeCurrent) + timeEntries = gdb.LoadTimeEntryCollection(gime.TypeCurrent) +} + +func loadActiveAndRecentTimeEntries() { + timeEntries = gdb.LoadTimeEntryCollection(gime.TypeNoArchive) } func cmdPrintList(args []string) int { - var err error - loadActiveTimeEntries() - // By default, list all entries for today - currTime := time.Now() - dur := currTime.Hour()*time.Hour + currTime.Minute()*time.Minute - if len(args) < 1 { - + loadActiveAndRecentTimeEntries() + // By default, list all entries ending today or still running + filter := func(t *gime.TimeEntry) bool { + return t.EndsToday() || t.IsRunning() } + fmt.Println(TimerCollectionToString(filterTimerCollection(timeEntries, filter))) return 0 } -func cmdPrintStatus() int { +func cmdPrintStatus(args []string) int { loadActiveTimeEntries() curr := time.Now() fmt.Println("Current Time:", curr.Format(time.Stamp)) - if activeTimeEntries.Length() == 0 { + if timeEntries.Length() == 0 { fmt.Println("No timer running") } else { - fmt.Print("Active Timers (", activeTimeEntries.Length(), ")\n") - for i := 0; i < activeTimeEntries.Length(); i++ { - v := activeTimeEntries.Get(i) - if v.GetStart().Day() == curr.Day() { - fmt.Print(" @"+strconv.Itoa(i)+" ", v.GetStart().Format("15:04"), " ") + fmt.Print("Active Timers (", timeEntries.Length(), ")\n") + // Find the longest start time & longest duration + short := true + for i := 0; i < timeEntries.Length(); i++ { + v := timeEntries.Get(i) + if v.GetStart().Day() != curr.Day() { + short = false + break + } + } + + for i := 0; i < timeEntries.Length(); i++ { + v := timeEntries.Get(i) + if short { + fmt.Printf(" @%d %s\n", i, TimerDetailToString(v)) } else { - fmt.Print(" @"+strconv.Itoa(i)+" ", v.GetStart().Format(time.Stamp), " ") + fmt.Printf(" @%d %s\n", i, TimerDetailToLongString(v)) } - since := time.Since(v.GetStart()).String() - since = strings.Split(since, ".")[0] - fmt.Print("(" + since + "s) ") - fmt.Print(v.GetTags().Length()) - if v.GetTags().Length() > 0 { - fmt.Print(" [ ") - for j := 0; j < v.GetTags().Length(); j++ { - fmt.Print(v.GetTags().Get(j)) - if j < v.GetTags().Length()-1 { - fmt.Print(", ") - } - } - fmt.Print(" ] ") - } - fmt.Println("") } } return 0 @@ -169,6 +174,9 @@ func cmdPrintStatus() int { func initialize() { var err error validOperations = make(map[string][]string) + opFuncs = make(map[string]func([]string) int) + + opFuncs["config"] = cmdDoConfig validOperations["config"] = []string{ "config [command] - Perform configuration", " list - List current configuration", @@ -178,18 +186,28 @@ func initialize() { " dbname=[database filename]", " dbarchname=[archive database filename]", } + + opFuncs["detail"] = cmdPrintDetail validOperations["detail"] = []string{ "detail @id - Print details about a timer", } + + opFuncs["delete"] = cmdDeleteTimer validOperations["delete"] = []string{ "delete @id - Delete a timer", } + + opFuncs["end"] = cmdStopTimer validOperations["end"] = []string{ "end - The same as stop", } + + opFuncs["help"] = cmdPrintHelp validOperations["help"] = []string{ "help - Print this", } + + opFuncs["list"] = cmdPrintList validOperations["list"] = []string{ "list [duration] [+tags] - List time entries", " valid values of [duration] include:", @@ -199,28 +217,48 @@ func initialize() { " year - List all entries for the current year", " To list entries by tag, preceed the tags with a +", } + + opFuncs["ls"] = cmdPrintList validOperations["ls"] = []string{ "ls [duration] [+tags] - The same as list", } + + opFuncs["remove"] = cmdDeleteTimer + validOperations["remove"] = []string{ + "remove @id - See 'delete'", + } + + opFuncs["status"] = cmdPrintStatus validOperations["status"] = []string{ "status - Print the status of all active timers", } + + opFuncs["start"] = cmdStartTimer validOperations["start"] = []string{ "start [time] [tags ...] - Start a timer with the given tags (space separated)", " If the first sub-argument given looks like a time,", " the timer will be started then (past or future).", " If a timer is already running it'll be stopped", } + + opFuncs["stop"] = cmdStopTimer validOperations["stop"] = []string{ "stop [time] - Stops the current timer", " If the first sub-argument given looks like a time,", " the timer will be stopped then (past or future).", } + + opFuncs["switch"] = cmdSwitchTimer validOperations["switch"] = []string{ "switch [tags ...] - Stop all currently running timers and start a new", " one with the given tags", } + opFuncs["archive"] = cmdDoArchive + validOperations["archive"] = []string{ + "archive - Archive all entries older than the archive date", + } + // Load the Config cfg, err = userConfig.NewConfig(AppName) if err != nil { @@ -244,6 +282,21 @@ func initialize() { fmt.Println("Error loading the database") os.Exit(1) } + + fuzzyFormats = []string{ + "15:04", // Kitchen, 24hr + time.Kitchen, + time.RFC3339, + "2006-01-02T15:04:05", // RFC3339 without timezone + "2006-01-02T15:04", // RFC3339 without seconds or timezone + time.Stamp, + "02 Jan 06 15:04:05", // RFC822 with second + time.RFC822, + "01/02/2006 15:04", // U.S. Format + "01/02/2006 15:04:05", // U.S. Format with seconds + "01/02/06 15:04", // U.S. Format, short year + "01/02/06 15:04:05", // U.S. Format, short year, with seconds + } } func matchParameter(in string) string { diff --git a/cmd/gime/timer_operations.go b/cmd/gime/timer_operations.go index 5f9bc7c..fbfdcd4 100644 --- a/cmd/gime/timer_operations.go +++ b/cmd/gime/timer_operations.go @@ -13,11 +13,11 @@ import ( func cmdSwitchTimer(args []string) int { loadActiveTimeEntries() tm := time.Now() - if activeTimeEntries.Length() > 0 { + if timeEntries.Length() > 0 { fmt.Println("Stopped Timers:") } - for i := 0; i < activeTimeEntries.Length(); i++ { - tmr := activeTimeEntries.Get(i) + for i := 0; i < timeEntries.Length(); i++ { + tmr := timeEntries.Get(i) tmr.SetEnd(tm) if err := gdb.UpdateTimeEntry(tmr); err != nil { fmt.Println(err.Error()) @@ -33,16 +33,14 @@ func cmdSwitchTimer(args []string) int { // to be passed along to os.Exit func cmdStartTimer(args []string) int { var err error - tm := time.Now() + var tm time.Time tagStart := 0 if len(args) > 0 { // Check if the first argument looks like a date/time - tm, err = time.Parse("15:04", args[0]) - if err != nil { - tm, err = time.Parse(time.Kitchen, args[0]) - } + tm, err = parseFuzzyTime(args[0]) } - if err != nil { + + if len(args) == 0 || err != nil { // Just start it now tm = time.Now() } else { @@ -72,46 +70,65 @@ func cmdStartTimer(args []string) int { // cmdStopTimer takes parameters that describe which times to stop func cmdStopTimer(args []string) int { + // args[0] should either be a timer id (starting with '@') or 'all' var err error tm := time.Now() actTimers := gdb.LoadTimeEntryCollection(gime.TypeCurrent) - if actTimers.Length() != 1 && (len(args) < 1 || args[0][0] != '@') { - fmt.Println("Couldn't determine which timer(s) to stop") - return 1 - } var tmr *gime.TimeEntry - if actTimers.Length() == 1 { - // only one timer running - tmr = actTimers.Get(0) - } else { - // 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()) + stopAll := len(args) > 0 && args[0] == "all" + + if !stopAll { + if actTimers.Length() != 1 && (len(args) < 1 || args[0][0] != '@') { + fmt.Println("Couldn't determine which timer(s) to stop") return 1 } - tmr, _, err = findTimerById(timerId) + if actTimers.Length() == 1 { + // only one timer running + tmr = actTimers.Get(0) + } else { + // 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, _, err = findTimerById(timerId) + if err != nil { + fmt.Println(err.Error()) + return 1 + } + } + } + + if len(args) > 1 { + // Check if the next argument looks like a date/time + tm, err = parseFuzzyTime(args[1]) if err != nil { fmt.Println(err.Error()) return 1 } } - if len(args) > 0 { - // Check if the first argument looks like a date/time - tm, err = time.Parse("15:04", args[0]) - if err != nil { - tm, err = time.Parse(time.Kitchen, args[0]) + 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 } - - tmr.SetEnd(tm) - if err = gdb.UpdateTimeEntry(tmr); err != nil { - fmt.Println(err.Error()) - return 1 + if stopAll { + 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 } - fmt.Println(TimerToString(tmr)) - return 0 + return stopTimer(tmr, tm) } // cmdDeleteTimer takes parameters that describe the timers to be deleted. @@ -134,7 +151,7 @@ func cmdDeleteTimer(args []string) int { return 1 } if gdb.RemoveTimeEntry(tmr.GetUUID()) != nil { - fmt.Println("Error removing entry " + tp + "." + tmr.GetUUID()) + fmt.Println("Error removing entry " + gime.TypeToString(tp) + "." + tmr.GetUUID()) return 1 } fmt.Println("Deleted Time Entry: " + TimerToString(tmr)) diff --git a/model.go b/model.go index fb238d5..d23222c 100644 --- a/model.go +++ b/model.go @@ -16,18 +16,31 @@ type GimeDB struct { path string filename, arch string - AllTypes []string + AllTypes []int } const ( - TypeCurrent = "current" - TypeRecent = "recent" - TypeArchive = "archive" - TypeUnknown = "unknown" // Not really a type, used for searches and stuff + TypeCurrent = 1 // 001 + TypeRecent = 2 // 010 + TypeNoArchive = 3 // 011 + TypeArchive = 4 // 100 + TypeAll = 7 // 111 ArchiveDays = time.Hour * 24 * 90 // Archive anything older than 90 days ) +func TypeToString(tp int) string { + switch tp { + case TypeCurrent: + return "current" + case TypeRecent: + return "recent" + case TypeArchive: + return "archive" + } + return "unknown" +} + // Load Database returns a database loaded off the files given // name and archName located in path func LoadDatabase(path, name, archName string) (*GimeDB, error) { @@ -38,7 +51,7 @@ func LoadDatabase(path, name, archName string) (*GimeDB, error) { path: path, filename: name, arch: archName, - AllTypes: []string{TypeCurrent, TypeRecent, TypeArchive}, + AllTypes: []int{TypeCurrent, TypeRecent, TypeArchive}, } if err := gdb.initDatabase(); err != nil { fmt.Println(err.Error()) @@ -48,7 +61,7 @@ func LoadDatabase(path, name, archName string) (*GimeDB, error) { } // Load a TimeEntry collection from a database -func (gdb *GimeDB) LoadTimeEntryCollection(tp string) *TimeEntryCollection { +func (gdb *GimeDB) LoadTimeEntryCollection(tp int) *TimeEntryCollection { ret := new(TimeEntryCollection) entries := gdb.dbGetAllTimeEntries(tp) for i := range entries { @@ -109,11 +122,11 @@ func (gdb *GimeDB) initDatabase() error { return err } // Create the path to the bucket to store the current time entry - if err := gdb.db.MkBucketPath([]string{TypeCurrent}); err != nil { + if err := gdb.db.MkBucketPath([]string{TypeToString(TypeCurrent)}); err != nil { return err } // Create the path to the bucket to store recent time entries - if err := gdb.db.MkBucketPath([]string{TypeRecent}); err != nil { + if err := gdb.db.MkBucketPath([]string{TypeToString(TypeRecent)}); err != nil { return err } @@ -123,10 +136,10 @@ func (gdb *GimeDB) initDatabase() error { } defer gdb.closeArchiveDatabase() // Create the path to the bucket to store archived time entries - return gdb.archDb.MkBucketPath([]string{TypeArchive}) + return gdb.archDb.MkBucketPath([]string{TypeToString(TypeArchive)}) } -func (gdb *GimeDB) openDBType(tp string) (*boltease.DB, error) { +func (gdb *GimeDB) openDBType(tp int) (*boltease.DB, error) { var err error if tp == TypeCurrent || tp == TypeRecent { if err = gdb.openDatabase(); err != nil { @@ -139,14 +152,14 @@ func (gdb *GimeDB) openDBType(tp string) (*boltease.DB, error) { } return gdb.archDb, err } - return nil, errors.New("Invalid database type: " + tp) + return nil, errors.New("Invalid database type: " + TypeToString(tp)) } -func (gdb *GimeDB) closeDBType(tp string) error { +func (gdb *GimeDB) closeDBType(tp int) error { if tp == TypeCurrent || tp == TypeRecent { return gdb.closeDatabase() } else if tp == TypeArchive { return gdb.closeArchiveDatabase() } - return errors.New("Invalid database type: " + tp) + return errors.New("Invalid database type: " + TypeToString(tp)) } diff --git a/model_timeentry.go b/model_timeentry.go index c485013..f31a97b 100644 --- a/model_timeentry.go +++ b/model_timeentry.go @@ -11,7 +11,7 @@ import ( // FindTimeEntryByUUID searches all entries // for the time entry with the given uuid, return the TimeEntry, the type, and/or and error // Types are searched TypeCurrent -> TypeRecent -> TypeArchive -func (gdb *GimeDB) FindTimeEntryByUUID(uuid string) (*TimeEntry, string, error) { +func (gdb *GimeDB) FindTimeEntryByUUID(uuid string) (*TimeEntry, int, error) { for i := range gdb.AllTypes { timeCollection := gdb.LoadTimeEntryCollection(gdb.AllTypes[i]) for j := 0; j < timeCollection.Length(); j++ { @@ -20,7 +20,7 @@ func (gdb *GimeDB) FindTimeEntryByUUID(uuid string) (*TimeEntry, string, error) } } } - return nil, TypeUnknown, errors.New("Unable to find time entry with uuid " + uuid) + return nil, TypeAll, errors.New("Unable to find time entry with uuid " + uuid) } // SaveTimeEntry creates a time entry in the database @@ -43,7 +43,7 @@ func (gdb *GimeDB) SaveTimeEntry(te *TimeEntry) error { if err != nil { return err } - tePath := []string{tp, te.start.Format(time.RFC3339)} + tePath := []string{TypeToString(tp), te.start.Format(time.RFC3339)} if err = useDb.SetValue(tePath, "uuid", te.uuid); err != nil { return err } @@ -87,7 +87,7 @@ func (gdb *GimeDB) RemoveTimeEntry(uuid string) error { if err != nil { return err } - return useDb.DeleteBucket([]string{tp}, fndEntry.start.Format(time.RFC3339)) + return useDb.DeleteBucket([]string{TypeToString(tp)}, fndEntry.start.Format(time.RFC3339)) } // GetTimeEntriesInRange takes two times and returns all time entries that occur @@ -101,7 +101,7 @@ func (gdb *GimeDB) GetTimeEntriesInRange(st time.Time, end time.Time) *TimeEntry } defer gdb.closeArchiveDatabase() var sttimes []string - if sttimes, err = gdb.db.GetBucketList([]string{TypeArchive}); err != nil { + if sttimes, err = gdb.db.GetBucketList([]string{TypeToString(TypeArchive)}); err != nil { return ret } for i := len(sttimes) - 1; i > 0; i-- { @@ -116,16 +116,21 @@ func (gdb *GimeDB) GetTimeEntriesInRange(st time.Time, end time.Time) *TimeEntry // TypeCurrent = "current" // TypeRecent = "recent" // TypeArchive = "archive" -// TypeUnknown = "unknown" - Returns _ALL_ entries +// TypeAll = "unknown" - Returns _ALL_ entries // Getting all archived time entries has the potential to be a lot of data -func (gdb *GimeDB) dbGetAllTimeEntries(tp string) []TimeEntry { +func (gdb *GimeDB) dbGetAllTimeEntries(tp int) []TimeEntry { var ret []TimeEntry - if tp == TypeUnknown { + if tp == TypeAll { for _, v := range gdb.AllTypes { ret = append(ret, gdb.dbGetAllTimeEntries(v)...) } return ret + } else if tp == TypeNoArchive { + ret = append(ret, gdb.dbGetAllTimeEntries(TypeCurrent)...) + ret = append(ret, gdb.dbGetAllTimeEntries(TypeRecent)...) + return ret } + var useDb *boltease.DB var err error if useDb, err = gdb.openDBType(tp); err != nil { @@ -134,7 +139,7 @@ func (gdb *GimeDB) dbGetAllTimeEntries(tp string) []TimeEntry { defer gdb.closeDBType(tp) var sttimes []string - if sttimes, err = useDb.GetBucketList([]string{tp}); err != nil { + if sttimes, err = useDb.GetBucketList([]string{TypeToString(tp)}); err != nil { return ret } for _, v := range sttimes { @@ -147,10 +152,10 @@ func (gdb *GimeDB) dbGetAllTimeEntries(tp string) []TimeEntry { // dbGetTimeEntry pulls a time entry of type tp with the given start time // from the db and returns it. -func (gdb *GimeDB) dbGetTimeEntry(tp, sttm string) (*TimeEntry, error) { +func (gdb *GimeDB) dbGetTimeEntry(tp int, sttm string) (*TimeEntry, error) { var ret *TimeEntry var err error - if tp == TypeUnknown { + if tp == TypeAll { // Loop through the types and return the first entry found that matches for _, v := range gdb.AllTypes { ret, _ = gdb.dbGetTimeEntry(v, sttm) @@ -166,7 +171,7 @@ func (gdb *GimeDB) dbGetTimeEntry(tp, sttm string) (*TimeEntry, error) { } defer gdb.closeDBType(tp) - entryPath := []string{tp, sttm} + entryPath := []string{TypeToString(tp), sttm} ret = new(TimeEntry) if ret.uuid, err = useDb.GetValue(entryPath, "uuid"); err != nil { return nil, errors.New("Unable to read entry uuid") diff --git a/timeentry.go b/timeentry.go index 9c06a64..0685b5a 100644 --- a/timeentry.go +++ b/timeentry.go @@ -106,15 +106,20 @@ func (t *TimeEntry) Equals(tst *TimeEntry) bool { // StartsToday returns if the timer's start time is today func (t *TimeEntry) StartsToday() bool { currTime := time.Now() - dur := currTime.Hour()*time.Hour + currTime.Minute()*time.Minute - return time.Since(t.GetStart()) < dur + dur := currTime.Hour()*int(time.Hour) + currTime.Minute()*int(time.Minute) + return int(time.Since(t.GetStart())) < dur } // StartsToday returns if the timer's end time is today func (t *TimeEntry) EndsToday() bool { currTime := time.Now() - dur := currTime.Hour()*time.Hour + currTime.Minute()*time.Minute - return time.Since(t.GetEnd()) < dur + dur := currTime.Hour()*int(time.Hour) + currTime.Minute()*int(time.Minute) + return int(time.Since(t.GetEnd())) < dur +} + +// IsRunning returns if the timer is still running +func (t *TimeEntry) IsRunning() bool { + return t.GetEnd().IsZero() } // String formats a string of the time entry