recurring prototype
This commit is contained in:
parent
e50d624b9c
commit
18d3074633
|
@ -1,2 +1,2 @@
|
|||
gte-process-inbox
|
||||
/gte-process-inbox
|
||||
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"git.sr.ht/~ewintr/gte/internal/task"
|
||||
"git.sr.ht/~ewintr/gte/pkg/mstore"
|
||||
)
|
||||
|
||||
func main() {
|
||||
config := &mstore.ImapConfiguration{
|
||||
ImapUrl: os.Getenv("IMAP_URL"),
|
||||
ImapUsername: os.Getenv("IMAP_USERNAME"),
|
||||
ImapPassword: os.Getenv("IMAP_PASSWORD"),
|
||||
}
|
||||
if !config.Valid() {
|
||||
log.Fatal("please set IMAP_USER, IMAP_PASSWORD, etc environment variables")
|
||||
}
|
||||
|
||||
mailStore, err := mstore.ImapConnect(config)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
defer mailStore.Disconnect()
|
||||
|
||||
taskRepo := task.NewRepository(mailStore)
|
||||
tasks, err := taskRepo.FindAll(task.FOLDER_RECURRING)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
for _, t := range tasks {
|
||||
if t.RecursToday() {
|
||||
subject, body, err := t.CreateNextMessage(task.Today)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
if err := mailStore.Add(task.FOLDER_PLANNED, subject, body); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
|
@ -96,7 +96,7 @@ func NewDateFromString(date string) Date {
|
|||
newWeekday = time.Sunday
|
||||
}
|
||||
|
||||
daysToAdd := int(newWeekday) - weekday
|
||||
daysToAdd := int(newWeekday) - int(weekday)
|
||||
if daysToAdd <= 0 {
|
||||
daysToAdd += 7
|
||||
}
|
||||
|
@ -116,11 +116,19 @@ func (d *Date) IsZero() bool {
|
|||
return d.t.IsZero()
|
||||
}
|
||||
|
||||
func (d *Date) Weekday() int {
|
||||
return int(d.t.Weekday())
|
||||
func (d *Date) Time() time.Time {
|
||||
return d.t
|
||||
}
|
||||
|
||||
func (d *Date) Weekday() time.Weekday {
|
||||
return d.t.Weekday()
|
||||
}
|
||||
|
||||
func (d *Date) Add(days int) Date {
|
||||
year, month, day := d.t.Date()
|
||||
return NewDate(year, int(month), day+days)
|
||||
}
|
||||
|
||||
func (d *Date) After(ud Date) bool {
|
||||
return d.t.After(ud.Time())
|
||||
}
|
||||
|
|
|
@ -1,25 +1,68 @@
|
|||
package task
|
||||
|
||||
import "time"
|
||||
|
||||
type Weekday time.Weekday
|
||||
import (
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
type Period int
|
||||
type Recurrer interface {
|
||||
RecursOn(date Date) bool
|
||||
FirstAfter(date Date) Date
|
||||
String() string
|
||||
}
|
||||
|
||||
func NewRecurrer(recurStr string) Recurrer {
|
||||
terms := strings.Split(recurStr, ", ")
|
||||
if len(terms) < 3 {
|
||||
return nil
|
||||
}
|
||||
|
||||
startDate, err := time.Parse("2006-01-02", terms[0])
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
if terms[1] != "weekly" {
|
||||
return nil
|
||||
}
|
||||
|
||||
if terms[2] != "wednesday" {
|
||||
return nil
|
||||
}
|
||||
|
||||
year, month, date := startDate.Date()
|
||||
return Weekly{
|
||||
Start: NewDate(year, int(month), date),
|
||||
Weekday: time.Wednesday,
|
||||
}
|
||||
}
|
||||
|
||||
// yyyy-mm-dd, weekly, wednesday
|
||||
type Weekly struct {
|
||||
Start Date
|
||||
Weekday Weekday
|
||||
Weekday time.Weekday
|
||||
}
|
||||
|
||||
func (w *Weekly) FirstAfter(date Date) Date {
|
||||
func (w Weekly) RecursOn(date Date) bool {
|
||||
if !w.Start.After(date) {
|
||||
return false
|
||||
}
|
||||
|
||||
return w.Weekday == date.Weekday()
|
||||
}
|
||||
|
||||
func (w Weekly) FirstAfter(date Date) Date {
|
||||
//sd := w.Start.Weekday()
|
||||
|
||||
return date
|
||||
}
|
||||
|
||||
func (w Weekly) String() string {
|
||||
return "2021-01-31, weekly, wednesday"
|
||||
}
|
||||
|
||||
/*
|
||||
type BiWeekly struct {
|
||||
Start Date
|
||||
Weekday Weekday
|
||||
|
@ -30,3 +73,4 @@ type RecurringTask struct {
|
|||
Start Date
|
||||
Recurrer Recurrer
|
||||
}
|
||||
*/
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
package task_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"git.sr.ht/~ewintr/go-kit/test"
|
||||
"git.sr.ht/~ewintr/gte/internal/task"
|
||||
)
|
||||
|
||||
func TestNewRecurrer(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
input string
|
||||
exp task.Recurrer
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
},
|
||||
{
|
||||
name: "weekly",
|
||||
input: "2021-01-31, weekly, wednesday",
|
||||
exp: task.Weekly{
|
||||
Start: task.NewDate(2021, 1, 31),
|
||||
Weekday: time.Wednesday,
|
||||
},
|
||||
},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
test.Equals(t, tc.exp, task.NewRecurrer(tc.input))
|
||||
})
|
||||
}
|
||||
}
|
|
@ -11,7 +11,8 @@ import (
|
|||
)
|
||||
|
||||
var (
|
||||
ErrOutdatedTask = errors.New("task is outdated")
|
||||
ErrOutdatedTask = errors.New("task is outdated")
|
||||
ErrTaskIsNotRecurring = errors.New("task is not recurring")
|
||||
)
|
||||
|
||||
const (
|
||||
|
@ -124,6 +125,13 @@ func New(msg *mstore.Message) *Task {
|
|||
}
|
||||
due := NewDateFromString(dueStr)
|
||||
|
||||
// Recurrer
|
||||
recurStr, d := FieldFromBody(FIELD_RECUR, msg.Body)
|
||||
if d {
|
||||
dirty = true
|
||||
}
|
||||
recur := NewRecurrer(recurStr)
|
||||
|
||||
// Folder
|
||||
folderOld := msg.Folder
|
||||
folderNew := folderOld
|
||||
|
@ -131,11 +139,14 @@ func New(msg *mstore.Message) *Task {
|
|||
switch {
|
||||
case newId:
|
||||
folderNew = FOLDER_NEW
|
||||
case !newId && due.IsZero():
|
||||
case !newId && recur != nil:
|
||||
folderNew = FOLDER_RECURRING
|
||||
case !newId && recur == nil && due.IsZero():
|
||||
folderNew = FOLDER_UNPLANNED
|
||||
case !newId && !due.IsZero():
|
||||
case !newId && recur == nil && !due.IsZero():
|
||||
folderNew = FOLDER_PLANNED
|
||||
}
|
||||
|
||||
}
|
||||
if folderOld != folderNew {
|
||||
dirty = true
|
||||
|
@ -163,6 +174,7 @@ func New(msg *mstore.Message) *Task {
|
|||
Folder: folderNew,
|
||||
Action: action,
|
||||
Due: due,
|
||||
Recur: recur,
|
||||
Project: project,
|
||||
Message: msg,
|
||||
Current: true,
|
||||
|
@ -183,6 +195,13 @@ func (t *Task) FormatSubject() string {
|
|||
FIELD_DUE: t.Due.String(),
|
||||
}
|
||||
|
||||
if fields[FIELD_DUE] != "" && fields[FIELD_PROJECT] == "" {
|
||||
fields[FIELD_PROJECT] = " "
|
||||
}
|
||||
if fields[FIELD_PROJECT] != "" && fields[FIELD_ACTION] == "" {
|
||||
fields[FIELD_ACTION] = " "
|
||||
}
|
||||
|
||||
parts := []string{}
|
||||
for _, f := range order {
|
||||
if fields[f] != "" {
|
||||
|
@ -194,15 +213,21 @@ func (t *Task) FormatSubject() string {
|
|||
}
|
||||
|
||||
func (t *Task) FormatBody() string {
|
||||
body := fmt.Sprintf("\n")
|
||||
order := []string{FIELD_ACTION, FIELD_DUE, FIELD_PROJECT, FIELD_VERSION, FIELD_ID}
|
||||
order := []string{FIELD_ACTION}
|
||||
fields := map[string]string{
|
||||
FIELD_ID: t.Id,
|
||||
FIELD_VERSION: strconv.Itoa(t.Version),
|
||||
FIELD_PROJECT: t.Project,
|
||||
FIELD_ACTION: t.Action,
|
||||
FIELD_DUE: t.Due.String(),
|
||||
}
|
||||
if t.IsRecurrer() {
|
||||
order = append(order, FIELD_RECUR)
|
||||
fields[FIELD_RECUR] = t.Recur.String()
|
||||
} else {
|
||||
order = append(order, FIELD_DUE)
|
||||
fields[FIELD_DUE] = t.Due.String()
|
||||
}
|
||||
order = append(order, []string{FIELD_PROJECT, FIELD_VERSION, FIELD_ID}...)
|
||||
|
||||
keyLen := 0
|
||||
for _, f := range order {
|
||||
|
@ -211,6 +236,7 @@ func (t *Task) FormatBody() string {
|
|||
}
|
||||
}
|
||||
|
||||
body := fmt.Sprintf("\n")
|
||||
for _, f := range order {
|
||||
key := f + FIELD_SEPARATOR
|
||||
for i := len(key); i <= keyLen; i++ {
|
||||
|
@ -226,6 +252,35 @@ func (t *Task) FormatBody() string {
|
|||
return body
|
||||
}
|
||||
|
||||
func (t *Task) IsRecurrer() bool {
|
||||
return t.Recur != nil
|
||||
}
|
||||
|
||||
func (t *Task) RecursToday() bool {
|
||||
if !t.IsRecurrer() {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
||||
return t.Recur.RecursOn(Today)
|
||||
}
|
||||
|
||||
func (t *Task) CreateNextMessage(date Date) (string, string, error) {
|
||||
if !t.IsRecurrer() {
|
||||
return "", "", ErrTaskIsNotRecurring
|
||||
}
|
||||
|
||||
tempTask := &Task{
|
||||
Id: uuid.New().String(),
|
||||
Version: 1,
|
||||
Action: t.Action,
|
||||
Project: t.Project,
|
||||
Due: t.Recur.FirstAfter(date),
|
||||
}
|
||||
|
||||
return tempTask.FormatSubject(), tempTask.FormatBody(), nil
|
||||
}
|
||||
|
||||
func FieldFromBody(field, body string) (string, bool) {
|
||||
value := ""
|
||||
dirty := false
|
||||
|
|
Loading…
Reference in New Issue