189 lines
4.4 KiB
Go
Executable File
189 lines
4.4 KiB
Go
Executable File
package task
|
|
|
|
import (
|
|
"encoding/json"
|
|
"errors"
|
|
"fmt"
|
|
"local/todo-server/server/ajax/form"
|
|
"net/http"
|
|
"regexp"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
type Task struct {
|
|
UUID string
|
|
Title string
|
|
Priority int
|
|
Tags StrList
|
|
Created time.Time
|
|
Edited time.Time
|
|
|
|
Completed time.Time
|
|
Complete bool
|
|
Note []string
|
|
Due time.Time
|
|
Loop time.Duration
|
|
|
|
Index int
|
|
}
|
|
|
|
type StrList []string
|
|
|
|
func (sl StrList) MarshalJSON() ([]byte, error) {
|
|
s := strings.Join(sl, ",")
|
|
return json.Marshal(s)
|
|
}
|
|
|
|
func New(r *http.Request) (*Task, error) {
|
|
task := &Task{
|
|
UUID: form.NewUUID(),
|
|
Title: form.Get(r, "title"),
|
|
Priority: form.ToInt(form.Get(r, "prio")),
|
|
Tags: append(StrList(form.ToStrArr(form.Get(r, "tag"))), StrList(form.ToStrArr(form.Get(r, "tags")))...),
|
|
Created: time.Now(),
|
|
Edited: time.Now(),
|
|
Due: form.ToTime(form.Get(r, "duedate")),
|
|
Loop: form.ToDuration(form.Get(r, "loop")),
|
|
}
|
|
task.SetNote(form.Get(r, "note"))
|
|
return task, task.validate()
|
|
}
|
|
|
|
func (t *Task) MarshalJSON() ([]byte, error) {
|
|
// {"total":4,"list":[
|
|
// {"id":"3455",
|
|
// "title":"redo qvolution",
|
|
// "listId":"18",
|
|
// "date":"14 Oct 2019 12:56 PM",
|
|
// "dateInt":1571079392,
|
|
// "dateInline":"14 Oct",
|
|
// "dateInlineTitle":"created at 14 Oct 2019 12:56 PM",
|
|
// "dateEditedInt":1571079401,
|
|
// "dateCompleted":"",
|
|
// "dateCompletedInline":"",
|
|
// "dateCompletedInlineTitle":"Completed at ",
|
|
// "compl":0,
|
|
// "prio":"0",
|
|
// "note":"",
|
|
// "noteText":"",
|
|
// "ow":4,
|
|
// "tags":"work",
|
|
// "tags_ids":"138",
|
|
// "duedate":"",
|
|
// "dueClass":"",
|
|
// "dueStr":"",
|
|
// "dueInt":33330000,
|
|
// "dueTitle":"Due ",
|
|
// "loop": "1m"}
|
|
// ]}
|
|
fullFormat := "02 Jan 2006 03:04 PM"
|
|
shortFormat := "02 Jan"
|
|
compl := 0
|
|
if t.Complete {
|
|
compl = 1
|
|
}
|
|
tagsIds := make([]string, len(t.Tags))
|
|
for i, tag := range t.Tags {
|
|
tagsIds[i] = form.Hash(tag)
|
|
}
|
|
m := map[string]interface{}{
|
|
"id": t.UUID,
|
|
"title": t.Title,
|
|
"listId": "list",
|
|
"date": t.Created.Format(fullFormat),
|
|
"dateInt": t.Created.Unix(),
|
|
"dateInline": t.Created.Format(shortFormat),
|
|
"dateInlineTitle": fmt.Sprintf("created at %s", t.Created.Format(fullFormat)),
|
|
"dateEditedInt": t.Edited.Unix(),
|
|
"dateCompleted": "",
|
|
"dateCompletedInline": "",
|
|
"dateCompletedInlineTitle": "",
|
|
"compl": compl,
|
|
"prio": t.Priority,
|
|
"note": strings.Join(t.Note, "\n"),
|
|
"noteText": strings.Join(t.Note, "\n"),
|
|
"ow": 0,
|
|
"tags": strings.Join([]string(t.Tags), ","),
|
|
"tags_ids": strings.Join([]string(tagsIds), ","),
|
|
"duedate": t.Due.Format(fullFormat),
|
|
"dueClass": "",
|
|
"dueStr": t.Due.Format(shortFormat),
|
|
"dueInt": t.Due.Unix(),
|
|
"dueTitle": "Due ",
|
|
"loop": t.Loop.String(),
|
|
}
|
|
if t.Due.IsZero() {
|
|
for k := range m {
|
|
if strings.HasPrefix(k, "due") {
|
|
delete(m, k)
|
|
}
|
|
}
|
|
}
|
|
if t.Complete {
|
|
m["dateCompleted"] = t.Completed.Format(fullFormat)
|
|
m["dateCompletedInline"] = t.Completed.Format(shortFormat)
|
|
m["dateCompletedInlineTitle"] = "Completed at "
|
|
}
|
|
return json.Marshal(m)
|
|
}
|
|
|
|
func (t *Task) AppendTags(tags []string) {
|
|
t.touch()
|
|
t.Tags = append(t.Tags, tags...)
|
|
}
|
|
|
|
func (t *Task) SetComplete(state bool) bool {
|
|
t.touch()
|
|
changed := t.Complete != state
|
|
t.Complete = state
|
|
if t.Complete {
|
|
t.Completed = time.Now()
|
|
} else {
|
|
t.Completed = time.Time{}
|
|
}
|
|
return changed
|
|
}
|
|
|
|
func (t *Task) SetPrio(prio int) {
|
|
t.touch()
|
|
t.Priority = prio
|
|
}
|
|
|
|
func (t *Task) SetNote(note string) {
|
|
t.touch()
|
|
t.Note = strings.Split(note, "\n")
|
|
}
|
|
|
|
func (t *Task) touch() {
|
|
t.Edited = time.Now()
|
|
}
|
|
|
|
func (t *Task) validate() error {
|
|
if t.Title == "" {
|
|
return errors.New("task cannot have nil title")
|
|
}
|
|
if err := t.smartSyntax(); err != nil {
|
|
return err
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (t *Task) smartSyntax() error {
|
|
re := regexp.MustCompile(`^(/([+-]{0,1}\d+)?/)?(.*?)(\s+/([^/]*)/$)?$|`)
|
|
matches := re.FindAllStringSubmatch(t.Title, 1)[0]
|
|
if len(matches) != 6 {
|
|
return nil
|
|
}
|
|
if matches[1] != "" {
|
|
t.Priority = form.ToInt(matches[1])
|
|
}
|
|
if matches[3] != "" {
|
|
t.Title = matches[3]
|
|
}
|
|
if matches[5] != "" {
|
|
t.Tags = form.ToStrArr(matches[5])
|
|
}
|
|
return nil
|
|
}
|