Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ability to keep recurrence attributes on event copies #54

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -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 <https://github.com/niccokunzmann/python-recurring-ical-events/issues/23>`_.
* RDATE, EXDATE, RRULE are the rules to create event repetitions.
They are **not** included in repeated events, see `Issue 23 <https://github.com/niccokunzmann/python-recurring-ical-events/issues/23>`_.
To change this, use ``of(calendar, keep_recurrence_attributes=True)``.

Development
-----------
Expand Down
27 changes: 16 additions & 11 deletions recurring_ical_events.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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", [])
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand Down Expand Up @@ -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)

55 changes: 55 additions & 0 deletions test/test_keep_recurrence_attributes.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
"""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, "rdate.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
assert event.get("RDATE", False) is False
assert event.get("EXDATE", 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
assert event.get("RDATE", False) is False
assert event.get("EXDATE", 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
assert event.get("RDATE", False) is not False
assert event.get("EXDATE", False) is not False