From 30c1d698e9eaebb065baa4163fe104b043cbca0b Mon Sep 17 00:00:00 2001 From: ce72 Date: Sat, 4 Mar 2023 12:36:02 +0100 Subject: [PATCH] chore(caldav): cleanup --- pkg/caldav/caldav.go | 52 ------- pkg/caldav/caldav_test.go | 277 +------------------------------------ pkg/caldav/parsing.go | 57 ++++---- pkg/caldav/parsing_test.go | 65 ++++++--- 4 files changed, 79 insertions(+), 372 deletions(-) diff --git a/pkg/caldav/caldav.go b/pkg/caldav/caldav.go index f04eff123..7ec629218 100644 --- a/pkg/caldav/caldav.go +++ b/pkg/caldav/caldav.go @@ -88,58 +88,6 @@ X-OUTLOOK-COLOR:` + color + ` X-FUNAMBOL-COLOR:` + color } -// ParseEvents parses an array of caldav events and gives them back as string -func ParseEvents(config *Config, events []*Event) (caldavevents string) { - caldavevents += `BEGIN:VCALENDAR -VERSION:2.0 -METHOD:PUBLISH -X-PUBLISHED-TTL:PT4H -X-WR-CALNAME:` + config.Name + ` -PRODID:-//` + config.ProdID + `//EN` + getCaldavColor(config.Color) - - for _, e := range events { - - if e.UID == "" { - e.UID = makeCalDavTimeFromTimeStamp(e.Timestamp) + utils.Sha256(e.Summary) - } - - formattedDescription := "" - if e.Description != "" { - re := regexp.MustCompile(`\r?\n`) - formattedDescription = re.ReplaceAllString(e.Description, "\\n") - } - - caldavevents += ` -BEGIN:VEVENT -UID:` + e.UID + ` -SUMMARY:` + e.Summary + getCaldavColor(e.Color) + ` -DESCRIPTION:` + formattedDescription + ` -DTSTAMP:` + makeCalDavTimeFromTimeStamp(e.Timestamp) + ` -DTSTART:` + makeCalDavTimeFromTimeStamp(e.Start) + ` -DTEND:` + makeCalDavTimeFromTimeStamp(e.End) - - for _, a := range e.Alarms { - if a.Description == "" { - a.Description = e.Summary - } - - caldavevents += ` -BEGIN:VALARM -TRIGGER:` + calcAlarmDateFromReminder(e.Start, a.Time) + ` -ACTION:DISPLAY -DESCRIPTION:` + a.Description + ` -END:VALARM` - } - caldavevents += ` -END:VEVENT` - } - - caldavevents += ` -END:VCALENDAR` // Need a line break - - return -} - func formatDuration(duration time.Duration) string { seconds := duration.Seconds() - duration.Minutes()*60 minutes := duration.Minutes() - duration.Hours()*60 diff --git a/pkg/caldav/caldav_test.go b/pkg/caldav/caldav_test.go index 11b1a42cd..63252fbb1 100644 --- a/pkg/caldav/caldav_test.go +++ b/pkg/caldav/caldav_test.go @@ -26,275 +26,6 @@ import ( "github.com/stretchr/testify/assert" ) -func TestParseEvents(t *testing.T) { - type args struct { - config *Config - events []*Event - } - tests := []struct { - name string - args args - wantCaldavevents string - }{ - { - name: "Test caldavparsing without reminders", - args: args{ - config: &Config{ - Name: "test", - ProdID: "RandomProdID which is not random", - Color: "ffffff", - }, - events: []*Event{ - { - Summary: "Event #1", - Description: "Lorem Ipsum", - UID: "randommduid", - Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()), - Start: time.Unix(1543626724, 0).In(config.GetTimeZone()), - End: time.Unix(1543627824, 0).In(config.GetTimeZone()), - Color: "affffe", - }, - { - Summary: "Event #2", - UID: "randommduidd", - Timestamp: time.Unix(1543726724, 0).In(config.GetTimeZone()), - Start: time.Unix(1543726724, 0).In(config.GetTimeZone()), - End: time.Unix(1543738724, 0).In(config.GetTimeZone()), - }, - { - Summary: "Event #3 with empty uid", - UID: "20181202T0600242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83", - Timestamp: time.Unix(1543726824, 0).In(config.GetTimeZone()), - Start: time.Unix(1543726824, 0).In(config.GetTimeZone()), - End: time.Unix(1543727000, 0).In(config.GetTimeZone()), - }, - }, - }, - wantCaldavevents: `BEGIN:VCALENDAR -VERSION:2.0 -METHOD:PUBLISH -X-PUBLISHED-TTL:PT4H -X-WR-CALNAME:test -PRODID:-//RandomProdID which is not random//EN -X-APPLE-CALENDAR-COLOR:#ffffffFF -X-OUTLOOK-COLOR:#ffffffFF -X-FUNAMBOL-COLOR:#ffffffFF -BEGIN:VEVENT -UID:randommduid -SUMMARY:Event #1 -X-APPLE-CALENDAR-COLOR:#affffeFF -X-OUTLOOK-COLOR:#affffeFF -X-FUNAMBOL-COLOR:#affffeFF -DESCRIPTION:Lorem Ipsum -DTSTAMP:20181201T011204Z -DTSTART:20181201T011204Z -DTEND:20181201T013024Z -END:VEVENT -BEGIN:VEVENT -UID:randommduidd -SUMMARY:Event #2 -DESCRIPTION: -DTSTAMP:20181202T045844Z -DTSTART:20181202T045844Z -DTEND:20181202T081844Z -END:VEVENT -BEGIN:VEVENT -UID:20181202T0600242aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83 -SUMMARY:Event #3 with empty uid -DESCRIPTION: -DTSTAMP:20181202T050024Z -DTSTART:20181202T050024Z -DTEND:20181202T050320Z -END:VEVENT -END:VCALENDAR`, - }, - { - name: "Test caldavparsing with reminders", - args: args{ - config: &Config{ - Name: "test2", - ProdID: "RandomProdID which is not random", - }, - events: []*Event{ - { - Summary: "Event #1", - Description: "Lorem Ipsum", - UID: "randommduid", - Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()), - Start: time.Unix(1543626724, 0).In(config.GetTimeZone()), - End: time.Unix(1543627824, 0).In(config.GetTimeZone()), - Alarms: []Alarm{ - {Time: time.Unix(1543626524, 0).In(config.GetTimeZone())}, - {Time: time.Unix(1543626224, 0).In(config.GetTimeZone())}, - {Time: time.Unix(1543626024, 0)}, - }, - }, - { - Summary: "Event #2", - UID: "randommduidd", - Timestamp: time.Unix(1543726724, 0).In(config.GetTimeZone()), - Start: time.Unix(1543726724, 0).In(config.GetTimeZone()), - End: time.Unix(1543738724, 0).In(config.GetTimeZone()), - Alarms: []Alarm{ - {Time: time.Unix(1543626524, 0).In(config.GetTimeZone())}, - {Time: time.Unix(1543626224, 0).In(config.GetTimeZone())}, - {Time: time.Unix(1543626024, 0).In(config.GetTimeZone())}, - }, - }, - { - Summary: "Event #3 with empty uid", - Timestamp: time.Unix(1543726824, 0).In(config.GetTimeZone()), - Start: time.Unix(1543726824, 0).In(config.GetTimeZone()), - End: time.Unix(1543727000, 0).In(config.GetTimeZone()), - Alarms: []Alarm{ - {Time: time.Unix(1543626524, 0).In(config.GetTimeZone())}, - {Time: time.Unix(1543626224, 0).In(config.GetTimeZone())}, - {Time: time.Unix(1543626024, 0).In(config.GetTimeZone())}, - {Time: time.Unix(1543826824, 0).In(config.GetTimeZone())}, - }, - }, - { - Summary: "Event #4 without any", - Timestamp: time.Unix(1543726824, 0), - Start: time.Unix(1543726824, 0), - End: time.Unix(1543727000, 0), - }, - }, - }, - wantCaldavevents: `BEGIN:VCALENDAR -VERSION:2.0 -METHOD:PUBLISH -X-PUBLISHED-TTL:PT4H -X-WR-CALNAME:test2 -PRODID:-//RandomProdID which is not random//EN -BEGIN:VEVENT -UID:randommduid -SUMMARY:Event #1 -DESCRIPTION:Lorem Ipsum -DTSTAMP:20181201T011204Z -DTSTART:20181201T011204Z -DTEND:20181201T013024Z -BEGIN:VALARM -TRIGGER:-PT3M20S -ACTION:DISPLAY -DESCRIPTION:Event #1 -END:VALARM -BEGIN:VALARM -TRIGGER:-PT8M20S -ACTION:DISPLAY -DESCRIPTION:Event #1 -END:VALARM -BEGIN:VALARM -TRIGGER:-PT11M40S -ACTION:DISPLAY -DESCRIPTION:Event #1 -END:VALARM -END:VEVENT -BEGIN:VEVENT -UID:randommduidd -SUMMARY:Event #2 -DESCRIPTION: -DTSTAMP:20181202T045844Z -DTSTART:20181202T045844Z -DTEND:20181202T081844Z -BEGIN:VALARM -TRIGGER:-PT27H50M0S -ACTION:DISPLAY -DESCRIPTION:Event #2 -END:VALARM -BEGIN:VALARM -TRIGGER:-PT27H55M0S -ACTION:DISPLAY -DESCRIPTION:Event #2 -END:VALARM -BEGIN:VALARM -TRIGGER:-PT27H58M20S -ACTION:DISPLAY -DESCRIPTION:Event #2 -END:VALARM -END:VEVENT -BEGIN:VEVENT -UID:20181202T050024Z2aaef4a81d770c1e775e26bc5abebc87f1d3d7bffaa83 -SUMMARY:Event #3 with empty uid -DESCRIPTION: -DTSTAMP:20181202T050024Z -DTSTART:20181202T050024Z -DTEND:20181202T050320Z -BEGIN:VALARM -TRIGGER:-PT27H51M40S -ACTION:DISPLAY -DESCRIPTION:Event #3 with empty uid -END:VALARM -BEGIN:VALARM -TRIGGER:-PT27H56M40S -ACTION:DISPLAY -DESCRIPTION:Event #3 with empty uid -END:VALARM -BEGIN:VALARM -TRIGGER:-PT28H0M0S -ACTION:DISPLAY -DESCRIPTION:Event #3 with empty uid -END:VALARM -BEGIN:VALARM -TRIGGER:PT27H46M40S -ACTION:DISPLAY -DESCRIPTION:Event #3 with empty uid -END:VALARM -END:VEVENT -BEGIN:VEVENT -UID:20181202T050024Zae7548ce9556df85038abe90dc674d4741a61ce74d1cf -SUMMARY:Event #4 without any -DESCRIPTION: -DTSTAMP:20181202T050024Z -DTSTART:20181202T050024Z -DTEND:20181202T050320Z -END:VEVENT -END:VCALENDAR`, - }, - { - name: "Test caldavparsing with multiline description", - args: args{ - config: &Config{ - Name: "test", - ProdID: "RandomProdID which is not random", - }, - events: []*Event{ - { - Summary: "Event #1", - Description: `Lorem Ipsum -Dolor sit amet`, - UID: "randommduid", - Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()), - Start: time.Unix(1543626724, 0).In(config.GetTimeZone()), - End: time.Unix(1543627824, 0).In(config.GetTimeZone()), - }, - }, - }, - wantCaldavevents: `BEGIN:VCALENDAR -VERSION:2.0 -METHOD:PUBLISH -X-PUBLISHED-TTL:PT4H -X-WR-CALNAME:test -PRODID:-//RandomProdID which is not random//EN -BEGIN:VEVENT -UID:randommduid -SUMMARY:Event #1 -DESCRIPTION:Lorem Ipsum\nDolor sit amet -DTSTAMP:20181201T011204Z -DTSTART:20181201T011204Z -DTEND:20181201T013024Z -END:VEVENT -END:VCALENDAR`, - }, - } - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - gotCaldavevents := ParseEvents(tt.args.config, tt.args.events) - assert.Equal(t, gotCaldavevents, tt.wantCaldavevents) - }) - } -} - func TestParseTodos(t *testing.T) { type args struct { config *Config @@ -531,15 +262,15 @@ END:VCALENDAR`, }, todos: []*Todo{ { - Summary: "Todo #1", - UID: "randommduid", - Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()), + Summary: "Todo #1", + UID: "randommduid", + Timestamp: time.Unix(1543626724, 0).In(config.GetTimeZone()), Alarms: []Alarm{ { Time: time.Unix(1543626724, 0).In(config.GetTimeZone()), }, { - Time: time.Unix(1543626724, 0).In(config.GetTimeZone()), + Time: time.Unix(1543626724, 0).In(config.GetTimeZone()), Description: "alarm description", }, }, diff --git a/pkg/caldav/parsing.go b/pkg/caldav/parsing.go index 3ab4a578e..e1fb0c289 100644 --- a/pkg/caldav/parsing.go +++ b/pkg/caldav/parsing.go @@ -155,34 +155,37 @@ func parseVAlarm(vAlarm *ics.VAlarm, vTask *models.Task) { if len(property.ICalParameters["VALUE"]) > 0 { switch property.ICalParameters["VALUE"][0] { case "DATE-TIME": - // TRIGGER;VALUE=DATE-TIME:20181201T011210Z + // Example: TRIGGER;VALUE=DATE-TIME:20181201T011210Z vTask.Reminders = append(vTask.Reminders, caldavTimeToTimestamp(property.Value)) } - } else if len(property.ICalParameters["RELATED"]) > 0 { - duration := parseDuration(property.Value) - switch property.ICalParameters["RELATED"][0] { - case "START": - // TRIGGER;RELATED=START:-P2D - if !vTask.StartDate.IsZero() { - vTask.Reminders = append(vTask.Reminders, vTask.StartDate.Add(duration)) - } - case "END": - // TRIGGER;RELATED=END:-P2D - if !vTask.EndDate.IsZero() { - vTask.Reminders = append(vTask.Reminders, vTask.EndDate.Add(duration)) - } else if !vTask.DueDate.IsZero() { - vTask.Reminders = append(vTask.Reminders, vTask.DueDate.Add(duration)) - } - } - } else { - duration := parseDuration(property.Value) - if duration != 0 { - // TRIGGER:-PT60M - if !vTask.DueDate.IsZero() { - vTask.Reminders = append(vTask.Reminders, vTask.DueDate.Add(duration)) - } - } - + // At the moment I don't think this should be merged. Relative triggers cannot + // be stored properly. This would result in missing or duplicate alarms on the + // client. + // + // } else if len(property.ICalParameters["RELATED"]) > 0 { + // duration := parseDuration(property.Value) + // switch property.ICalParameters["RELATED"][0] { + // case "START": + // // Example: TRIGGER;RELATED=START:-P2D + // if !vTask.StartDate.IsZero() { + // vTask.Reminders = append(vTask.Reminders, vTask.StartDate.Add(duration)) + // } + // case "END": + // // Example: TRIGGER;RELATED=END:-P2D + // if !vTask.EndDate.IsZero() { + // vTask.Reminders = append(vTask.Reminders, vTask.EndDate.Add(duration)) + // } else if !vTask.DueDate.IsZero() { + // vTask.Reminders = append(vTask.Reminders, vTask.DueDate.Add(duration)) + // } + // } + // } else { + // duration := parseDuration(property.Value) + // if duration != 0 { + // // Example: TRIGGER:-PT60M + // if !vTask.DueDate.IsZero() { + // vTask.Reminders = append(vTask.Reminders, vTask.DueDate.Add(duration)) + // } + // } } } } @@ -212,7 +215,7 @@ func caldavTimeToTimestamp(tstring string) time.Time { return t } -// From https://stackoverflow.com/a/57617885; with support for negative durations added +// TODO: move to utils/time.go and share with ticktick.go 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 diff --git a/pkg/caldav/parsing_test.go b/pkg/caldav/parsing_test.go index c77f01fc1..1755e6ea0 100644 --- a/pkg/caldav/parsing_test.go +++ b/pkg/caldav/parsing_test.go @@ -119,7 +119,7 @@ END:VCALENDAR`, }, }, { - name: "With alarm", + name: "With alarm (time trigger)", args: args{content: `BEGIN:VCALENDAR VERSION:2.0 METHOD:PUBLISH @@ -131,24 +131,10 @@ UID:randomuid DTSTAMP:20181201T011204 SUMMARY:Todo #1 DESCRIPTION:Lorem Ipsum -DTSTART:20230228T170000Z -DUE:20230304T150000Z BEGIN:VALARM TRIGGER;VALUE=DATE-TIME:20181201T011210Z ACTION:DISPLAY END:VALARM -BEGIN:VALARM -TRIGGER:-PT60M -ACTION:DISPLAY -END:VALARM -BEGIN:VALARM -TRIGGER;RELATED=START:-P1D -ACTION:DISPLAY -END:VALARM -BEGIN:VALARM -TRIGGER;RELATED=END:-PT30M -ACTION:DISPLAY -END:VALARM END:VTODO END:VCALENDAR`, }, @@ -156,17 +142,56 @@ END:VCALENDAR`, Title: "Todo #1", UID: "randomuid", Description: "Lorem Ipsum", - StartDate: time.Date(2023, 2, 28, 17, 0, 0, 0, config.GetTimeZone()), - DueDate: time.Date(2023, 3, 4, 15, 0, 0, 0, config.GetTimeZone()), Reminders: []time.Time{ time.Date(2018, 12, 1, 1, 12, 10, 0, config.GetTimeZone()), - time.Date(2023, 3, 4, 14, 0, 0, 0, config.GetTimeZone()), - time.Date(2023, 2, 27, 17, 00, 0, 0, config.GetTimeZone()), - time.Date(2023, 3, 4, 14, 30, 0, 0, config.GetTimeZone()), }, Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()), }, }, +// { +// name: "With alarm (relative trigger)", +// 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 +// DTSTART:20230228T170000Z +// DUE:20230304T150000Z +// BEGIN:VALARM +// TRIGGER:-PT60M +// ACTION:DISPLAY +// END:VALARM +// BEGIN:VALARM +// TRIGGER;RELATED=START:-P1D +// ACTION:DISPLAY +// END:VALARM +// BEGIN:VALARM +// TRIGGER;RELATED=END:-PT30M +// ACTION:DISPLAY +// END:VALARM +// END:VTODO +// END:VCALENDAR`, +// }, +// wantVTask: &models.Task{ +// Title: "Todo #1", +// UID: "randomuid", +// Description: "Lorem Ipsum", +// StartDate: time.Date(2023, 2, 28, 17, 0, 0, 0, config.GetTimeZone()), +// DueDate: time.Date(2023, 3, 4, 15, 0, 0, 0, config.GetTimeZone()), +// Reminders: []time.Time{ +// time.Date(2023, 3, 4, 14, 0, 0, 0, config.GetTimeZone()), +// time.Date(2023, 2, 27, 17, 00, 0, 0, config.GetTimeZone()), +// time.Date(2023, 3, 4, 14, 30, 0, 0, config.GetTimeZone()), +// }, +// Updated: time.Unix(1543626724, 0).In(config.GetTimeZone()), +// }, +// }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) {