From 3e49c27ad1b4d9d42db8d724b55bb37114f1c9a2 Mon Sep 17 00:00:00 2001 From: ce72 Date: Wed, 29 Mar 2023 19:24:33 +0200 Subject: [PATCH] fix: review findings --- pkg/caldav/caldav.go | 8 +-- pkg/caldav/parsing.go | 42 ++-------------- pkg/modules/migration/ticktick/ticktick.go | 35 +------------ pkg/utils/duration.go | 57 ++++++++++++++++++++++ pkg/utils/duration_test.go | 39 +++++++++++++++ 5 files changed, 106 insertions(+), 75 deletions(-) create mode 100644 pkg/utils/duration.go create mode 100644 pkg/utils/duration_test.go diff --git a/pkg/caldav/caldav.go b/pkg/caldav/caldav.go index 830c73f94..309997538 100644 --- a/pkg/caldav/caldav.go +++ b/pkg/caldav/caldav.go @@ -62,7 +62,7 @@ type Todo struct { type Alarm struct { Time time.Time Duration time.Duration - RelativeTo string + RelativeTo models.ReminderRelation Description string } @@ -204,13 +204,13 @@ func ParseAlarms(alarms []Alarm, taskDescription string) (caldavalarms string) { caldavalarms += ` BEGIN:VALARM` switch a.RelativeTo { - case "due_date": + case models.ReminderRelationDueDate: caldavalarms += ` TRIGGER:` + makeCalDavDuration(a.Duration) - case "start_date": + case models.ReminderRelationStartDate: caldavalarms += ` TRIGGER;RELATED=START:` + makeCalDavDuration(a.Duration) - case "end_date": + case models.ReminderRelationEndDate: caldavalarms += ` TRIGGER;RELATED=END:` + makeCalDavDuration(a.Duration) default: diff --git a/pkg/caldav/parsing.go b/pkg/caldav/parsing.go index 38a4d0238..004d50cbb 100644 --- a/pkg/caldav/parsing.go +++ b/pkg/caldav/parsing.go @@ -18,13 +18,13 @@ package caldav import ( "errors" - "regexp" "strconv" "strings" "time" "code.vikunja.io/api/pkg/log" "code.vikunja.io/api/pkg/models" + "code.vikunja.io/api/pkg/utils" ics "github.com/arran4/golang-ical" ) @@ -45,7 +45,7 @@ func GetCaldavTodosForTasks(project *models.ProjectWithTasksAndBuckets, projectT alarms = append(alarms, Alarm{ Time: reminder.Reminder, 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)}) } case len(property.ICalParameters["RELATED"]) > 0: - duration := parseDuration(property.Value) + duration := utils.ParseISO8601Duration(property.Value) switch property.ICalParameters["RELATED"][0] { case "START": // Example: TRIGGER;RELATED=START:-P2D @@ -179,7 +179,7 @@ func parseVAlarm(vAlarm *ics.VAlarm, reminders []*models.TaskReminder) []*models RelativeTo: models.ReminderRelationEndDate}) } default: - duration := parseDuration(property.Value) + duration := utils.ParseISO8601Duration(property.Value) // Example: TRIGGER:-PT60M reminders = append(reminders, &models.TaskReminder{ RelativePeriod: int64(duration.Seconds()), @@ -213,37 +213,3 @@ func caldavTimeToTimestamp(tstring string) time.Time { } 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 -} diff --git a/pkg/modules/migration/ticktick/ticktick.go b/pkg/modules/migration/ticktick/ticktick.go index c31c8817a..861408d77 100644 --- a/pkg/modules/migration/ticktick/ticktick.go +++ b/pkg/modules/migration/ticktick/ticktick.go @@ -20,9 +20,7 @@ import ( "encoding/csv" "errors" "io" - "regexp" "sort" - "strconv" "strings" "time" @@ -30,6 +28,7 @@ import ( "code.vikunja.io/api/pkg/models" "code.vikunja.io/api/pkg/modules/migration" "code.vikunja.io/api/pkg/user" + "code.vikunja.io/api/pkg/utils" "github.com/gocarina/gocsv" ) @@ -75,36 +74,6 @@ func (date *tickTickTime) UnmarshalCSV(csv string) (err error) { 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) { namespace := &models.NamespaceWithProjectsAndTasks{ Namespace: models.Namespace{ @@ -231,7 +200,7 @@ func (m *Migrator) Migrate(user *user.User, file io.ReaderAt, size int64) error task.IsChecklist = true } - reminder := parseDuration(task.ReminderDuration) + reminder := utils.ParseISO8601Duration(task.ReminderDuration) if reminder > 0 { task.Reminder = reminder } diff --git a/pkg/utils/duration.go b/pkg/utils/duration.go new file mode 100644 index 000000000..de394497f --- /dev/null +++ b/pkg/utils/duration.go @@ -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 . + +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 +} diff --git a/pkg/utils/duration_test.go b/pkg/utils/duration_test.go new file mode 100644 index 000000000..1af2e905d --- /dev/null +++ b/pkg/utils/duration_test.go @@ -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 . + +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) + }) +}