aboutsummaryrefslogtreecommitdiffstats
path: root/todolist
diff options
context:
space:
mode:
authorGrant Ammons <gammons@gmail.com>2017-03-07 08:12:35 -0500
committerGrant Ammons <gammons@gmail.com>2017-03-07 08:12:35 -0500
commit5763376a7ef50a116f9585ffd280bbf8e37621d3 (patch)
tree8dc6d5376e1bf422a1aa693c27f505721741a1f0 /todolist
parent871fb90b26a98ee3569c325940a5f4e380631c54 (diff)
parent34931b46ed9ebb06e9555d6e62c98c70dc460d59 (diff)
Merge branch 'master' into garbage-collect
Diffstat (limited to 'todolist')
-rw-r--r--todolist/app.go22
-rw-r--r--todolist/date_filter.go37
-rw-r--r--todolist/date_filter_test.go5
-rw-r--r--todolist/file_store.go25
-rw-r--r--todolist/filter.go20
-rw-r--r--todolist/formatter.go52
-rw-r--r--todolist/parser.go24
-rw-r--r--todolist/parser_test.go9
-rw-r--r--todolist/todo_item.go17
-rw-r--r--todolist/todo_list.go14
-rw-r--r--todolist/todo_list_test.go20
-rw-r--r--todolist/todos.json2
-rw-r--r--todolist/util.go17
13 files changed, 193 insertions, 71 deletions
diff --git a/todolist/app.go b/todolist/app.go
index 20a35ef..a9145a1 100644
--- a/todolist/app.go
+++ b/todolist/app.go
@@ -150,6 +150,28 @@ func (a *App) ListTodos(input string) {
formatter.Print()
}
+func (a *App) PrioritizeTodo(input string) {
+ a.Load()
+ id, _ := a.getId(input)
+ if id == -1 {
+ return
+ }
+ a.TodoList.Prioritize(id)
+ a.Save()
+ fmt.Println("Todo prioritized.")
+}
+
+func (a *App) UnprioritizeTodo(input string) {
+ a.Load()
+ id, _ := a.getId(input)
+ if id == -1 {
+ return
+ }
+ a.TodoList.Unprioritize(id)
+ a.Save()
+ fmt.Println("Todo un-prioritized.")
+}
+
func (a *App) getId(input string) (int, *Todo) {
re, _ := regexp.Compile("\\d+")
if re.MatchString(input) {
diff --git a/todolist/date_filter.go b/todolist/date_filter.go
index 2bf51e2..b56f117 100644
--- a/todolist/date_filter.go
+++ b/todolist/date_filter.go
@@ -3,8 +3,6 @@ package todolist
import (
"regexp"
"time"
-
- "github.com/jinzhu/now"
)
type DateFilter struct {
@@ -17,38 +15,38 @@ func NewDateFilter(todos []*Todo) *DateFilter {
}
func (f *DateFilter) FilterDate(input string) []*Todo {
- agendaRegex, _ := regexp.Compile(`agenda .*$`)
+ agendaRegex, _ := regexp.Compile(`agenda.*$`)
if agendaRegex.MatchString(input) {
- return f.filterAgenda(now.BeginningOfDay())
+ return f.filterAgenda(bod(time.Now()))
}
r, _ := regexp.Compile(`due .*$`)
match := r.FindString(input)
switch {
case match == "due tod" || match == "due today":
- return f.filterToday(now.BeginningOfDay())
+ return f.filterToday(bod(time.Now()))
case match == "due tom" || match == "due tomorrow":
- return f.filterTomorrow(now.BeginningOfDay())
+ return f.filterTomorrow(bod(time.Now()))
case match == "due sun" || match == "due sunday":
- return f.filterDay(now.BeginningOfDay(), time.Sunday)
+ return f.filterDay(bod(time.Now()), time.Sunday)
case match == "due mon" || match == "due monday":
- return f.filterDay(now.BeginningOfDay(), time.Monday)
+ return f.filterDay(bod(time.Now()), time.Monday)
case match == "due tue" || match == "due tuesday":
- return f.filterDay(now.BeginningOfDay(), time.Tuesday)
+ return f.filterDay(bod(time.Now()), time.Tuesday)
case match == "due wed" || match == "due wednesday":
- return f.filterDay(now.BeginningOfDay(), time.Wednesday)
+ return f.filterDay(bod(time.Now()), time.Wednesday)
case match == "due thu" || match == "due thursday":
- return f.filterDay(now.BeginningOfDay(), time.Thursday)
+ return f.filterDay(bod(time.Now()), time.Thursday)
case match == "due fri" || match == "due friday":
- return f.filterDay(now.BeginningOfDay(), time.Friday)
+ return f.filterDay(bod(time.Now()), time.Friday)
case match == "due sat" || match == "due saturday":
- return f.filterDay(now.BeginningOfDay(), time.Saturday)
+ return f.filterDay(bod(time.Now()), time.Saturday)
case match == "due this week":
- return f.filterThisWeek(now.BeginningOfDay())
+ return f.filterThisWeek(bod(time.Now()))
case match == "due next week":
- return f.filterNextWeek(now.BeginningOfDay())
+ return f.filterNextWeek(bod(time.Now()))
case match == "overdue":
- return f.filterOverdue(now.BeginningOfDay())
+ return f.filterOverdue(bod(time.Now()))
}
return f.Todos
}
@@ -57,6 +55,9 @@ func (f *DateFilter) filterAgenda(pivot time.Time) []*Todo {
var ret []*Todo
for _, todo := range f.Todos {
+ if todo.Due == "" {
+ continue
+ }
dueTime, _ := time.ParseInLocation("2006-01-02", todo.Due, f.Location)
if dueTime.Before(pivot) || todo.Due == pivot.Format("2006-01-02") {
ret = append(ret, todo)
@@ -102,7 +103,7 @@ func (f *DateFilter) filterTomorrow(pivot time.Time) []*Todo {
func (f *DateFilter) filterThisWeek(pivot time.Time) []*Todo {
var ret []*Todo
- begin := now.New(f.FindSunday(pivot)).BeginningOfDay()
+ begin := bod(f.FindSunday(pivot))
end := begin.AddDate(0, 0, 7)
for _, todo := range f.Todos {
@@ -144,7 +145,7 @@ func (f *DateFilter) filterOverdue(pivot time.Time) []*Todo {
}
func (f *DateFilter) FindSunday(pivot time.Time) time.Time {
- switch now.New(pivot).Weekday() {
+ switch pivot.Weekday() {
case time.Sunday:
return pivot
case time.Monday:
diff --git a/todolist/date_filter_test.go b/todolist/date_filter_test.go
index 8ee8db5..444b4ac 100644
--- a/todolist/date_filter_test.go
+++ b/todolist/date_filter_test.go
@@ -4,7 +4,6 @@ import (
"testing"
"time"
- "github.com/jinzhu/now"
"github.com/stretchr/testify/assert"
)
@@ -63,7 +62,7 @@ func TestFilterOverdue(t *testing.T) {
var todos []*Todo
lastWeekTodo := &Todo{Id: 1, Subject: "one", Due: time.Now().AddDate(0, 0, -7).Format("2006-01-02")}
- todayTodo := &Todo{Id: 2, Subject: "two", Due: now.BeginningOfDay().Format("2006-01-02")}
+ todayTodo := &Todo{Id: 2, Subject: "two", Due: bod(time.Now()).Format("2006-01-02")}
tomorrowTodo := &Todo{Id: 3, Subject: "three", Due: time.Now().AddDate(0, 0, 1).Format("2006-01-02")}
todos = append(todos, lastWeekTodo)
@@ -71,7 +70,7 @@ func TestFilterOverdue(t *testing.T) {
todos = append(todos, tomorrowTodo)
filter := NewDateFilter(todos)
- filtered := filter.filterOverdue(now.BeginningOfDay())
+ filtered := filter.filterOverdue(bod(time.Now()))
assert.Equal(1, len(filtered))
assert.Equal(1, filtered[0].Id)
diff --git a/todolist/file_store.go b/todolist/file_store.go
index 5413295..4a7f0e0 100644
--- a/todolist/file_store.go
+++ b/todolist/file_store.go
@@ -5,6 +5,7 @@ import (
"fmt"
"io/ioutil"
"os"
+ "os/user"
)
type FileStore struct {
@@ -13,22 +14,36 @@ type FileStore struct {
}
func NewFileStore() *FileStore {
- return &FileStore{FileLocation: ".todos.json", Loaded: false}
+ localrepo := ".todos.json"
+ usr, _ := user.Current()
+ homerepo := fmt.Sprintf("%s/.todos.json", usr.HomeDir)
+ _, err1 := os.Stat(localrepo)
+ _, err2 := os.Stat(homerepo)
+
+ if err1 == nil {
+ return &FileStore{FileLocation: localrepo, Loaded: false}
+ } else if err2 == nil {
+ return &FileStore{FileLocation: homerepo, Loaded: false}
+ } else {
+ fmt.Println("No todo file found!")
+ fmt.Println("You may run 'todo init' to initialize an empty repo in working directory.")
+ os.Exit(1)
+ return nil
+ }
}
func (f *FileStore) Load() ([]*Todo, error) {
data, err := ioutil.ReadFile(f.FileLocation)
if err != nil {
- fmt.Println("No todo file found!")
- fmt.Println("Initialize a new todo repo by running 'todo init'")
+ fmt.Println("Error reading", f.FileLocation, "by", err)
return nil, err
- os.Exit(0)
+ os.Exit(1)
}
var todos []*Todo
jerr := json.Unmarshal(data, &todos)
if jerr != nil {
- fmt.Println("Error reading json data", jerr)
+ fmt.Println("Error reading", f.FileLocation, "by", jerr)
return nil, jerr
os.Exit(1)
}
diff --git a/todolist/filter.go b/todolist/filter.go
index 7705ca5..8c2a7ad 100644
--- a/todolist/filter.go
+++ b/todolist/filter.go
@@ -12,6 +12,7 @@ func NewFilter(todos []*Todo) *TodoFilter {
func (f *TodoFilter) Filter(input string) []*Todo {
f.Todos = f.filterArchived(input)
+ f.Todos = f.filterPrioritized(input)
f.Todos = f.filterProjects(input)
f.Todos = f.filterContexts(input)
f.Todos = NewDateFilter(f.Todos).FilterDate(input)
@@ -38,6 +39,15 @@ func (f *TodoFilter) filterArchived(input string) []*Todo {
}
}
+func (f *TodoFilter) filterPrioritized(input string) []*Todo {
+ r, _ := regexp.Compile(`l p`)
+ if r.MatchString(input) {
+ return f.getPrioritized()
+ } else {
+ return f.Todos
+ }
+}
+
func (f *TodoFilter) filterProjects(input string) []*Todo {
if !f.isFilteringByProjects(input) {
return f.Todos
@@ -88,6 +98,16 @@ func (f *TodoFilter) getArchived() []*Todo {
return ret
}
+func (f *TodoFilter) getPrioritized() []*Todo {
+ var ret []*Todo
+ for _, todo := range f.Todos {
+ if todo.IsPriority {
+ ret = append(ret, todo)
+ }
+ }
+ return ret
+}
+
func (f *TodoFilter) getUnarchived() []*Todo {
var ret []*Todo
for _, todo := range f.Todos {
diff --git a/todolist/formatter.go b/todolist/formatter.go
index 43d9b1e..c0f3313 100644
--- a/todolist/formatter.go
+++ b/todolist/formatter.go
@@ -44,20 +44,28 @@ func (f *Formatter) Print() {
}
func (f *Formatter) printTodo(todo *Todo) {
- yellow := color.New(color.FgYellow).SprintFunc()
+ yellow := color.New(color.FgYellow)
+ if todo.IsPriority {
+ yellow.Add(color.Bold, color.Italic)
+ }
fmt.Fprintf(f.Writer, " %s\t%s\t%s\t%s\t\n",
- yellow(strconv.Itoa(todo.Id)),
+ yellow.SprintFunc()(strconv.Itoa(todo.Id)),
f.formatCompleted(todo.Completed),
- f.formatDue(todo.Due),
- f.formatSubject(todo.Subject))
+ f.formatDue(todo.Due, todo.IsPriority),
+ f.formatSubject(todo.Subject, todo.IsPriority))
}
-func (f *Formatter) formatDue(due string) string {
- blue := color.New(color.FgBlue).SprintFunc()
- red := color.New(color.FgRed).SprintFunc()
+func (f *Formatter) formatDue(due string, isPriority bool) string {
+ blue := color.New(color.FgBlue)
+ red := color.New(color.FgRed)
+
+ if isPriority {
+ blue.Add(color.Bold, color.Italic)
+ red.Add(color.Bold, color.Italic)
+ }
if due == "" {
- return blue(" ")
+ return blue.SprintFunc()(" ")
}
dueTime, err := time.Parse("2006-01-02", due)
@@ -68,13 +76,13 @@ func (f *Formatter) formatDue(due string) string {
}
if isToday(dueTime) {
- return blue("today")
+ return blue.SprintFunc()("today")
} else if isTomorrow(dueTime) {
- return blue("tomorrow")
+ return blue.SprintFunc()("tomorrow")
} else if isPastDue(dueTime) {
- return red(dueTime.Format("Mon Jan 2"))
+ return red.SprintFunc()(dueTime.Format("Mon Jan 2"))
} else {
- return blue(dueTime.Format("Mon Jan 2"))
+ return blue.SprintFunc()(dueTime.Format("Mon Jan 2"))
}
}
@@ -98,9 +106,17 @@ func isPastDue(t time.Time) bool {
return time.Now().After(t)
}
-func (f *Formatter) formatSubject(subject string) string {
- red := color.New(color.FgRed).SprintFunc()
- magenta := color.New(color.FgMagenta).SprintFunc()
+func (f *Formatter) formatSubject(subject string, isPriority bool) string {
+
+ red := color.New(color.FgRed)
+ magenta := color.New(color.FgMagenta)
+ white := color.New(color.FgWhite)
+
+ if isPriority {
+ red.Add(color.Bold, color.Italic)
+ magenta.Add(color.Bold, color.Italic)
+ white.Add(color.Bold, color.Italic)
+ }
splitted := strings.Split(subject, " ")
projectRegex, _ := regexp.Compile(`\+[\p{L}\d_]+`)
@@ -110,11 +126,11 @@ func (f *Formatter) formatSubject(subject string) string {
for _, word := range splitted {
if projectRegex.MatchString(word) {
- coloredWords = append(coloredWords, magenta(word))
+ coloredWords = append(coloredWords, magenta.SprintFunc()(word))
} else if contextRegex.MatchString(word) {
- coloredWords = append(coloredWords, red(word))
+ coloredWords = append(coloredWords, red.SprintFunc()(word))
} else {
- coloredWords = append(coloredWords, word)
+ coloredWords = append(coloredWords, white.SprintFunc()(word))
}
}
return strings.Join(coloredWords, " ")
diff --git a/todolist/parser.go b/todolist/parser.go
index 6cf1967..24a4016 100644
--- a/todolist/parser.go
+++ b/todolist/parser.go
@@ -7,8 +7,6 @@ import (
"strconv"
"strings"
"time"
-
- "github.com/jinzhu/now"
)
type Parser struct{}
@@ -79,9 +77,9 @@ func (p *Parser) Due(input string, day time.Time) string {
case "none":
return ""
case "today", "tod":
- return now.BeginningOfDay().Format("2006-01-02")
+ return bod(time.Now()).Format("2006-01-02")
case "tomorrow", "tom":
- return now.BeginningOfDay().AddDate(0, 0, 1).Format("2006-01-02")
+ return bod(time.Now()).AddDate(0, 0, 1).Format("2006-01-02")
case "monday", "mon":
return p.monday(day)
case "tuesday", "tue":
@@ -97,8 +95,8 @@ func (p *Parser) Due(input string, day time.Time) string {
case "sunday", "sun":
return p.sunday(day)
case "next week":
- n := now.BeginningOfDay()
- return now.New(n).Monday().AddDate(0, 0, 7).Format("2006-01-02")
+ n := bod(time.Now())
+ return getNearestMonday(n).AddDate(0, 0, 7).Format("2006-01-02")
}
return p.parseArbitraryDate(res, time.Now())
}
@@ -137,37 +135,37 @@ func (p *Parser) parseArbitraryDateWithYear(_date string, year int) time.Time {
}
func (p *Parser) monday(day time.Time) string {
- mon := now.New(day).Monday()
+ mon := getNearestMonday(day)
return p.thisOrNextWeek(mon, day)
}
func (p *Parser) tuesday(day time.Time) string {
- tue := now.New(day).Monday().AddDate(0, 0, 1)
+ tue := getNearestMonday(day).AddDate(0, 0, 1)
return p.thisOrNextWeek(tue, day)
}
func (p *Parser) wednesday(day time.Time) string {
- tue := now.New(day).Monday().AddDate(0, 0, 2)
+ tue := getNearestMonday(day).AddDate(0, 0, 2)
return p.thisOrNextWeek(tue, day)
}
func (p *Parser) thursday(day time.Time) string {
- tue := now.New(day).Monday().AddDate(0, 0, 3)
+ tue := getNearestMonday(day).AddDate(0, 0, 3)
return p.thisOrNextWeek(tue, day)
}
func (p *Parser) friday(day time.Time) string {
- tue := now.New(day).Monday().AddDate(0, 0, 4)
+ tue := getNearestMonday(day).AddDate(0, 0, 4)
return p.thisOrNextWeek(tue, day)
}
func (p *Parser) saturday(day time.Time) string {
- tue := now.New(day).Monday().AddDate(0, 0, 5)
+ tue := getNearestMonday(day).AddDate(0, 0, 5)
return p.thisOrNextWeek(tue, day)
}
func (p *Parser) sunday(day time.Time) string {
- tue := now.New(day).Monday().AddDate(0, 0, 6)
+ tue := getNearestMonday(day).AddDate(0, 0, 6)
return p.thisOrNextWeek(tue, day)
}
diff --git a/todolist/parser_test.go b/todolist/parser_test.go
index 791958c..cbb94e8 100644
--- a/todolist/parser_test.go
+++ b/todolist/parser_test.go
@@ -6,7 +6,6 @@ import (
"testing"
"time"
- "github.com/jinzhu/now"
"github.com/stretchr/testify/assert"
)
@@ -75,11 +74,11 @@ func TestParseContexts(t *testing.T) {
func TestDueToday(t *testing.T) {
parser := &Parser{}
todo := parser.ParseNewTodo("do this thing with @bob and @mary due today")
- if todo.Due != now.BeginningOfDay().Format("2006-01-02") {
+ if todo.Due != bod(time.Now()).Format("2006-01-02") {
fmt.Println("Date is different", todo.Due, time.Now())
}
todo = parser.ParseNewTodo("do this thing with @bob and @mary due tod")
- if todo.Due != now.BeginningOfDay().Format("2006-01-02") {
+ if todo.Due != bod(time.Now()).Format("2006-01-02") {
fmt.Println("Date is different", todo.Due, time.Now())
}
}
@@ -87,11 +86,11 @@ func TestDueToday(t *testing.T) {
func TestDueTomorrow(t *testing.T) {
parser := &Parser{}
todo := parser.ParseNewTodo("do this thing with @bob and @mary due tomorrow")
- if todo.Due != now.BeginningOfDay().AddDate(0, 0, 1).Format("2006-01-02") {
+ if todo.Due != bod(time.Now()).AddDate(0, 0, 1).Format("2006-01-02") {
fmt.Println("Date is different", todo.Due, time.Now())
}
todo = parser.ParseNewTodo("do this thing with @bob and @mary due tom")
- if todo.Due != now.BeginningOfDay().AddDate(0, 0, 1).Format("2006-01-02") {
+ if todo.Due != bod(time.Now()).AddDate(0, 0, 1).Format("2006-01-02") {
fmt.Println("Date is different", todo.Due, time.Now())
}
}
diff --git a/todolist/todo_item.go b/todolist/todo_item.go
index 6b820b1..273df8d 100644
--- a/todolist/todo_item.go
+++ b/todolist/todo_item.go
@@ -3,17 +3,18 @@ package todolist
import "time"
type Todo struct {
- Id int `json:"id"`
- Subject string `json:"subject"`
- Projects []string `json:"projects"`
- Contexts []string `json:"contexts"`
- Due string `json:"due"`
- Completed bool `json:"completed"`
- Archived bool `json:"archived"`
+ Id int `json:"id"`
+ Subject string `json:"subject"`
+ Projects []string `json:"projects"`
+ Contexts []string `json:"contexts"`
+ Due string `json:"due"`
+ Completed bool `json:"completed"`
+ Archived bool `json:"archived"`
+ IsPriority bool `json:"isPriority"`
}
func NewTodo() *Todo {
- return &Todo{Completed: false, Archived: false}
+ return &Todo{Completed: false, Archived: false, IsPriority: false}
}
func (t Todo) Valid() bool {
diff --git a/todolist/todo_list.go b/todolist/todo_list.go
index ea8ecbe..19fe0b7 100644
--- a/todolist/todo_list.go
+++ b/todolist/todo_list.go
@@ -63,6 +63,20 @@ func (t *TodoList) IndexOf(todoToFind *Todo) int {
return -1
}
+func (t *TodoList) Prioritize(id int) {
+ todo := t.FindById(id)
+ todo.IsPriority = true
+ t.Delete(id)
+ t.Data = append(t.Data, todo)
+}
+
+func (t *TodoList) Unprioritize(id int) {
+ todo := t.FindById(id)
+ todo.IsPriority = false
+ t.Delete(id)
+ t.Data = append(t.Data, todo)
+}
+
type ByDate []*Todo
func (a ByDate) Len() int { return len(a) }
diff --git a/todolist/todo_list_test.go b/todolist/todo_list_test.go
index eb0bc84..eefd99c 100644
--- a/todolist/todo_list_test.go
+++ b/todolist/todo_list_test.go
@@ -128,3 +128,23 @@ func TestGarbageCollect(t *testing.T) {
assert.Equal(1, list.NextId())
assert.Equal(2, list.MaxId())
}
+
+func TestPrioritizeNotInTodosJson(t *testing.T) {
+ assert := assert.New(t)
+ store := &FileStore{FileLocation: "todos.json"}
+ list := &TodoList{}
+ todos, _ := store.Load()
+ list.Load(todos)
+ assert.Equal(false, list.FindById(2).IsPriority)
+}
+
+func TestPrioritizeTodo(t *testing.T) {
+ assert := assert.New(t)
+ list := &TodoList{}
+ todo := &Todo{Archived: false, Completed: false, Subject: "testing", IsPriority: false}
+ list.Add(todo)
+ list.Prioritize(1)
+ assert.Equal(true, list.FindById(1).IsPriority)
+ list.Unprioritize(1)
+ assert.Equal(false, list.FindById(1).IsPriority)
+}
diff --git a/todolist/todos.json b/todolist/todos.json
index 5e44228..bc147d1 100644
--- a/todolist/todos.json
+++ b/todolist/todos.json
@@ -1 +1 @@
-[{"id":1,"subject":"this is the first subject","projects":["test1"],"contexts":["root"],"due":"2016-04-04","completed":false,"archived":true},{"id":2,"subject":" audit userify for 2FA","projects":["test1"],"contexts":["root","more"],"due":"","completed":true,"archived":false}] \ No newline at end of file
+[{"id":1,"subject":"this is the first subject","projects":["test1"],"contexts":["root"],"due":"2016-04-04","completed":false,"archived":true,"isPriority":false},{"id":2,"subject":" audit userify for 2FA","projects":["test1"],"contexts":["root","more"],"due":"","completed":true,"archived":false,"isPriority":false}] \ No newline at end of file
diff --git a/todolist/util.go b/todolist/util.go
index bbed216..72d475f 100644
--- a/todolist/util.go
+++ b/todolist/util.go
@@ -1,5 +1,7 @@
package todolist
+import "time"
+
func AddIfNotThere(arr []string, items []string) []string {
for _, item := range items {
there := false
@@ -27,3 +29,18 @@ func AddTodoIfNotThere(arr []*Todo, item *Todo) []*Todo {
}
return arr
}
+
+func bod(t time.Time) time.Time {
+ year, month, day := t.Date()
+ return time.Date(year, month, day, 0, 0, 0, 0, t.Location())
+}
+
+func getNearestMonday(t time.Time) time.Time {
+ for {
+ if t.Weekday() != time.Monday {
+ t = t.AddDate(0, 0, -1)
+ } else {
+ return t
+ }
+ }
+}