parent
562ef9af36
commit
e21a3904ff
8 changed files with 350 additions and 17 deletions
|
@ -216,7 +216,7 @@ DURATION:PT` + fmt.Sprintf("%.6f", t.Duration.Hours()) + `H` + fmt.Sprintf("%.6f
|
|||
|
||||
if t.Priority != 0 {
|
||||
caldavtodos += `
|
||||
PRIORITY:` + strconv.Itoa(int(t.Priority))
|
||||
PRIORITY:` + strconv.Itoa(mapPriorityToCaldav(t.Priority))
|
||||
}
|
||||
|
||||
caldavtodos += `
|
||||
|
|
|
@ -375,6 +375,39 @@ COMPLETED:20181201T013024
|
|||
STATUS:COMPLETED
|
||||
LAST-MODIFIED:00010101T000000
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
{
|
||||
name: "with priority",
|
||||
args: args{
|
||||
config: &Config{
|
||||
Name: "test",
|
||||
ProdID: "RandomProdID which is not random",
|
||||
},
|
||||
todos: []*Todo{
|
||||
{
|
||||
Summary: "Todo #1",
|
||||
Description: "Lorem Ipsum",
|
||||
UID: "randommduid",
|
||||
Priority: 1,
|
||||
Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
},
|
||||
},
|
||||
wantCaldavtasks: `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:test
|
||||
PRODID:-//RandomProdID which is not random//EN
|
||||
BEGIN:VTODO
|
||||
UID:randommduid
|
||||
DTSTAMP:20181201T011204
|
||||
SUMMARY:Todo #1
|
||||
DESCRIPTION:Lorem Ipsum
|
||||
PRIORITY:9
|
||||
LAST-MODIFIED:00010101T000000
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
}
|
||||
|
|
|
@ -21,21 +21,20 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/caldav"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"github.com/laurent22/ical-go"
|
||||
)
|
||||
|
||||
func getCaldavTodosForTasks(list *models.List, listTasks []*models.Task) string {
|
||||
func GetCaldavTodosForTasks(list *models.List, listTasks []*models.Task) string {
|
||||
|
||||
// Make caldav todos from Vikunja todos
|
||||
var caldavtodos []*caldav.Todo
|
||||
var caldavtodos []*Todo
|
||||
for _, t := range listTasks {
|
||||
|
||||
duration := t.EndDate.Sub(t.StartDate)
|
||||
|
||||
caldavtodos = append(caldavtodos, &caldav.Todo{
|
||||
caldavtodos = append(caldavtodos, &Todo{
|
||||
Timestamp: t.Updated,
|
||||
UID: t.UID,
|
||||
Summary: t.Title,
|
||||
|
@ -52,15 +51,15 @@ func getCaldavTodosForTasks(list *models.List, listTasks []*models.Task) string
|
|||
})
|
||||
}
|
||||
|
||||
caldavConfig := &caldav.Config{
|
||||
caldavConfig := &Config{
|
||||
Name: list.Title,
|
||||
ProdID: "Vikunja Todo App",
|
||||
}
|
||||
|
||||
return caldav.ParseTodos(caldavConfig, caldavtodos)
|
||||
return ParseTodos(caldavConfig, caldavtodos)
|
||||
}
|
||||
|
||||
func parseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
func ParseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
||||
parsed, err := ical.ParseCalendar(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
|
@ -78,13 +77,15 @@ func parseTaskFromVTODO(content string) (vTask *models.Task, err error) {
|
|||
}
|
||||
}
|
||||
|
||||
// Parse the UID
|
||||
// Parse the priority
|
||||
var priority int64
|
||||
if _, ok := task["PRIORITY"]; ok {
|
||||
priority, err = strconv.ParseInt(task["PRIORITY"], 10, 64)
|
||||
priorityParsed, err := strconv.ParseInt(task["PRIORITY"], 10, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
priority = parseVTODOPriority(priorityParsed)
|
||||
}
|
||||
|
||||
// Parse the enddate
|
||||
|
@ -118,7 +119,7 @@ func caldavTimeToTimestamp(tstring string) time.Time {
|
|||
return time.Time{}
|
||||
}
|
||||
|
||||
format := caldav.DateFormat
|
||||
format := DateFormat
|
||||
|
||||
if strings.HasSuffix(tstring, "Z") {
|
||||
format = `20060102T150405Z`
|
101
pkg/caldav/parsing_test.go
Normal file
101
pkg/caldav/parsing_test.go
Normal file
|
@ -0,0 +1,101 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public Licensee for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public Licensee
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package caldav
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/config"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"gopkg.in/d4l3k/messagediff.v1"
|
||||
)
|
||||
|
||||
func TestParseTaskFromVTODO(t *testing.T) {
|
||||
type args struct {
|
||||
content string
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantVTask *models.Task
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "normal",
|
||||
args: args{content: `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:test
|
||||
PRODID:-//RandomProdID which is not random//EN
|
||||
BEGIN:VTODO
|
||||
UID:randomuid
|
||||
DTSTAMP:20181201T011204
|
||||
SUMMARY:Todo #1
|
||||
DESCRIPTION:Lorem Ipsum
|
||||
LAST-MODIFIED:00010101T000000
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
wantVTask: &models.Task{
|
||||
Title: "Todo #1",
|
||||
UID: "randomuid",
|
||||
Description: "Lorem Ipsum",
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "With priority",
|
||||
args: args{content: `BEGIN:VCALENDAR
|
||||
VERSION:2.0
|
||||
METHOD:PUBLISH
|
||||
X-PUBLISHED-TTL:PT4H
|
||||
X-WR-CALNAME:test
|
||||
PRODID:-//RandomProdID which is not random//EN
|
||||
BEGIN:VTODO
|
||||
UID:randomuid
|
||||
DTSTAMP:20181201T011204
|
||||
SUMMARY:Todo #1
|
||||
DESCRIPTION:Lorem Ipsum
|
||||
PRIORITY:9
|
||||
LAST-MODIFIED:00010101T000000
|
||||
END:VTODO
|
||||
END:VCALENDAR`,
|
||||
},
|
||||
wantVTask: &models.Task{
|
||||
Title: "Todo #1",
|
||||
UID: "randomuid",
|
||||
Description: "Lorem Ipsum",
|
||||
Priority: 1,
|
||||
Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()),
|
||||
},
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ParseTaskFromVTODO(tt.args.content)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("ParseTaskFromVTODO() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if diff, equal := messagediff.PrettyDiff(got, tt.wantVTask); !equal {
|
||||
t.Errorf("ParseTaskFromVTODO() gotVTask = %v, want %v, diff = %s", got, tt.wantVTask, diff)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
66
pkg/caldav/priority.go
Normal file
66
pkg/caldav/priority.go
Normal file
|
@ -0,0 +1,66 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public Licensee for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public Licensee
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package caldav
|
||||
|
||||
// In caldav, priority values are an int from 0 to 9 where 1 is the highest priority and 9 the lowest. 0 is "unset".
|
||||
// Vikunja only has priorites from 0 to 5 where 0 is unset and 5 is the highest
|
||||
// See https://icalendar.org/iCalendar-RFC-5545/3-8-1-9-priority.html
|
||||
func mapPriorityToCaldav(priority int64) (caldavPriority int) {
|
||||
switch priority {
|
||||
case 0:
|
||||
return 0
|
||||
case 1: // Low
|
||||
return 9
|
||||
case 2: // Medium
|
||||
return 5
|
||||
case 3: // High
|
||||
return 3
|
||||
case 4: // Urgent
|
||||
return 2
|
||||
case 5: // DO NOW
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
// See mapPriorityToCaldav
|
||||
func parseVTODOPriority(priority int64) (vikunjaPriority int64) {
|
||||
switch priority {
|
||||
case 0:
|
||||
return 0
|
||||
case 1:
|
||||
return 5
|
||||
case 2:
|
||||
return 4
|
||||
case 3:
|
||||
return 3
|
||||
case 4:
|
||||
return 3
|
||||
case 5:
|
||||
return 2
|
||||
case 6:
|
||||
return 1
|
||||
case 7:
|
||||
return 1
|
||||
case 8:
|
||||
return 1
|
||||
case 9:
|
||||
return 1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
131
pkg/caldav/priority_test.go
Normal file
131
pkg/caldav/priority_test.go
Normal file
|
@ -0,0 +1,131 @@
|
|||
// Vikunja is a to-do list application to facilitate your life.
|
||||
// Copyright 2018-2021 Vikunja and contributors. All rights reserved.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU Affero General Public Licensee as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU Affero General Public Licensee for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU Affero General Public Licensee
|
||||
// along with this program. If not, see <https://www.gnu.org/licenses/>.
|
||||
|
||||
package caldav
|
||||
|
||||
import "testing"
|
||||
|
||||
func Test_parseVTODOPriority(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
priority int64
|
||||
want int64
|
||||
}{
|
||||
{
|
||||
name: "unset",
|
||||
priority: 0,
|
||||
want: 0,
|
||||
},
|
||||
{
|
||||
name: "DO NOW",
|
||||
priority: 1,
|
||||
want: 5,
|
||||
},
|
||||
{
|
||||
name: "urgent",
|
||||
priority: 2,
|
||||
want: 4,
|
||||
},
|
||||
{
|
||||
name: "high 1",
|
||||
priority: 3,
|
||||
want: 3,
|
||||
},
|
||||
{
|
||||
name: "high 2",
|
||||
priority: 4,
|
||||
want: 3,
|
||||
},
|
||||
{
|
||||
name: "medium",
|
||||
priority: 5,
|
||||
want: 2,
|
||||
},
|
||||
{
|
||||
name: "low 1",
|
||||
priority: 6,
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "low 2",
|
||||
priority: 7,
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "low 3",
|
||||
priority: 8,
|
||||
want: 1,
|
||||
},
|
||||
{
|
||||
name: "low 4",
|
||||
priority: 9,
|
||||
want: 1,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotVikunjaPriority := parseVTODOPriority(tt.priority); gotVikunjaPriority != tt.want {
|
||||
t.Errorf("parseVTODOPriority() = %v, want %v", gotVikunjaPriority, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_mapPriorityToCaldav(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
priority int64
|
||||
wantCaldavPriority int
|
||||
}{
|
||||
{
|
||||
name: "unset",
|
||||
priority: 0,
|
||||
wantCaldavPriority: 0,
|
||||
},
|
||||
{
|
||||
name: "low",
|
||||
priority: 1,
|
||||
wantCaldavPriority: 9,
|
||||
},
|
||||
{
|
||||
name: "medium",
|
||||
priority: 2,
|
||||
wantCaldavPriority: 5,
|
||||
},
|
||||
{
|
||||
name: "high",
|
||||
priority: 3,
|
||||
wantCaldavPriority: 3,
|
||||
},
|
||||
{
|
||||
name: "urgent",
|
||||
priority: 4,
|
||||
wantCaldavPriority: 2,
|
||||
},
|
||||
{
|
||||
name: "DO NOW",
|
||||
priority: 5,
|
||||
wantCaldavPriority: 1,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if gotCaldavPriority := mapPriorityToCaldav(tt.priority); gotCaldavPriority != tt.wantCaldavPriority {
|
||||
t.Errorf("mapPriorityToCaldav() = %v, want %v", gotCaldavPriority, tt.wantCaldavPriority)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
|
@ -24,6 +24,7 @@ import (
|
|||
"strconv"
|
||||
"strings"
|
||||
|
||||
caldav2 "code.vikunja.io/api/pkg/caldav"
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
"code.vikunja.io/api/pkg/user"
|
||||
|
@ -66,7 +67,7 @@ func ListHandler(c echo.Context) error {
|
|||
// Parse it
|
||||
vtodo := string(body)
|
||||
if vtodo != "" && strings.HasPrefix(vtodo, `BEGIN:VCALENDAR`) {
|
||||
storage.task, err = parseTaskFromVTODO(vtodo)
|
||||
storage.task, err = caldav2.ParseTaskFromVTODO(vtodo)
|
||||
if err != nil {
|
||||
log.Error(err)
|
||||
return echo.ErrInternalServerError
|
||||
|
|
|
@ -21,8 +21,8 @@ import (
|
|||
"strings"
|
||||
"time"
|
||||
|
||||
"code.vikunja.io/api/pkg/caldav"
|
||||
"code.vikunja.io/api/pkg/db"
|
||||
|
||||
"code.vikunja.io/api/pkg/log"
|
||||
"code.vikunja.io/api/pkg/models"
|
||||
user2 "code.vikunja.io/api/pkg/user"
|
||||
|
@ -256,7 +256,7 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
|
|||
s := db.NewSession()
|
||||
defer s.Close()
|
||||
|
||||
vTask, err := parseTaskFromVTODO(content)
|
||||
vTask, err := caldav.ParseTaskFromVTODO(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -295,7 +295,7 @@ func (vcls *VikunjaCaldavListStorage) CreateResource(rpath, content string) (*da
|
|||
// UpdateResource updates a resource
|
||||
func (vcls *VikunjaCaldavListStorage) UpdateResource(rpath, content string) (*data.Resource, error) {
|
||||
|
||||
vTask, err := parseTaskFromVTODO(content)
|
||||
vTask, err := caldav.ParseTaskFromVTODO(content)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
@ -411,12 +411,12 @@ func (vlra *VikunjaListResourceAdapter) CalculateEtag() string {
|
|||
// GetContent returns the content string of a resource (a task in our case)
|
||||
func (vlra *VikunjaListResourceAdapter) GetContent() string {
|
||||
if vlra.list != nil && vlra.list.Tasks != nil {
|
||||
return getCaldavTodosForTasks(vlra.list, vlra.listTasks)
|
||||
return caldav.GetCaldavTodosForTasks(vlra.list, vlra.listTasks)
|
||||
}
|
||||
|
||||
if vlra.task != nil {
|
||||
list := models.List{Tasks: []*models.Task{vlra.task}}
|
||||
return getCaldavTodosForTasks(&list, list.Tasks)
|
||||
return caldav.GetCaldavTodosForTasks(&list, list.Tasks)
|
||||
}
|
||||
|
||||
return ""
|
||||
|
|
Loading…
Reference in a new issue