From b829a99630a3e4757c7217621e26d33d0843c080 Mon Sep 17 00:00:00 2001 From: Huon Imberger Date: Tue, 18 May 2021 09:57:21 +1000 Subject: [PATCH 1/3] Add keep_recurrence_attributes arg --- README.rst | 4 ++- recurring_ical_events.py | 27 +++++++++++-------- test/test_keep_recurrence_attributes.py | 36 +++++++++++++++++++++++++ 3 files changed, 55 insertions(+), 12 deletions(-) create mode 100644 test/test_keep_recurrence_attributes.py diff --git a/README.rst b/README.rst index 7c22fd4..c9e04da 100644 --- a/README.rst +++ b/README.rst @@ -141,7 +141,9 @@ However, these attributes may differ from the source event: * DTSTART which is the start of the event instance. * DTEND which is the end of the event instance. -* RDATE, EXDATE, RRULE are the rules to create event repetitions. They are **not** included in repeated events, see `Issue 23 `_. +* RDATE, EXDATE, RRULE are the rules to create event repetitions. + They are **not** included in repeated events, see `Issue 23 `_. + To change this, use ``of(calendar, keep_recurrence_attributes=True)``. Development ----------- diff --git a/recurring_ical_events.py b/recurring_ical_events.py index e824eac..0eb28da 100644 --- a/recurring_ical_events.py +++ b/recurring_ical_events.py @@ -109,18 +109,20 @@ class Repetition: "RRULE", "RDATE", "EXDATE" ] - def __init__(self, source, start, stop): + def __init__(self, source, start, stop, keep_recurrence_attributes=False): self.source = source self.start = start self.stop = stop + self.keep_recurrence_attributes = keep_recurrence_attributes def as_vevent(self): revent = self.source.copy() revent["DTSTART"] = vDDDTypes(self.start) revent["DTEND"] = vDDDTypes(self.stop) - for attribute in self.ATTRIBUTES_TO_DELETE_ON_COPY: - if attribute in revent: - del revent[attribute] + if not self.keep_recurrence_attributes: + for attribute in self.ATTRIBUTES_TO_DELETE_ON_COPY: + if attribute in revent: + del revent[attribute] for subcomponent in self.source.subcomponents: revent.add_component(subcomponent) return revent @@ -131,10 +133,11 @@ def is_in_span(self, span_start, span_stop): def __repr__(self): return "{}({{'UID':{}...}}, {}, {})".format(self.__class__.__name__, self.source.get("UID"), self.start, self.stop) - def __init__(self, event): + def __init__(self, event, keep_recurrence_attributes=False): self.event = event self.start = self.original_start = event["DTSTART"].dt self.end = self.original_end = self._get_event_end() + self.keep_recurrence_attributes = keep_recurrence_attributes self.exdates = [] self.exdates_utc = set() exdates = event.get("EXDATE", []) @@ -242,8 +245,10 @@ def within_days(self, span_start, span_stop): yield self.Repetition( self.event, self.convert_to_original_type(start), - self.convert_to_original_type(stop)) - + self.convert_to_original_type(stop), + self.keep_recurrence_attributes, + ) + def convert_to_original_type(self, date): if not isinstance(self.original_start, datetime.datetime) and \ not isinstance(self.original_end, datetime.datetime): @@ -260,14 +265,14 @@ def _get_event_end(self): return self.event["DTSTART"].dt - def __init__(self, calendar): + def __init__(self, calendar, keep_recurrence_attributes=False): """Create an unfoldable calendar from a given calendar.""" assert calendar.get("CALSCALE", "GREGORIAN") == "GREGORIAN", "Only Gregorian calendars are supported." # https://www.kanzaki.com/docs/ical/calscale.html self.repetitions = [] for event in calendar.walk(): if not is_event(event): continue - self.repetitions.append(self.RepeatedEvent(event)) + self.repetitions.append(self.RepeatedEvent(event, keep_recurrence_attributes)) @staticmethod @@ -369,7 +374,7 @@ def add_event(event): add_event(repetition.as_vevent()) return events -def of(a_calendar): +def of(a_calendar, keep_recurrence_attributes=False): """Unfold recurring events of a_calendar""" - return UnfoldableCalendar(a_calendar) + return UnfoldableCalendar(a_calendar, keep_recurrence_attributes) diff --git a/test/test_keep_recurrence_attributes.py b/test/test_keep_recurrence_attributes.py new file mode 100644 index 0000000..22e61f3 --- /dev/null +++ b/test/test_keep_recurrence_attributes.py @@ -0,0 +1,36 @@ +"""This file tests the `keep_recurrence_attributes` argument of `of` works as expected.""" +import os + +from datetime import datetime +from recurring_ical_events import of +from icalendar import Calendar + +HERE = os.path.dirname(__name__) or "test" +CALENDARS_FOLDER = os.path.join(HERE, "calendars") +calendar_path = os.path.join(CALENDARS_FOLDER, "one-day-event-repeat-every-day.ics") + + +def test_keep_recurrence_attributes_default(calendars): + with open(calendar_path, "rb") as file: + content = file.read() + + calendar = Calendar.from_ical(content) + today = datetime.today() + one_year_ahead = today.replace(year=today.year + 1) + + events = of(calendar).between(today, one_year_ahead) + for event in events: + assert event.get("RRULE", False) is False + + +def test_keep_recurrence_attributes_true(calendars): + with open(calendar_path, "rb") as file: + content = file.read() + + calendar = Calendar.from_ical(content) + today = datetime.today() + one_year_ahead = today.replace(year=today.year + 1) + + events = of(calendar, keep_recurrence_attributes=True).between(today, one_year_ahead) + for event in events: + assert event.get("RRULE", False) is not False From c6051435a1784abe7ae3ddc4cf7fca6119816809 Mon Sep 17 00:00:00 2001 From: Huon Imberger Date: Tue, 18 May 2021 10:06:59 +1000 Subject: [PATCH 2/3] Add specific false test --- test/test_keep_recurrence_attributes.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/test/test_keep_recurrence_attributes.py b/test/test_keep_recurrence_attributes.py index 22e61f3..d03a163 100644 --- a/test/test_keep_recurrence_attributes.py +++ b/test/test_keep_recurrence_attributes.py @@ -23,6 +23,19 @@ def test_keep_recurrence_attributes_default(calendars): assert event.get("RRULE", False) is False +def test_keep_recurrence_attributes_false(calendars): + with open(calendar_path, "rb") as file: + content = file.read() + + calendar = Calendar.from_ical(content) + today = datetime.today() + one_year_ahead = today.replace(year=today.year + 1) + + events = of(calendar, keep_recurrence_attributes=False).between(today, one_year_ahead) + for event in events: + assert event.get("RRULE", False) is False + + def test_keep_recurrence_attributes_true(calendars): with open(calendar_path, "rb") as file: content = file.read() From 17452e2507c79835b56a8d279fdd85cedfcab588 Mon Sep 17 00:00:00 2001 From: Huon Imberger Date: Tue, 18 May 2021 10:08:54 +1000 Subject: [PATCH 3/3] Include all attrs in test --- test/test_keep_recurrence_attributes.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/test/test_keep_recurrence_attributes.py b/test/test_keep_recurrence_attributes.py index d03a163..2f4ec63 100644 --- a/test/test_keep_recurrence_attributes.py +++ b/test/test_keep_recurrence_attributes.py @@ -7,7 +7,7 @@ HERE = os.path.dirname(__name__) or "test" CALENDARS_FOLDER = os.path.join(HERE, "calendars") -calendar_path = os.path.join(CALENDARS_FOLDER, "one-day-event-repeat-every-day.ics") +calendar_path = os.path.join(CALENDARS_FOLDER, "rdate.ics") def test_keep_recurrence_attributes_default(calendars): @@ -21,6 +21,8 @@ def test_keep_recurrence_attributes_default(calendars): events = of(calendar).between(today, one_year_ahead) for event in events: assert event.get("RRULE", False) is False + assert event.get("RDATE", False) is False + assert event.get("EXDATE", False) is False def test_keep_recurrence_attributes_false(calendars): @@ -34,6 +36,8 @@ def test_keep_recurrence_attributes_false(calendars): events = of(calendar, keep_recurrence_attributes=False).between(today, one_year_ahead) for event in events: assert event.get("RRULE", False) is False + assert event.get("RDATE", False) is False + assert event.get("EXDATE", False) is False def test_keep_recurrence_attributes_true(calendars): @@ -47,3 +51,5 @@ def test_keep_recurrence_attributes_true(calendars): events = of(calendar, keep_recurrence_attributes=True).between(today, one_year_ahead) for event in events: assert event.get("RRULE", False) is not False + assert event.get("RDATE", False) is not False + assert event.get("EXDATE", False) is not False