Skip to content

Commit

Permalink
Implement task due
Browse files Browse the repository at this point in the history
  • Loading branch information
kakengloh committed Aug 20, 2022
1 parent ede4296 commit db6afd2
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 41 deletions.
24 changes: 23 additions & 1 deletion cmd/mod.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd
import (
"fmt"
"strconv"
"time"

"github.com/kakengloh/tsk/entity"
"github.com/kakengloh/tsk/repository"
Expand Down Expand Up @@ -66,7 +67,27 @@ func NewModCommand(tr repository.TaskRepository) *cobra.Command {
status = v
}

task, err = tr.UpdateTask(id, title, priority, status)
// Due
d, err := cmd.Flags().GetString("due")
if err != nil {
return err
}
var due time.Time
if d != "" {
duration, err := time.ParseDuration(d)
fmt.Println(err)
if err == nil {
due = time.Now().Add(duration)
} else {
val, err := time.ParseInLocation("2006-01-02 15:04", d, time.Local)
if err != nil {
return err
}
due = val
}
}

task, err = tr.UpdateTask(id, title, priority, status, due)

pt.PrintTask(task, "Task modified ✅")

Expand All @@ -77,6 +98,7 @@ func NewModCommand(tr repository.TaskRepository) *cobra.Command {
setCmd.PersistentFlags().StringP("title", "t", "", "Set title")
setCmd.PersistentFlags().StringP("status", "s", "", "Set status (todo / doing / done)")
setCmd.PersistentFlags().StringP("priority", "p", "", "Set priority (low / medium / high")
setCmd.PersistentFlags().StringP("due", "d", "", "Set due")

return setCmd
}
23 changes: 22 additions & 1 deletion cmd/new.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package cmd

import (
"fmt"
"time"

"github.com/kakengloh/tsk/entity"
"github.com/kakengloh/tsk/repository"
Expand Down Expand Up @@ -46,8 +47,27 @@ func NewNewCommand(tr repository.TaskRepository) *cobra.Command {
return err
}

// Due
d, err := cmd.Flags().GetString("due")
if err != nil {
return err
}
var due time.Time
if d != "" {
duration, err := time.ParseDuration(d)
if err == nil {
due = time.Now().Add(duration)
} else {
val, err := time.ParseInLocation("2006-01-02 15:04", d, time.Local)
if err != nil {
return err
}
due = val
}
}

// Create task
t, err := tr.CreateTask(title, priority, status, n)
t, err := tr.CreateTask(title, priority, status, due, n)

if err != nil {
return fmt.Errorf("failed to create task: %w", err)
Expand All @@ -62,6 +82,7 @@ func NewNewCommand(tr repository.TaskRepository) *cobra.Command {
newCmd.PersistentFlags().StringP("priority", "p", entity.TaskPriorityLow.String(), "Priority")
newCmd.PersistentFlags().StringP("status", "s", entity.TaskStatusTodo.String(), "Status")
newCmd.PersistentFlags().StringP("note", "n", "", "Note")
newCmd.PersistentFlags().StringP("due", "d", "", "Due")

return newCmd
}
9 changes: 7 additions & 2 deletions cmd/new_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package cmd_test
import (
"bytes"
"testing"
"time"

"github.com/golang/mock/gomock"
"github.com/kakengloh/tsk/cmd"
Expand All @@ -22,7 +23,7 @@ func Test_NewCommand(t *testing.T) {
defer ctrl.Finish()

tr := mock.NewMockTaskRepository(ctrl)
tr.EXPECT().CreateTask(task.Title, task.Priority, task.Status, "").Return(task, nil)
tr.EXPECT().CreateTask(task.Title, task.Priority, task.Status, gomock.Any(), "").Return(task, nil)

buf := new(bytes.Buffer)

Expand All @@ -41,18 +42,20 @@ func Test_NewCommand(t *testing.T) {
}

func Test_NewCommandWithOptions(t *testing.T) {
due, _ := time.ParseInLocation("2006-01-02 15:04", "2023-01-01 12:00", time.Local)
task := entity.Task{
Title: "make coffee",
Priority: entity.TaskPriorityMedium,
Status: entity.TaskStatusDoing,
Notes: []string{"long black"},
Due: due,
}

ctrl := gomock.NewController(t)
defer ctrl.Finish()

tr := mock.NewMockTaskRepository(ctrl)
tr.EXPECT().CreateTask(task.Title, task.Priority, task.Status, task.Notes[0]).Return(task, nil)
tr.EXPECT().CreateTask(task.Title, task.Priority, task.Status, due, task.Notes[0]).Return(task, nil)

buf := new(bytes.Buffer)

Expand All @@ -64,6 +67,7 @@ func Test_NewCommandWithOptions(t *testing.T) {
"-p=medium",
"-s=doing",
"-n=long black",
"-d=2023-01-01 12:00",
})

err := newCmd.Execute()
Expand All @@ -73,4 +77,5 @@ func Test_NewCommandWithOptions(t *testing.T) {
assert.Contains(t, buf.String(), "make coffee")
assert.Contains(t, buf.String(), "Medium")
assert.Contains(t, buf.String(), "Doing")
assert.Contains(t, buf.String(), "2023-01-01 12:00")
}
1 change: 1 addition & 0 deletions entity/task.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ type Task struct {
Title string
Priority TaskPriority
Status TaskStatus
Due time.Time
Notes []string
CreatedAt time.Time
UpdatedAt time.Time
Expand Down
17 changes: 9 additions & 8 deletions mock/mock_task_repository.go

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 7 additions & 2 deletions repository/bolt_task_repository.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ func NewBoltTaskRepository(db *bbolt.DB) (*BoltTaskRepository, error) {
return &BoltTaskRepository{db}, err
}

func (tr *BoltTaskRepository) CreateTask(title string, priority entity.TaskPriority, status entity.TaskStatus, note string) (entity.Task, error) {
func (tr *BoltTaskRepository) CreateTask(title string, priority entity.TaskPriority, status entity.TaskStatus, due time.Time, note string) (entity.Task, error) {
notes := []string{}
if note != "" {
notes = append(notes, note)
Expand All @@ -43,6 +43,7 @@ func (tr *BoltTaskRepository) CreateTask(title string, priority entity.TaskPrior
Title: title,
Priority: priority,
Status: status,
Due: due,
Notes: notes,
CreatedAt: time.Now(),
UpdatedAt: time.Now(),
Expand Down Expand Up @@ -130,7 +131,7 @@ func (tr *BoltTaskRepository) GetTaskByID(id int) (entity.Task, error) {
return t, err
}

func (tr *BoltTaskRepository) UpdateTask(id int, title string, priority entity.TaskPriority, status entity.TaskStatus) (entity.Task, error) {
func (tr *BoltTaskRepository) UpdateTask(id int, title string, priority entity.TaskPriority, status entity.TaskStatus, due time.Time) (entity.Task, error) {
var t entity.Task

err := tr.DB.Update(func(tx *bbolt.Tx) error {
Expand All @@ -153,6 +154,10 @@ func (tr *BoltTaskRepository) UpdateTask(id int, title string, priority entity.T
t.Priority = priority
t.Status = status

if !due.IsZero() {
t.Due = due
}

buf, err := json.Marshal(t)
if err != nil {
return err
Expand Down
10 changes: 7 additions & 3 deletions repository/task_repository.go
Original file line number Diff line number Diff line change
@@ -1,12 +1,16 @@
package repository

import "github.com/kakengloh/tsk/entity"
import (
"time"

"github.com/kakengloh/tsk/entity"
)

type TaskRepository interface {
CreateTask(title string, priority entity.TaskPriority, status entity.TaskStatus, note string) (entity.Task, error)
CreateTask(title string, priority entity.TaskPriority, status entity.TaskStatus, due time.Time, note string) (entity.Task, error)
ListTasks(status entity.TaskStatus, priority entity.TaskPriority, keyword string) (entity.TaskList, error)
GetTaskByID(id int) (entity.Task, error)
UpdateTask(id int, title string, priority entity.TaskPriority, status entity.TaskStatus) (entity.Task, error)
UpdateTask(id int, title string, priority entity.TaskPriority, status entity.TaskStatus, due time.Time) (entity.Task, error)
UpdateTaskStatus(status entity.TaskStatus, ids ...int) []UpdateTaskStatusResult
DeleteTask(id int) error
BulkDeleteTasks(ids ...int) map[int]error
Expand Down
80 changes: 56 additions & 24 deletions util/printer/printer.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,13 @@ func (p *Printer) PrintTask(task entity.Task, caption string) {

// Generate headers
table.SetAutoFormatHeaders(false)
table.SetHeader([]string{"ID", "Title", "Status", "Priority", "Created", "Notes"})
table.SetHeaderColor(
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
)
headers := []string{"ID", "Title", "Status", "Priority", "Created", "Due", "Notes"}
headerColors := make([]tablewriter.Colors, len(headers))
for i := range headers {
headerColors[i] = tablewriter.Colors{tablewriter.Bold}
}
table.SetHeader(headers)
table.SetHeaderColor(headerColors...)

// Populate data
// Status formatting
Expand All @@ -61,13 +59,31 @@ func (p *Printer) PrintTask(task entity.Task, caption string) {

// Calculate time ago
// Show relative time by default
since := time.Since(task.CreatedAt).Round(time.Second).String() + " ago"
created := time.Since(task.CreatedAt).Round(time.Second).String() + " ago"
// If duration is at least a week, show the exact date
if time.Since(task.CreatedAt).Hours() >= 168 {
since = task.CreatedAt.Format("02/01/2006")
created = task.CreatedAt.Format("2006-01-02")
}

// Calculate due
due := ""
if !task.Due.IsZero() {
duration := time.Until(task.Due).Round(time.Second)

if duration.Milliseconds() > 0 {
// Before due
due = "in " + duration.String()

if time.Until(task.Due).Hours() > 24 {
due = task.Due.Format("2006-01-02 15:04")
}
} else {
// Over due
due = color.RedString("over " + duration.String()[1:])
}
}

table.Append([]string{strconv.Itoa(task.ID), task.Title, status, priority, since, notes})
table.Append([]string{strconv.Itoa(task.ID), task.Title, status, priority, created, due, notes})

fmt.Fprintln(p.Stdout)
table.Render()
Expand All @@ -84,16 +100,14 @@ func (p *Printer) PrintTaskList(tasks []entity.Task) {
table.SetCaption(true, caption)

// Generate headers
headers := []string{"ID", "Title", "Status", "Priority", "Created", "Due", "Notes"}
headerColors := make([]tablewriter.Colors, len(headers))
for i := range headers {
headerColors[i] = tablewriter.Colors{tablewriter.Bold}
}
table.SetAutoFormatHeaders(false)
table.SetHeader([]string{"ID", "Title", "Status", "Priority", "Created", "Notes"})
table.SetHeaderColor(
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
tablewriter.Colors{tablewriter.Bold},
)
table.SetHeader(headers)
table.SetHeaderColor(headerColors...)

// Populate data
for _, t := range tasks {
Expand All @@ -112,13 +126,31 @@ func (p *Printer) PrintTaskList(tasks []entity.Task) {

// Calculate time ago
// Show relative time by default
since := time.Since(t.CreatedAt).Round(time.Second).String() + " ago"
created := time.Since(t.CreatedAt).Round(time.Second).String() + " ago"
// If duration is at least a week, show the exact date
if time.Since(t.CreatedAt).Hours() >= 168 {
since = t.CreatedAt.Format("02/01/2006")
created = t.CreatedAt.Format("2006-01-02")
}

// Calculate due
due := ""
if !t.Due.IsZero() {
duration := time.Until(t.Due).Round(time.Second)

if duration.Milliseconds() > 0 {
// Before due
due = "in " + duration.String()

if time.Until(t.Due).Hours() > 24 {
due = t.Due.Format("2006-01-02 15:04")
}
} else {
// Over due
due = color.RedString("over " + duration.String()[1:])
}
}

table.Append([]string{strconv.Itoa(t.ID), t.Title, status, priority, since, notes})
table.Append([]string{strconv.Itoa(t.ID), t.Title, status, priority, created, due, notes})
}

fmt.Fprintln(p.Stdout)
Expand Down

0 comments on commit db6afd2

Please sign in to comment.