diff --git a/app_state.go b/app_state.go index 893836b..cae742d 100644 --- a/app_state.go +++ b/app_state.go @@ -5,7 +5,7 @@ import ( "os" "strings" - timertxt "git.bullercodeworks.com/brian/go-timertxt" + timertxt "github.com/br0xen/go-timertxt" "github.com/br0xen/user-config" ) @@ -109,64 +109,89 @@ func (a *AppState) initialize() { a.OpFuncs = make(map[string]func([]string) int) a.addOperation("ls", []string{ - "ls - List Timers", + "ls [--a] [start] [end] [contexts] [projects] - List Timers", + " --a - Include done.txt file", + " [start] - List entries after this date", + " [end] - List entries before this date", + " [@contexts] - List entries with the given contexts", + " [+projects] - List entries with the given projects", }, a.opListTimers, ) a.addOperation("lsa", []string{ - "lsa - The same as 'ls -a'", + "lsa [start] [end] [contexts] [projects] - Shortcut for 'ls --a'", }, func(args []string) int { - return a.opListTimers(append([]string{"-a"}, args...)) + return a.opListTimers(append([]string{"--a"}, args...)) }, ) a.addOperation("start", []string{ "start [time] [@contexts...] [+projects...] [tag:value...]", - " - Start a timer with the given details", - " If the first argument looks like a time,", - " the timer will be started then (past or future)", + " - Start a timer with the given details", + " If the first argument looks like a time,", + " the timer will be started then (past or future)", }, a.opStartTimer, ) a.addOperation("stop", []string{ - "stop [time] - Stops the current timer", - " If the first argument looks like a time,", - " the timer will be stopped then (past or future)", + "stop [time] - Stops the current timer", + " If the first argument looks like a time,", + " the timer will be stopped then (past or future)", }, a.opStopTimer, ) + a.addOperation("switch", + []string{ + "switch [time] [@contexts...] [+projects...] [tag:value...] - Stops all active timers and starts a new one", + " with the given arguments", + }, + a.opSwitchTimer, + ) + a.addOperation("archive", + []string{ + "archive [id] - Archive the timer with the given id", + }, + a.opArchiveTimer, + ) a.addOperation("status", []string{ - "status - Prints the status of all active timers", + "status - Prints the status of all active timers", }, a.opStatus, ) + a.addOperation("mod", + []string{ + "mod [start=] [end=] [projects=] [contexts=]", + " - Prints the status of all active timers", + }, + a.opModifyTimer, + ) a.addOperation("fuzzyparse", []string{ - "fuzzyparse [date string] - Parses the passed string and print the RFC3339 result (for testing)", + "fuzzyparse [date string] - Parses the passed string and print the RFC3339 result (for testing)", }, a.opFuzzyParse, ) a.addOperation("--reinit", - []string{"--reinit - Reset all Configuration Settings"}, + []string{"--reinit - Reset all Configuration Settings"}, func(args []string) int { a.initializeConfig() return 0 }, ) a.addOperation("-h", - []string{"-h - Print this message"}, + []string{"-h - Print this message"}, a.opPrintUsage, ) a.addOperation("help", - []string{"help - Print this message"}, + []string{"help - Print this message"}, a.opPrintUsage, ) a.addOperation("--h", - []string{"--h - Print this message"}, + []string{"--h - Print this message"}, a.opPrintUsage, ) a.directory = a.config.Get("directory") diff --git a/helpers.go b/helpers.go index 257ea8c..bf697b9 100644 --- a/helpers.go +++ b/helpers.go @@ -6,7 +6,7 @@ import ( "strings" "time" - timertxt "git.bullercodeworks.com/brian/go-timertxt" + timertxt "github.com/br0xen/go-timertxt" ) func GetRoundToDuration() time.Duration { @@ -118,7 +118,14 @@ func timerToFriendlyString(t *timertxt.Timer) string { for _, v := range t.Projects { projects += "+" + v + " " } - return fmt.Sprintf("% 2d. %s - %s [ %s] [ %s] %s", t.Id, start, end, contexts, projects, t.Notes) + var dur time.Duration + if t.FinishDate.IsZero() { + dur = time.Now().Sub(t.StartDate) + } else { + dur = t.FinishDate.Sub(t.StartDate) + } + dur = dur.Round(GetRoundToDuration()) + return fmt.Sprintf("% 2d. %s - %s [ %s] [ %s] %s ( %.2f )", t.Id, start, end, contexts, projects, t.Notes, DurationToDecimal(dur)) } func friendlyFormatForTime(t time.Time) string { diff --git a/model.go b/model.go index 71c5c3e..34f954f 100644 --- a/model.go +++ b/model.go @@ -4,7 +4,7 @@ import ( "strings" "time" - timertxt "git.bullercodeworks.com/brian/go-timertxt" + timertxt "github.com/br0xen/go-timertxt" ) func (a *AppState) SetTimerFinished(id int, end time.Time) error { diff --git a/screen.go b/screen.go index 56537c3..1750e29 100644 --- a/screen.go +++ b/screen.go @@ -1,6 +1,9 @@ package main import ( + "errors" + "time" + termbox "github.com/nsf/termbox-go" ) @@ -46,15 +49,19 @@ func readUserInput(e chan termbox.Event) { } } -func refreshList(e chan termbox.Event) { - /* - for { - time.Sleep(5 * time.Minute) - app.LoadTasklist() - app.LoadDoneList() - e <- termbox.Event{Type: termbox.EventNone} +func checkForUpdate(e chan termbox.Event) { + for { + time.Sleep(time.Minute) + // Check if the on-disk tasklist has changed + //app.LoadTasklist() + //app.LoadDoneList() + if false { + e <- termbox.Event{ + Type: termbox.EventError, + Err: errors.New("List changed elsewhere"), + } } - */ + } } /* diff --git a/screen_main.go b/screen_main.go index e38671c..885e50d 100644 --- a/screen_main.go +++ b/screen_main.go @@ -5,7 +5,7 @@ import ( "strings" "time" - timertxt "git.bullercodeworks.com/brian/go-timertxt" + timertxt "github.com/br0xen/go-timertxt" "github.com/br0xen/termbox-util" termbox "github.com/nsf/termbox-go" ) diff --git a/timer_ops.go b/timer_ops.go index 3676892..1131e32 100644 --- a/timer_ops.go +++ b/timer_ops.go @@ -6,7 +6,7 @@ import ( "strings" "time" - timertxt "git.bullercodeworks.com/brian/go-timertxt" + timertxt "github.com/br0xen/go-timertxt" ) func (a *AppState) opStatus(args []string) int { @@ -33,21 +33,33 @@ func (a *AppState) opStatus(args []string) int { * By default, only list Today */ func (a *AppState) opListTimers(args []string) int { - var start, end time.Time + var includeArchive bool var err error + start := time.Time{} + end := time.Now() + var contextFilters []string + var projectFilters []string + + var allFilters []func(timertxt.Timer) bool + if len(args) > 0 { + contextFilters, args = getContextsFromSlice(args) + projectFilters, args = getProjectsFromSlice(args) + } if len(args) > 0 { if args[0] == "--a" { - start = time.Time{} - end = time.Now() + includeArchive = true args = args[1:] + } + } + if len(args) > 0 { + if start, err = parseFuzzyTime(args[0]); err != nil { + y, m, d := time.Now().Date() + start = time.Date(y, m, d, 0, 0, 0, 0, time.Now().Location()) } else { - if start, err = parseFuzzyTime(args[0]); err != nil { - y, m, d := time.Now().Date() - start = time.Date(y, m, d, 0, 0, 0, 0, time.Now().Location()) - } else { - args = args[1:] - } + args = args[1:] + } + if len(args) > 0 { if end, err = parseFuzzyTime(args[0]); err != nil { y, m, d := time.Now().Date() end = time.Date(y, m, d, 23, 59, 59, 0, time.Now().Location()) @@ -56,8 +68,51 @@ func (a *AppState) opListTimers(args []string) int { } } } + if includeArchive { + if err = a.LoadDoneList(); err != nil { + fmt.Println("Error loading done.txt entries") + fmt.Println(err.Error()) + return 1 + } + } list := a.TimerList.GetTimersInRange(start, end) + if includeArchive { + *list = append(*list, (*a.DoneList.GetTimersInRange(start, end))...) + } + if len(contextFilters) > 0 { + allFilters = append(allFilters, func(t timertxt.Timer) bool { + for _, v := range contextFilters { + v = strings.TrimPrefix(v, "@") + if !t.HasContext(v) { + return false + } + } + return true + }) + } + if len(projectFilters) > 0 { + allFilters = append(allFilters, func(t timertxt.Timer) bool { + for _, v := range projectFilters { + v = strings.TrimPrefix(v, "+") + if !t.HasProject(v) { + return false + } + } + return true + }) + } + doFilters := func(t timertxt.Timer) bool { + for _, v := range allFilters { + if !v(t) { + return false + } + } + // If we made it all the way down here, it matches + return true + } + list = list.Filter(doFilters) + dayTotals := make(map[string]time.Duration) for _, v := range *list { dur := v.FinishDate.Sub(v.StartDate) @@ -149,7 +204,27 @@ func (a *AppState) opStopTimer(args []string) int { } func (a *AppState) opSwitchTimer(args []string) int { - return 0 + var timerIds []int + var err error + end := time.Now() + // Stop all running timers and start a new one with the given args + for _, v := range *a.TimerList.GetActiveTimers() { + timerIds = append(timerIds, v.Id) + } + fmt.Print("Stopping ", timerIds, "\n") + for _, v := range timerIds { + var stopped *timertxt.Timer + if stopped, err = a.TimerList.GetTimer(v); err != nil { + fmt.Println(err.Error()) + continue + } + if err = a.SetTimerFinished(v, end); err != nil { + fmt.Println(err.Error()) + continue + } + fmt.Println("Stopped Timer:", timerToFriendlyString(stopped)) + } + return a.opStartTimer(args) } func (a *AppState) opArchiveTimer(args []string) int { @@ -186,7 +261,73 @@ func (a *AppState) opArchiveTimer(args []string) int { return 0 } -func (a *AppState) opState(args []string) int { +func (a *AppState) opModifyTimer(args []string) int { + var timer *timertxt.Timer + var contexts, projects []string + id, err := strconv.Atoi(args[0]) + if err != nil { + // We didn't have a timer id, so try to modify the first active timer + if len(*a.TimerList.GetActiveTimers()) > 0 { + timer = &(*a.TimerList.GetActiveTimers())[0] + } else { + // And we don't have any active timers + fmt.Println("No active timers, 'id' must be provided.") + return 1 + } + } else { + args = args[1:] + if timer, err = a.TimerList.GetTimer(id); err != nil { + fmt.Printf("Error getting timer %d\n", id) + return 1 + } + } + var start, end time.Time + + for _, v := range args { + pts := strings.Split(v, "=") + switch pts[0] { + case "beginning", "start": + if start, err = parseFuzzyTime(pts[1]); err != nil { + fmt.Println("Error parsing start time.") + return 1 + } + case "stop", "finish", "end": + if end, err = parseFuzzyTime(pts[1]); err != nil { + fmt.Println("Error parsing end time.") + return 1 + } + case "project", "projects": + projects = strings.Split(pts[1], ",") + case "context", "contexts": + contexts = strings.Split(pts[1], ",") + } + } + if len(contexts) > 0 { + for k := range contexts { + contexts[k] = strings.TrimPrefix(contexts[k], "@") + } + timer.Contexts = contexts + } + if len(projects) > 0 { + for k := range projects { + projects[k] = strings.TrimPrefix(projects[k], "+") + } + timer.Projects = projects + } + if !start.IsZero() { + timer.StartDate = start + } + if !end.IsZero() { + timer.FinishDate = end + timer.Finished = true + } + fmt.Println("Modified Timer:") + fmt.Println(timerToFriendlyString(timer)) + + if err := a.WriteList(); err != nil { + fmt.Println(err.Error()) + return 1 + } return 0 }