This commit is contained in:
parent
7bd6e910c3
commit
3e49c27ad1
|
@ -62,7 +62,7 @@ type Todo struct {
|
||||||
type Alarm struct {
|
type Alarm struct {
|
||||||
Time time.Time
|
Time time.Time
|
||||||
Duration time.Duration
|
Duration time.Duration
|
||||||
RelativeTo string
|
RelativeTo models.ReminderRelation
|
||||||
Description string
|
Description string
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -204,13 +204,13 @@ func ParseAlarms(alarms []Alarm, taskDescription string) (caldavalarms string) {
|
||||||
caldavalarms += `
|
caldavalarms += `
|
||||||
BEGIN:VALARM`
|
BEGIN:VALARM`
|
||||||
switch a.RelativeTo {
|
switch a.RelativeTo {
|
||||||
case "due_date":
|
case models.ReminderRelationDueDate:
|
||||||
caldavalarms += `
|
caldavalarms += `
|
||||||
TRIGGER:` + makeCalDavDuration(a.Duration)
|
TRIGGER:` + makeCalDavDuration(a.Duration)
|
||||||
case "start_date":
|
case models.ReminderRelationStartDate:
|
||||||
caldavalarms += `
|
caldavalarms += `
|
||||||
TRIGGER;RELATED=START:` + makeCalDavDuration(a.Duration)
|
TRIGGER;RELATED=START:` + makeCalDavDuration(a.Duration)
|
||||||
case "end_date":
|
case models.ReminderRelationEndDate:
|
||||||
caldavalarms += `
|
caldavalarms += `
|
||||||
TRIGGER;RELATED=END:` + makeCalDavDuration(a.Duration)
|
TRIGGER;RELATED=END:` + makeCalDavDuration(a.Duration)
|
||||||
default:
|
default:
|
||||||
|
|
|
@ -18,13 +18,13 @@ package caldav
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"regexp"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"code.vikunja.io/api/pkg/log"
|
"code.vikunja.io/api/pkg/log"
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
|
||||||
ics "github.com/arran4/golang-ical"
|
ics "github.com/arran4/golang-ical"
|
||||||
)
|
)
|
||||||
|
@ -45,7 +45,7 @@ func GetCaldavTodosForTasks(project *models.ProjectWithTasksAndBuckets, projectT
|
||||||
alarms = append(alarms, Alarm{
|
alarms = append(alarms, Alarm{
|
||||||
Time: reminder.Reminder,
|
Time: reminder.Reminder,
|
||||||
Duration: time.Duration(reminder.RelativePeriod) * time.Second,
|
Duration: time.Duration(reminder.RelativePeriod) * time.Second,
|
||||||
RelativeTo: string(reminder.RelativeTo),
|
RelativeTo: reminder.RelativeTo,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ func parseVAlarm(vAlarm *ics.VAlarm, reminders []*models.TaskReminder) []*models
|
||||||
Reminder: caldavTimeToTimestamp(property.Value)})
|
Reminder: caldavTimeToTimestamp(property.Value)})
|
||||||
}
|
}
|
||||||
case len(property.ICalParameters["RELATED"]) > 0:
|
case len(property.ICalParameters["RELATED"]) > 0:
|
||||||
duration := parseDuration(property.Value)
|
duration := utils.ParseISO8601Duration(property.Value)
|
||||||
switch property.ICalParameters["RELATED"][0] {
|
switch property.ICalParameters["RELATED"][0] {
|
||||||
case "START":
|
case "START":
|
||||||
// Example: TRIGGER;RELATED=START:-P2D
|
// Example: TRIGGER;RELATED=START:-P2D
|
||||||
|
@ -179,7 +179,7 @@ func parseVAlarm(vAlarm *ics.VAlarm, reminders []*models.TaskReminder) []*models
|
||||||
RelativeTo: models.ReminderRelationEndDate})
|
RelativeTo: models.ReminderRelationEndDate})
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
duration := parseDuration(property.Value)
|
duration := utils.ParseISO8601Duration(property.Value)
|
||||||
// Example: TRIGGER:-PT60M
|
// Example: TRIGGER:-PT60M
|
||||||
reminders = append(reminders, &models.TaskReminder{
|
reminders = append(reminders, &models.TaskReminder{
|
||||||
RelativePeriod: int64(duration.Seconds()),
|
RelativePeriod: int64(duration.Seconds()),
|
||||||
|
@ -213,37 +213,3 @@ func caldavTimeToTimestamp(tstring string) time.Time {
|
||||||
}
|
}
|
||||||
return t
|
return t
|
||||||
}
|
}
|
||||||
|
|
||||||
var durationRegex = regexp.MustCompile(`([-+])?P([\d\.]+Y)?([\d\.]+M)?([\d\.]+D)?T?([\d\.]+H)?([\d\.]+M)?([\d\.]+?S)?`)
|
|
||||||
|
|
||||||
// ParseDuration converts a ISO8601 duration into a time.Duration
|
|
||||||
func parseDuration(str string) time.Duration {
|
|
||||||
matches := durationRegex.FindStringSubmatch(str)
|
|
||||||
|
|
||||||
if len(matches) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
years := parseDurationPart(matches[2], time.Hour*24*365)
|
|
||||||
months := parseDurationPart(matches[3], time.Hour*24*30)
|
|
||||||
days := parseDurationPart(matches[4], time.Hour*24)
|
|
||||||
hours := parseDurationPart(matches[5], time.Hour)
|
|
||||||
minutes := parseDurationPart(matches[6], time.Second*60)
|
|
||||||
seconds := parseDurationPart(matches[7], time.Second)
|
|
||||||
|
|
||||||
duration := years + months + days + hours + minutes + seconds
|
|
||||||
|
|
||||||
if matches[1] == "-" {
|
|
||||||
return -duration
|
|
||||||
}
|
|
||||||
return duration
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDurationPart(value string, unit time.Duration) time.Duration {
|
|
||||||
if len(value) != 0 {
|
|
||||||
if parsed, err := strconv.ParseFloat(value[:len(value)-1], 64); err == nil {
|
|
||||||
return time.Duration(float64(unit) * parsed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
|
@ -20,9 +20,7 @@ import (
|
||||||
"encoding/csv"
|
"encoding/csv"
|
||||||
"errors"
|
"errors"
|
||||||
"io"
|
"io"
|
||||||
"regexp"
|
|
||||||
"sort"
|
"sort"
|
||||||
"strconv"
|
|
||||||
"strings"
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
@ -30,6 +28,7 @@ import (
|
||||||
"code.vikunja.io/api/pkg/models"
|
"code.vikunja.io/api/pkg/models"
|
||||||
"code.vikunja.io/api/pkg/modules/migration"
|
"code.vikunja.io/api/pkg/modules/migration"
|
||||||
"code.vikunja.io/api/pkg/user"
|
"code.vikunja.io/api/pkg/user"
|
||||||
|
"code.vikunja.io/api/pkg/utils"
|
||||||
|
|
||||||
"github.com/gocarina/gocsv"
|
"github.com/gocarina/gocsv"
|
||||||
)
|
)
|
||||||
|
@ -75,36 +74,6 @@ func (date *tickTickTime) UnmarshalCSV(csv string) (err error) {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
// Copied from https://stackoverflow.com/a/57617885
|
|
||||||
var durationRegex = regexp.MustCompile(`P([\d\.]+Y)?([\d\.]+M)?([\d\.]+D)?T?([\d\.]+H)?([\d\.]+M)?([\d\.]+?S)?`)
|
|
||||||
|
|
||||||
// ParseDuration converts a ISO8601 duration into a time.Duration
|
|
||||||
func parseDuration(str string) time.Duration {
|
|
||||||
matches := durationRegex.FindStringSubmatch(str)
|
|
||||||
|
|
||||||
if len(matches) == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
years := parseDurationPart(matches[1], time.Hour*24*365)
|
|
||||||
months := parseDurationPart(matches[2], time.Hour*24*30)
|
|
||||||
days := parseDurationPart(matches[3], time.Hour*24)
|
|
||||||
hours := parseDurationPart(matches[4], time.Hour)
|
|
||||||
minutes := parseDurationPart(matches[5], time.Second*60)
|
|
||||||
seconds := parseDurationPart(matches[6], time.Second)
|
|
||||||
|
|
||||||
return years + months + days + hours + minutes + seconds
|
|
||||||
}
|
|
||||||
|
|
||||||
func parseDurationPart(value string, unit time.Duration) time.Duration {
|
|
||||||
if len(value) != 0 {
|
|
||||||
if parsed, err := strconv.ParseFloat(value[:len(value)-1], 64); err == nil {
|
|
||||||
return time.Duration(float64(unit) * parsed)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.NamespaceWithProjectsAndTasks) {
|
func convertTickTickToVikunja(tasks []*tickTickTask) (result []*models.NamespaceWithProjectsAndTasks) {
|
||||||
namespace := &models.NamespaceWithProjectsAndTasks{
|
namespace := &models.NamespaceWithProjectsAndTasks{
|
||||||
Namespace: models.Namespace{
|
Namespace: models.Namespace{
|
||||||
|
@ -231,7 +200,7 @@ func (m *Migrator) Migrate(user *user.User, file io.ReaderAt, size int64) error
|
||||||
task.IsChecklist = true
|
task.IsChecklist = true
|
||||||
}
|
}
|
||||||
|
|
||||||
reminder := parseDuration(task.ReminderDuration)
|
reminder := utils.ParseISO8601Duration(task.ReminderDuration)
|
||||||
if reminder > 0 {
|
if reminder > 0 {
|
||||||
task.Reminder = reminder
|
task.Reminder = reminder
|
||||||
}
|
}
|
||||||
|
|
57
pkg/utils/duration.go
Normal file
57
pkg/utils/duration.go
Normal file
|
@ -0,0 +1,57 @@
|
||||||
|
// 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 utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"regexp"
|
||||||
|
"strconv"
|
||||||
|
"time"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ParseISO8601Duration converts a ISO8601 duration into a time.Duration
|
||||||
|
func ParseISO8601Duration(str string) time.Duration {
|
||||||
|
matches := durationRegex.FindStringSubmatch(str)
|
||||||
|
|
||||||
|
if len(matches) == 0 {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
years := parseDurationPart(matches[2], time.Hour*24*365)
|
||||||
|
months := parseDurationPart(matches[3], time.Hour*24*30)
|
||||||
|
days := parseDurationPart(matches[4], time.Hour*24)
|
||||||
|
hours := parseDurationPart(matches[5], time.Hour)
|
||||||
|
minutes := parseDurationPart(matches[6], time.Second*60)
|
||||||
|
seconds := parseDurationPart(matches[7], time.Second)
|
||||||
|
|
||||||
|
duration := years + months + days + hours + minutes + seconds
|
||||||
|
|
||||||
|
if matches[1] == "-" {
|
||||||
|
return -duration
|
||||||
|
}
|
||||||
|
return duration
|
||||||
|
}
|
||||||
|
|
||||||
|
var durationRegex = regexp.MustCompile(`([-+])?P([\d\.]+Y)?([\d\.]+M)?([\d\.]+D)?T?([\d\.]+H)?([\d\.]+M)?([\d\.]+?S)?`)
|
||||||
|
|
||||||
|
func parseDurationPart(value string, unit time.Duration) time.Duration {
|
||||||
|
if len(value) != 0 {
|
||||||
|
if parsed, err := strconv.ParseFloat(value[:len(value)-1], 64); err == nil {
|
||||||
|
return time.Duration(float64(unit) * parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}
|
39
pkg/utils/duration_test.go
Normal file
39
pkg/utils/duration_test.go
Normal file
|
@ -0,0 +1,39 @@
|
||||||
|
// 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 utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"testing"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/stretchr/testify/assert"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestParseISO8601Duration(t *testing.T) {
|
||||||
|
t.Run("full example", func(t *testing.T) {
|
||||||
|
dur := ParseISO8601Duration("P1DT1H1M1S")
|
||||||
|
expected, _ := time.ParseDuration("25h1m1s")
|
||||||
|
|
||||||
|
assert.Equal(t, expected, dur)
|
||||||
|
})
|
||||||
|
t.Run("negative duration", func(t *testing.T) {
|
||||||
|
dur := ParseISO8601Duration("-P1DT1H1M1S")
|
||||||
|
expected, _ := time.ParseDuration("-25h1m1s")
|
||||||
|
|
||||||
|
assert.Equal(t, expected, dur)
|
||||||
|
})
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user