From 7383f5bc5677db1db43b907a08bde3a81a748770 Mon Sep 17 00:00:00 2001 From: Eduard Itrich Date: Tue, 20 Dec 2022 20:19:11 +0100 Subject: [PATCH 1/4] Ignore new line at end of file (it breaks the custom ics parser) Signed-off-by: Eduard Itrich --- example/test_calendar.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/test_calendar.yaml b/example/test_calendar.yaml index fb751e5..4973ccd 100644 --- a/example/test_calendar.yaml +++ b/example/test_calendar.yaml @@ -58,5 +58,5 @@ events: # Event with custom RRULE - summary: Another day begin: 2021-04-22 - ics: | + ics: |- RRULE:FREQ=YEARLY;UNTIL=20280422T000000 From 9d78f7528bc27c4bed26500173feea92b9fa4b2a Mon Sep 17 00:00:00 2001 From: Eduard Itrich Date: Tue, 20 Dec 2022 20:19:25 +0100 Subject: [PATCH 2/4] Add possibility to add additional dates to recurring events Signed-off-by: Eduard Itrich --- example/test_calendar.yaml | 4 ++- tests/test_events.py | 5 +++- yaml2ics.py | 58 ++++++++++++++++++++++++-------------- 3 files changed, 44 insertions(+), 23 deletions(-) diff --git a/example/test_calendar.yaml b/example/test_calendar.yaml index 4973ccd..b12a5d4 100644 --- a/example/test_calendar.yaml +++ b/example/test_calendar.yaml @@ -33,7 +33,7 @@ events: weeks: 1 until: 2022-12-31 # required - - summary: Recurring event with exception + - summary: Recurring event with exception and additional date begin: 2022-07-01 10:00:00 duration: { minutes: 60 } repeat: @@ -44,6 +44,8 @@ events: except_on: - 2022-07-13 - 2022-07-14 06:00:00 + also_on: + - 2022-12-24 06:00:00 # All-day event - summary: Earth Day diff --git a/tests/test_events.py b/tests/test_events.py index 64d34ee..2f89f4d 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -69,7 +69,7 @@ def test_exception(): event = event_from_yaml( parse_yaml( """ - summary: Recurring event with exception + summary: Recurring event with exception and additional date timezone: America/Los_Angeles begin: 2022-07-01 10:00:00 duration: {minutes: 60} @@ -80,6 +80,8 @@ def test_exception(): except_on: - 2022-07-13 - 2022-07-14 06:00:00 + also_on: + - 2022-12-24 06:00:00 """ ) ) @@ -90,6 +92,7 @@ def test_exception(): "EXDATE;TZID=/ics.py/2020.1/America/Los_Angeles:20220713,20220714T060000" in event_str ) + assert "RDATE;TZID=/ics.py/2020.1/America/Los_Angeles:20221224T060000" in event_str def test_event_with_time_range(): diff --git a/yaml2ics.py b/yaml2ics.py index 6bea5bf..91e20e2 100644 --- a/yaml2ics.py +++ b/yaml2ics.py @@ -25,11 +25,34 @@ } -def strfexception(exdate): - if isinstance(exdate, datetime.datetime): - return datetime.datetime.strftime(exdate, "%Y%m%dT%H%M%S") - elif isinstance(exdate, datetime.date): - return datetime.datetime.strftime(exdate, "%Y%m%d") +def datetime2utc(date): + if isinstance(date, datetime.datetime): + return datetime.datetime.strftime(date, "%Y%m%dT%H%M%S") + elif isinstance(date, datetime.date): + return datetime.datetime.strftime(date, "%Y%m%d") + + +# See RFC2445, 4.8.5 REcurrence Component Properties +# This function can be used to add a list of e.g. exception dates (EXDATE) or recurrence dates (RDATE) +# to a reoccurring event +def add_recurrence_property( + event: ics.Event, property_name, dates: map, tz: datetime.tzinfo = None +): + if tz: + event.extra.append( + ics.ContentLine( + name=property_name, + params={"TZID": [str(ics.Timezone.from_tzinfo(tz))]}, + value=",".join(dates), + ) + ) + else: + event.extra.append( + ics.ContentLine( + name=property_name, + value=",".join(dates), + ) + ) def event_from_yaml(event_yaml: dict, tz: datetime.tzinfo = None) -> ics.Event: @@ -101,24 +124,17 @@ def event_from_yaml(event_yaml: dict, tz: datetime.tzinfo = None) -> ics.Event: if "except_on" in repeat: exdates = map( - lambda exdate: strfexception(exdate), + lambda exdate: datetime2utc(exdate), repeat["except_on"], ) - if tz: - event.extra.append( - ics.ContentLine( - name="EXDATE", - params={"TZID": [str(ics.Timezone.from_tzinfo(tz))]}, - value=",".join(exdates), - ) - ) - else: - event.extra.append( - ics.ContentLine( - name="EXDATE", - value=",".join(exdates), - ) - ) + add_recurrence_property(event, "EXDATE", exdates, tz) + + if "also_on" in repeat: + rdates = map( + lambda rdate: datetime2utc(rdate), + repeat["also_on"], + ) + add_recurrence_property(event, "RDATE", rdates, tz) event.dtstamp = datetime.datetime.utcnow().replace(tzinfo=dateutil.tz.UTC) if tz and event.floating and not event.all_day: From 286cd100c02f6ec34eba7814691955a05d79de19 Mon Sep 17 00:00:00 2001 From: Eduard Itrich Date: Tue, 31 Jan 2023 20:14:22 +0100 Subject: [PATCH 3/4] Incorporate feedback from @stefanv. Thank you! Signed-off-by: Eduard Itrich --- yaml2ics.py | 30 ++++++++---------------------- 1 file changed, 8 insertions(+), 22 deletions(-) diff --git a/yaml2ics.py b/yaml2ics.py index 91e20e2..a18e406 100644 --- a/yaml2ics.py +++ b/yaml2ics.py @@ -38,21 +38,13 @@ def datetime2utc(date): def add_recurrence_property( event: ics.Event, property_name, dates: map, tz: datetime.tzinfo = None ): - if tz: - event.extra.append( - ics.ContentLine( - name=property_name, - params={"TZID": [str(ics.Timezone.from_tzinfo(tz))]}, - value=",".join(dates), - ) - ) - else: - event.extra.append( - ics.ContentLine( - name=property_name, - value=",".join(dates), - ) + event.extra.append( + ics.ContentLine( + name=property_name, + params={"TZID": [str(ics.Timezone.from_tzinfo(tz))]} if tz else None, + value=",".join(dates), ) + ) def event_from_yaml(event_yaml: dict, tz: datetime.tzinfo = None) -> ics.Event: @@ -123,17 +115,11 @@ def event_from_yaml(event_yaml: dict, tz: datetime.tzinfo = None) -> ics.Event: ) if "except_on" in repeat: - exdates = map( - lambda exdate: datetime2utc(exdate), - repeat["except_on"], - ) + exdates = [datetime2utc(rdate) for rdate in repeat["except_on"]] add_recurrence_property(event, "EXDATE", exdates, tz) if "also_on" in repeat: - rdates = map( - lambda rdate: datetime2utc(rdate), - repeat["also_on"], - ) + rdates = [datetime2utc(rdate) for rdate in repeat["also_on"]] add_recurrence_property(event, "RDATE", rdates, tz) event.dtstamp = datetime.datetime.utcnow().replace(tzinfo=dateutil.tz.UTC) From 762b13b077c002f4f8bd854f105128031be0df95 Mon Sep 17 00:00:00 2001 From: Eduard Itrich Date: Tue, 31 Jan 2023 22:34:19 +0100 Subject: [PATCH 4/4] Remove YAML whitespace that is actually not needed Signed-off-by: Eduard Itrich --- example/test_calendar.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/example/test_calendar.yaml b/example/test_calendar.yaml index b12a5d4..d54824d 100644 --- a/example/test_calendar.yaml +++ b/example/test_calendar.yaml @@ -60,5 +60,5 @@ events: # Event with custom RRULE - summary: Another day begin: 2021-04-22 - ics: |- + ics: | RRULE:FREQ=YEARLY;UNTIL=20280422T000000