Skip to content

Commit

Permalink
Add a status concept (#206)
Browse files Browse the repository at this point in the history
* add to simple screen printer

* WIP for starting todos

* filter by started date

* add a flag for started

* formatting fix

* refactor.  this is now a status command

* Fix CLI auth flow

* use correct hb endpoint

* update version

* ensure we send status to the backend

Co-authored-by: Grant Ammons <[email protected]>
  • Loading branch information
gammons and Grant Ammons authored Aug 3, 2020
1 parent f419a18 commit 4a44e01
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 12 deletions.
9 changes: 9 additions & 0 deletions cmd/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ Filtering by date:
Lists all todos whose due date is today or earlier:
ultralist list due:agenda
Filtering by status:
------------------
List all todos with a status of "started"
ultralist list status:started
Filtering by priority, completed, etc:
--------------------------------------
Expand Down Expand Up @@ -61,6 +67,9 @@ Grouping:
Lists all todos grouped by project:
ultralist list group:p
Lists all todos grouped by status:
ultralist list group:s
Combining filters:
------------------
Expand Down
31 changes: 31 additions & 0 deletions cmd/status.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package cmd

import (
"strings"

"github.com/spf13/cobra"
"github.com/ultralist/ultralist/ultralist"
)

func init() {
var (
setStatusCmdDesc = "Sets the status of a todo item"
setStatusCmdExample = `
ultralist status 33 blocked
ultralist s 33 blocked`
setStatusCmdLongDesc = `Sets the status of a todo item. Status can be any string.`
)

var setStatusCmd = &cobra.Command{
Use: "status [id] <status>",
Aliases: []string{"s"},
Example: setStatusCmdExample,
Long: setStatusCmdLongDesc,
Short: setStatusCmdDesc,
Run: func(cmd *cobra.Command, args []string) {
ultralist.NewApp().SetTodoStatus(strings.Join(args, " "))
},
}

rootCmd.AddCommand(setStatusCmd)
}
19 changes: 19 additions & 0 deletions ultralist/app.go
Original file line number Diff line number Diff line change
Expand Up @@ -335,6 +335,21 @@ func (a *App) UnprioritizeTodo(input string) {
fmt.Println("Todo un-prioritized.")
}

// StartTodo will start a todo.
func (a *App) SetTodoStatus(input string) {
a.Load()
ids := a.getIDs(input)
if len(ids) == 0 {
return
}

splitted := strings.Split(input, " ")

a.TodoList.SetStatus(splitted[len(splitted)-1], ids...)
a.save()
fmt.Println("Todo status updated.")
}

// GarbageCollect will delete all archived todos.
func (a *App) GarbageCollect() {
a.Load()
Expand Down Expand Up @@ -474,13 +489,17 @@ func (a *App) getGroups(input string, todos []*Todo) *GroupedTodos {
grouper := &Grouper{}
contextRegex, _ := regexp.Compile(`group:c.*$`)
projectRegex, _ := regexp.Compile(`group:p.*$`)
statusRegex, _ := regexp.Compile(`group:s.*$`)

var grouped *GroupedTodos

if contextRegex.MatchString(input) {
grouped = grouper.GroupByContext(todos)
} else if projectRegex.MatchString(input) {
grouped = grouper.GroupByProject(todos)
} else if statusRegex.MatchString(input) {
fmt.Println("grouping by status")
grouped = grouper.GroupByStatus(todos)
} else {
grouped = grouper.GroupByNothing(todos)
}
Expand Down
2 changes: 2 additions & 0 deletions ultralist/event_logger.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ type EventLog struct {
Completed bool `json:"completed"`
CompletedDate string `json:"completedDate"`
Archived bool `json:"archived"`
Status string `json:"status"`
IsPriority bool `json:"isPriority"`
Notes []string `json:"notes"`
}
Expand Down Expand Up @@ -180,6 +181,7 @@ func (e *EventLogger) writeTodoEvent(eventType string, todo *Todo) *EventLog {
Completed: todo.Completed,
CompletedDate: todo.CompletedDate,
Archived: todo.Archived,
Status: todo.Status,
IsPriority: todo.IsPriority,
Notes: todo.Notes,
}
Expand Down
26 changes: 25 additions & 1 deletion ultralist/filter.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package ultralist

import "regexp"
import (
"regexp"
"strings"
)

// TodoFilter filters todos based on patterns.
type TodoFilter struct {
Expand All @@ -19,6 +22,7 @@ func (f *TodoFilter) Filter(input string) []*Todo {
f.Todos = f.filterPrioritized(input)
f.Todos = f.filterProjects(input)
f.Todos = f.filterContexts(input)
f.Todos = f.filterStatus(input)
f.Todos = NewDateFilter(f.Todos).FilterDate(input)

return f.Todos
Expand Down Expand Up @@ -73,6 +77,26 @@ func (f *TodoFilter) filterPrioritized(input string) []*Todo {
return f.Todos
}

func (f *TodoFilter) filterStatus(input string) []*Todo {

r, _ := regexp.Compile(`status:\w+`)
if !r.MatchString(input) {
return f.Todos
}

statusString := strings.Split(r.FindString(input), ":")[1]

var ret []*Todo

for _, todo := range f.Todos {
if todo.Status == statusString {
ret = append(ret, todo)
}
}

return ret
}

func (f *TodoFilter) filterProjects(input string) []*Todo {
if !f.isFilteringByProjects(input) {
return f.Todos
Expand Down
20 changes: 20 additions & 0 deletions ultralist/grouper.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,26 @@ func (g *Grouper) GroupByProject(todos []*Todo) *GroupedTodos {
return &GroupedTodos{Groups: groups}
}

// GroupByStatus is grouping todos by status
func (g *Grouper) GroupByStatus(todos []*Todo) *GroupedTodos {
groups := map[string][]*Todo{}

for _, todo := range todos {
if todo.Status != "" {
groups[todo.Status] = append(groups[todo.Status], todo)
} else {
groups["No status"] = append(groups["No status"], todo)
}
}

// finally, sort the todos
for groupName, todos := range groups {
groups[groupName] = g.sort(todos)
}

return &GroupedTodos{Groups: groups}
}

// GroupByNothing is the default result if todos are not grouped by context project.
func (g *Grouper) GroupByNothing(todos []*Todo) *GroupedTodos {
groups := map[string][]*Todo{}
Expand Down
36 changes: 27 additions & 9 deletions ultralist/screen_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ import (
var (
blue = color.New(0, color.FgBlue)
blueBold = color.New(color.Bold, color.FgBlue)
green = color.New(0, color.FgGreen)
greenBold = color.New(color.Bold, color.FgGreen)
cyan = color.New(0, color.FgCyan)
cyanBold = color.New(color.Bold, color.FgCyan)
magenta = color.New(0, color.FgMagenta)
Expand Down Expand Up @@ -65,14 +67,15 @@ func (f *ScreenPrinter) printTodo(tabby *tabby.Tabby, todo *Todo, printNotes boo
tabby.AddLine(
f.formatID(todo.ID, todo.IsPriority),
f.formatCompleted(todo.Completed),
f.formatInformation(todo),
f.formatDue(todo.Due, todo.IsPriority, todo.Completed),
f.formatStatus(todo.Status, todo.IsPriority),
f.formatSubject(todo.Subject, todo.IsPriority))
} else {
tabby.AddLine(
f.formatID(todo.ID, todo.IsPriority),
f.formatCompleted(todo.Completed),
f.formatDue(todo.Due, todo.IsPriority, todo.Completed),
f.formatStatus(todo.Status, todo.IsPriority),
f.formatSubject(todo.Subject, todo.IsPriority))
}

Expand All @@ -83,6 +86,7 @@ func (f *ScreenPrinter) printTodo(tabby *tabby.Tabby, todo *Todo, printNotes boo
white.Sprint(""),
white.Sprint(""),
white.Sprint(""),
white.Sprint(""),
white.Sprint(note))
}
}
Expand All @@ -99,9 +103,8 @@ func (f *ScreenPrinter) formatCompleted(completed bool) string {
if completed {
if f.UnicodeSupport {
return white.Sprint("[✔]")
} else {
return white.Sprint("[x]")
}
return white.Sprint("[x]")
}
return white.Sprint("[ ]")
}
Expand All @@ -118,6 +121,25 @@ func (f *ScreenPrinter) formatDue(due string, isPriority bool, completed bool) s
return f.printDue(dueTime, completed)
}

func (f *ScreenPrinter) formatStatus(status string, isPriority bool) string {
if status == "" {
return green.Sprint(" ")
}

if len(status) < 10 {
for x := len(status); x <= 10; x++ {
status += " "
}
}

statusRune := []rune(status)

if isPriority {
return greenBold.Sprintf("%-10v", string(statusRune[0:10]))
}
return green.Sprintf("%-10s", string(statusRune[0:10]))
}

func (f *ScreenPrinter) formatInformation(todo *Todo) string {
var information []string
if todo.IsPriority {
Expand All @@ -130,12 +152,8 @@ func (f *ScreenPrinter) formatInformation(todo *Todo) string {
} else {
information = append(information, " ")
}
if todo.Archived {
information = append(information, "A")
} else {
information = append(information, " ")
}
return white.Sprint(strings.Join(information, " "))

return white.Sprint(strings.Join(information, ""))
}

func (f *ScreenPrinter) printDue(due time.Time, completed bool) string {
Expand Down
2 changes: 1 addition & 1 deletion ultralist/simple_screen_printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ func (f *SimpleScreenPrinter) formatInformation(todo *Todo) string {
} else {
information = append(information, " ")
}
return fmt.Sprint(strings.Join(information, " "))
return fmt.Sprint(strings.Join(information, ""))
}

func (f *SimpleScreenPrinter) printDue(due time.Time, completed bool) string {
Expand Down
2 changes: 2 additions & 0 deletions ultralist/todo_item.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ type Todo struct {
Due string `json:"due"`
Completed bool `json:"completed"`
CompletedDate string `json:"completedDate"`
Status string `json:"status"`
Archived bool `json:"archived"`
IsPriority bool `json:"isPriority"`
Notes []string `json:"notes"`
Expand Down Expand Up @@ -98,6 +99,7 @@ func (t Todo) Equals(other *Todo) bool {
!reflect.DeepEqual(t.Contexts, other.Contexts) ||
t.Due != other.Due ||
t.Completed != other.Completed ||
t.Status != other.Status ||
t.CompletedDate != other.CompletedDate ||
t.Archived != other.Archived ||
t.IsPriority != other.IsPriority ||
Expand Down
17 changes: 16 additions & 1 deletion ultralist/todo_list.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package ultralist

import "sort"
import (
"sort"
)

// TodoList is the struct of a list with several todos.
type TodoList struct {
Expand Down Expand Up @@ -117,6 +119,19 @@ func (t *TodoList) Unprioritize(ids ...int) {
}
}

// SetStatus sets the status of a todo
func (t *TodoList) SetStatus(input string, ids ...int) {
for _, id := range ids {
todo := t.FindByID(id)
if todo == nil {
continue
}
todo.Status = input
t.Delete(id)
t.Data = append(t.Data, todo)
}
}

// IndexOf finds the index of a todo.
func (t *TodoList) IndexOf(todoToFind *Todo) int {
for i, todo := range t.Data {
Expand Down

0 comments on commit 4a44e01

Please sign in to comment.