Several improvements
Fix 'switch' issue Archive by tag Much faster archiving
This commit is contained in:
parent
f0c8bcb408
commit
dbd838e77f
2
Godeps/Godeps.json
generated
2
Godeps/Godeps.json
generated
@ -5,7 +5,7 @@
|
||||
"Deps": [
|
||||
{
|
||||
"ImportPath": "git.bullercodeworks.com/brian/gime-lib",
|
||||
"Rev": "579fc6c62aa8f5452e02d3fe3334e2d035b0b712"
|
||||
"Rev": "2a2aea8641e8b327544452f900ea4f5fcc43fc7e"
|
||||
},
|
||||
{
|
||||
"ImportPath": "github.com/BurntSushi/toml",
|
||||
|
30
helpers.go
30
helpers.go
@ -145,6 +145,20 @@ func findIdOfTimer(tmr *gime.TimeEntry) (int, error) {
|
||||
return -1, errors.New("Unable to find timer")
|
||||
}
|
||||
|
||||
// pullTagsFromArgs takes a list of arguments, removes all tags from them
|
||||
// then returns the tags and the remaining args
|
||||
func pullTagsFromArgs(args []string) ([]string, []string) {
|
||||
var tags, rem []string
|
||||
for _, opt := range args {
|
||||
if opt[0] == '+' {
|
||||
tags = append(tags, opt[1:])
|
||||
} else {
|
||||
rem = append(rem, opt)
|
||||
}
|
||||
}
|
||||
return tags, rem
|
||||
}
|
||||
|
||||
func parseFuzzyTime(t string) (time.Time, error) {
|
||||
var ret time.Time
|
||||
var err error
|
||||
@ -167,6 +181,22 @@ func parseFuzzyTime(t string) (time.Time, error) {
|
||||
return time.Time{}, errors.New("Unable to parse time: " + t)
|
||||
}
|
||||
|
||||
func friendlyFormatForTime(t time.Time) string {
|
||||
nowTime := time.Now()
|
||||
if t.Year() != nowTime.Year() || t.Month() != nowTime.Month() {
|
||||
return "2006-01-02 15:04"
|
||||
} else if t.Day() != nowTime.Day() {
|
||||
return "01/02 15:04"
|
||||
}
|
||||
return "15:04"
|
||||
}
|
||||
|
||||
// timeToFriendlyString returns an easier to read version of the time
|
||||
// giving enough details that the user should be fine inferring the rest
|
||||
func timeToFriendlyString(t time.Time) string {
|
||||
return t.Format(friendlyFormatForTime(t))
|
||||
}
|
||||
|
||||
func sinceToString(tm time.Time) string {
|
||||
return diffToString(tm, time.Now())
|
||||
}
|
||||
|
9
main.go
9
main.go
@ -244,10 +244,11 @@ func initialize() {
|
||||
validOperations["list"] = []string{
|
||||
"list [duration] [+tags] - List time entries",
|
||||
" valid values of [duration] include:",
|
||||
" day - List all entries for the current day",
|
||||
" week - List all entries for the current week",
|
||||
" month - List all entries for the current month",
|
||||
" year - List all entries for the current year",
|
||||
" :day - List all entries for the current day",
|
||||
" :week - List all entries for the current week",
|
||||
" :month - List all entries for the current month",
|
||||
" :year - List all entries for the current year",
|
||||
" Or other date values, we'll try to parse it.",
|
||||
" To list entries by tag, preceed the tags with a +",
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,39 @@ import (
|
||||
"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(" " + 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()
|
||||
@ -19,7 +52,7 @@ func cmdContinueTimer(args []string) int {
|
||||
tagColl := te.GetTags()
|
||||
var tags []string
|
||||
for i := 0; i < tagColl.Length(); i++ {
|
||||
tags = append(tags, tagColl.Get(i))
|
||||
tags = append(tags, "+"+tagColl.Get(i))
|
||||
}
|
||||
args = append(args, tags...)
|
||||
return cmdStartTimer(args)
|
||||
@ -28,73 +61,40 @@ func cmdContinueTimer(args []string) int {
|
||||
// switchTimer performs a stop on any currently running timers
|
||||
// and starts a new timer with the given arguments
|
||||
func cmdSwitchTimer(args []string) int {
|
||||
loadActiveTimeEntries()
|
||||
tm := time.Now()
|
||||
if timeEntries.Length() > 0 {
|
||||
fmt.Println("Stopped Timers:")
|
||||
}
|
||||
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())
|
||||
return 1
|
||||
var foundId bool
|
||||
for i := range args {
|
||||
// see if we have a timer id in the args
|
||||
if args[i][0] == '@' {
|
||||
foundId = true
|
||||
}
|
||||
fmt.Println(" " + TimerToString(tmr))
|
||||
}
|
||||
stopArgs := make([]string, len(args))
|
||||
copy(stopArgs, args)
|
||||
if !foundId {
|
||||
// We didn't find one, so make sure we stop _all_ timers
|
||||
stopArgs = append(stopArgs, "@all")
|
||||
}
|
||||
fmt.Println(stopArgs)
|
||||
fmt.Println(args)
|
||||
return 0
|
||||
if cmdStopTimer(args) != 0 {
|
||||
// Error while stopping timers
|
||||
return 1
|
||||
}
|
||||
fmt.Println("Started Timer:")
|
||||
return cmdStartTimer(args)
|
||||
}
|
||||
|
||||
// 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 tm time.Time
|
||||
tagStart := 0
|
||||
if len(args) > 0 {
|
||||
// Check if the first argument looks like a date/time
|
||||
tm, err = parseFuzzyTime(args[0])
|
||||
}
|
||||
|
||||
if len(args) == 0 || err != nil {
|
||||
// Just start it now
|
||||
tm = time.Now()
|
||||
} else {
|
||||
tagStart = 1
|
||||
}
|
||||
var entry *gime.TimeEntry
|
||||
|
||||
var timerArgs []string
|
||||
if tagStart < len(args) {
|
||||
timerArgs = args[tagStart:]
|
||||
}
|
||||
tc := new(gime.TagCollection)
|
||||
for i := range timerArgs {
|
||||
tc.Push(timerArgs[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(" " + TimerToString(entry))
|
||||
return 0
|
||||
}
|
||||
|
||||
// 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'
|
||||
// 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] == "all" || args[i][0] == '@' {
|
||||
if args[i][0] == '@' {
|
||||
stopId = args[i]
|
||||
continue
|
||||
}
|
||||
@ -105,7 +105,7 @@ func cmdStopTimer(args []string) int {
|
||||
continue
|
||||
}
|
||||
}
|
||||
if stopId != "all" {
|
||||
if stopId != "@all" {
|
||||
// Find the timer that we're stopping
|
||||
timerId, err := strconv.Atoi(stopId[1:])
|
||||
if err != nil {
|
||||
@ -128,7 +128,7 @@ func cmdStopTimer(args []string) int {
|
||||
fmt.Println("Stopped:", InferTimerDetailString(tmr))
|
||||
return 0
|
||||
}
|
||||
if stopId == "all" {
|
||||
if stopId == "@all" {
|
||||
var ret int
|
||||
for i := 0; i < actTimers.Length(); i++ {
|
||||
ret += stopTimer(actTimers.Get(i), tm)
|
||||
@ -161,7 +161,7 @@ func cmdDeleteTimer(args []string) int {
|
||||
fmt.Println(err.Error())
|
||||
return 1
|
||||
}
|
||||
if gdb.RemoveTimeEntry(tmr.GetUUID()) != nil {
|
||||
if gdb.RemoveTimeEntry(tmr) != nil {
|
||||
fmt.Println("Error removing entry " + gime.TypeToString(tp) + "." + tmr.GetUUID())
|
||||
return 1
|
||||
}
|
||||
@ -179,12 +179,35 @@ func cmdPrintList(args []string) int {
|
||||
var tmpBeg, tmpEnd time.Time
|
||||
// Check for command modifiers
|
||||
if strings.HasPrefix(opt, ":") {
|
||||
if opt == ":ids" {
|
||||
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
|
||||
}
|
||||
|
||||
// Find tags
|
||||
if strings.HasPrefix(opt, "+") {
|
||||
searchTags = append(searchTags, opt[1:])
|
||||
continue
|
||||
}
|
||||
|
||||
// Do our best to figure out what timers the user wants to list
|
||||
var err error
|
||||
if strings.Contains(opt, "-") {
|
||||
@ -194,9 +217,6 @@ func cmdPrintList(args []string) int {
|
||||
// This should be the starting date
|
||||
tmpBeg, err = parseFuzzyTime(pts[0])
|
||||
if err != nil {
|
||||
// We couldn't parse it as a time,
|
||||
// Probably this is just a tag
|
||||
searchTags = append(searchTags, opt)
|
||||
continue
|
||||
}
|
||||
}
|
||||
@ -204,13 +224,9 @@ func cmdPrintList(args []string) int {
|
||||
// This should be the ending date
|
||||
tmpEnd, err = parseFuzzyTime(pts[1])
|
||||
if err != nil {
|
||||
searchTags = append(searchTags, opt)
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// Tag filters
|
||||
searchTags = append(searchTags, opt)
|
||||
}
|
||||
if !tmpBeg.IsZero() || !tmpEnd.IsZero() {
|
||||
beg, end = tmpBeg, tmpEnd
|
||||
@ -248,10 +264,19 @@ func cmdPrintList(args []string) int {
|
||||
|
||||
dayStr := ""
|
||||
timers := filterTimerCollection(timeEntries, compoundFilter)
|
||||
_ = dayStr
|
||||
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
|
||||
@ -283,19 +308,49 @@ func cmdDoArchive(args []string) int {
|
||||
fmt.Println("Nothing to do")
|
||||
return 1
|
||||
}
|
||||
bef, err := parseFuzzyTime(args[0])
|
||||
if err != nil {
|
||||
fmt.Println("Error parsing time")
|
||||
|
||||
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
|
||||
fmt.Print("Archive all timers before ", bef)
|
||||
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)
|
||||
if tst.GetEnd().Before(bef) {
|
||||
archIt := false
|
||||
if !bef.IsZero() {
|
||||
archIt = tst.GetEnd().Before(bef)
|
||||
} else {
|
||||
archIt = true
|
||||
}
|
||||
if archIt {
|
||||
fmt.Print(".")
|
||||
if err = gdb.ArchiveTimeEntry(tst.GetUUID()); err != nil {
|
||||
if err = gdb.ArchiveTimeEntry(tst); err != nil {
|
||||
fmt.Print("Error archiving entry (", tst.GetUUID(), ")", err.Error())
|
||||
ret = 1
|
||||
}
|
||||
|
1
vendor/git.bullercodeworks.com/brian/gime-lib/model.go
generated
vendored
1
vendor/git.bullercodeworks.com/brian/gime-lib/model.go
generated
vendored
@ -25,6 +25,7 @@ const (
|
||||
TypeNoArchive = 3 // 011
|
||||
TypeArchive = 4 // 100
|
||||
TypeAll = 7 // 111
|
||||
TypeError = 8 //1000
|
||||
|
||||
ArchiveDays = time.Hour * 24 * 90 // Archive anything older than 90 days
|
||||
)
|
||||
|
81
vendor/git.bullercodeworks.com/brian/gime-lib/model_timeentry.go
generated
vendored
81
vendor/git.bullercodeworks.com/brian/gime-lib/model_timeentry.go
generated
vendored
@ -13,21 +13,25 @@ import (
|
||||
func (gdb *GimeDB) GetLatestTimeEntry() (*TimeEntry, error) {
|
||||
var ret *TimeEntry
|
||||
var err error
|
||||
tc := gdb.LoadTimeEntryCollection(TypeRecent)
|
||||
for i := 0; i < tc.Length(); i++ {
|
||||
te := tc.Get(i)
|
||||
if ret == nil {
|
||||
ret = te
|
||||
} else {
|
||||
if te.GetEnd().After(ret.GetEnd()) {
|
||||
ret = te
|
||||
}
|
||||
}
|
||||
|
||||
var useDb *boltease.DB
|
||||
if useDb, err = gdb.openDBType(TypeRecent); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
if ret == nil {
|
||||
err = errors.New("Could not find latest time entry")
|
||||
defer gdb.closeDBType(TypeRecent)
|
||||
|
||||
var sttimes []string
|
||||
if sttimes, err = useDb.GetBucketList([]string{TypeToString(TypeRecent)}); err != nil {
|
||||
return ret, err
|
||||
}
|
||||
return ret, err
|
||||
sort.Slice(sttimes, func(i, j int) bool {
|
||||
return sttimes[j] < sttimes[i]
|
||||
})
|
||||
// The first entry should be the most recent
|
||||
if len(sttimes) > 0 {
|
||||
return gdb.dbGetTimeEntry(TypeRecent, sttimes[0])
|
||||
}
|
||||
return nil, errors.New("No recent time entries found")
|
||||
}
|
||||
|
||||
// findTimeEntryAndTypeByUUID searches all entries
|
||||
@ -87,13 +91,20 @@ func (gdb *GimeDB) SaveTimeEntryType(tp int, te *TimeEntry) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (gdb *GimeDB) ArchiveTimeEntry(te *TimeEntry) error {
|
||||
if err := gdb.RemoveTimeEntryFromCategory(te, TypeRecent); err != nil {
|
||||
return err
|
||||
}
|
||||
return gdb.SaveTimeEntryType(TypeArchive, te)
|
||||
}
|
||||
|
||||
// ArchiveTimeEntry takes a time from TypeRecent and moves it to TypeArchive
|
||||
func (gdb *GimeDB) ArchiveTimeEntry(uuid string) error {
|
||||
func (gdb *GimeDB) ArchiveTimeEntryByUUID(uuid string) error {
|
||||
archTime, tp, err := gdb.findTimeEntryAndTypeByUUID(uuid)
|
||||
if tp != TypeRecent {
|
||||
return errors.New("Couldn't find timer to archive in the 'Recent' bucket")
|
||||
}
|
||||
if err = gdb.RemoveTimeEntry(archTime.uuid); err != nil {
|
||||
if err = gdb.RemoveTimeEntry(archTime); err != nil {
|
||||
return err
|
||||
}
|
||||
return gdb.SaveTimeEntryType(TypeArchive, archTime)
|
||||
@ -106,14 +117,36 @@ func (gdb *GimeDB) UpdateTimeEntry(te *TimeEntry) error {
|
||||
if te.uuid == "" {
|
||||
return errors.New("Given time entry has no uuid")
|
||||
}
|
||||
if err = gdb.RemoveTimeEntry(te.uuid); err != nil {
|
||||
if err = gdb.RemoveTimeEntry(te); err != nil {
|
||||
return err
|
||||
}
|
||||
return gdb.SaveTimeEntry(te)
|
||||
}
|
||||
|
||||
// RemoveTimeEntry removes a time entry with the given uuid from the database
|
||||
func (gdb *GimeDB) RemoveTimeEntry(uuid string) error {
|
||||
func (gdb *GimeDB) RemoveTimeEntry(te *TimeEntry) error {
|
||||
for _, v := range gdb.AllTypes {
|
||||
if gdb.RemoveTimeEntryFromCategory(te, v) == nil {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return errors.New("Couldn't find Time Entry to Remove")
|
||||
}
|
||||
|
||||
func (gdb *GimeDB) RemoveTimeEntryFromCategory(te *TimeEntry, tp int) error {
|
||||
if !gdb.dbTimeEntryIsInCategory(te, tp) {
|
||||
return errors.New("Couldn't find timer to remove in the given bucket")
|
||||
}
|
||||
var err error
|
||||
var useDb *boltease.DB
|
||||
if useDb, err = gdb.openDBType(tp); err != nil {
|
||||
return err
|
||||
}
|
||||
defer gdb.closeDBType(tp)
|
||||
return useDb.DeleteBucket([]string{TypeToString(tp)}, te.start.Format(time.RFC3339))
|
||||
}
|
||||
|
||||
func (gdb *GimeDB) RemoveTimeEntryByUUID(uuid string) error {
|
||||
fndEntry, tp, err := gdb.findTimeEntryAndTypeByUUID(uuid)
|
||||
if err != nil {
|
||||
return errors.New("Unable to find time entry with uuid " + uuid)
|
||||
@ -193,6 +226,20 @@ func (gdb *GimeDB) dbGetAllTimeEntries(tp int) []TimeEntry {
|
||||
return ret
|
||||
}
|
||||
|
||||
func (gdb *GimeDB) dbFindTimeEntryCategory(te *TimeEntry) int {
|
||||
for _, v := range gdb.AllTypes {
|
||||
if gdb.dbTimeEntryIsInCategory(te, v) {
|
||||
return v
|
||||
}
|
||||
}
|
||||
return TypeError
|
||||
}
|
||||
|
||||
func (gdb *GimeDB) dbTimeEntryIsInCategory(te *TimeEntry, tp int) bool {
|
||||
_, err := gdb.dbGetTimeEntry(tp, te.start.Format(time.RFC3339))
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// dbGetTimeEntry pulls a time entry of type tp with the given start time
|
||||
// from the db and returns it.
|
||||
func (gdb *GimeDB) dbGetTimeEntry(tp int, sttm string) (*TimeEntry, error) {
|
||||
|
Loading…
Reference in New Issue
Block a user