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

Append include_dtend parameter for allowing to include or not the dtend #237

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
18 changes: 14 additions & 4 deletions recurrence/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -291,10 +291,18 @@ class Recurrence:
recurrence in the set (according to the rfc2445 spec).
With `include_dtstart == False` `dtstart` is only the rule's
starting point like in python's `dateutil.rrule`.

`include_dtend` : bool
Defines if `dtend` is included in the recurrence set as
the last occurrence. With `include_dtend == True` it is
both the ending point for recurrences and the last
recurrence in the set (according to the rfc2445 spec).
With `include_dtend == False` `dtend` is only the rule's
ending point like in python's `dateutil.rrule`.
"""
def __init__(
self, dtstart=None, dtend=None, rrules=(), exrules=(),
rdates=(), exdates=(), include_dtstart=True
rdates=(), exdates=(), include_dtstart=True, include_dtend=True
):
"""
Create a new recurrence.
Expand All @@ -312,6 +320,7 @@ def __init__(
self.rdates = list(rdates)
self.exdates = list(exdates)
self.include_dtstart = include_dtstart
self.include_dtend = include_dtend

def __iter__(self):
return self.occurrences()
Expand Down Expand Up @@ -542,6 +551,7 @@ def to_dateutil_rruleset(self, dtstart=None, dtend=None, cache=False):
dtstart = dtstart or self.dtstart
dtend = dtend or self.dtend
include_dtstart = self.include_dtstart
include_dtend = self.include_dtend

if dtend:
dtend = normalize_offset_awareness(dtend or self.dtend, dtstart)
Expand All @@ -568,7 +578,7 @@ def to_dateutil_rruleset(self, dtstart=None, dtend=None, cache=False):
rruleset.rdate(rdate)
elif not dtend:
rruleset.rdate(rdate)
if dtend is not None:
if include_dtend and dtend is not None:
rruleset.rdate(dtend)

for exdate in self.exdates:
Expand Down Expand Up @@ -918,7 +928,7 @@ def serialize_rule(rule):
return u'\n'.join(u'%s:%s' % i for i in items)


def deserialize(text, include_dtstart=True):
def deserialize(text, include_dtstart=True, include_dtend=True):
"""
Deserialize a rfc2445 formatted string.

Expand Down Expand Up @@ -1084,7 +1094,7 @@ def deserialize_dt(text):
for item in param_text.split(','):
exdates.append(deserialize_dt(item))

return Recurrence(dtstart, dtend, rrules, exrules, rdates, exdates, include_dtstart)
return Recurrence(dtstart, dtend, rrules, exrules, rdates, exdates, include_dtstart, include_dtend)


def rule_to_text(rule, short=False):
Expand Down
5 changes: 3 additions & 2 deletions recurrence/fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@
class RecurrenceField(fields.Field):
"""Field that stores a `recurrence.base.Recurrence` to the database."""

def __init__(self, include_dtstart=True, **kwargs):
def __init__(self, include_dtstart=True, include_dtend=True, **kwargs):
self.include_dtstart = include_dtstart
self.include_dtend = include_dtend
super(RecurrenceField, self).__init__(**kwargs)

def get_internal_type(self):
Expand All @@ -20,7 +21,7 @@ def to_python(self, value):
if value is None or isinstance(value, recurrence.Recurrence):
return value
value = super(RecurrenceField, self).to_python(value) or u''
return recurrence.deserialize(value, self.include_dtstart)
return recurrence.deserialize(value, self.include_dtstart, self.include_dtend)

def from_db_value(self, value, *args, **kwargs):
return self.to_python(value)
Expand Down
62 changes: 62 additions & 0 deletions tests/test_fields.py
Original file line number Diff line number Diff line change
Expand Up @@ -218,6 +218,68 @@ def test_include_dtstart_from_object():
datetime(2015, 8, 3, 0, 0), datetime(2015, 8, 10, 0, 0)]


def test_include_dtend_from_field():
rule = Rule(
recurrence.WEEKLY,
byday=recurrence.MONDAY
)

limits = Recurrence(
rrules=[rule]
)

value = recurrence.serialize(limits)

model_field = recurrence.fields.RecurrenceField() # Test with include_dtend=True (default)
rec_obj = model_field.to_python(value)
assert rec_obj == limits
# 14th of August (dtend) is expected but only for inc=True
assert [occ for occ in rec_obj.to_dateutil_rruleset(datetime(2015, 8, 2), datetime(2015, 8, 14))] == [
datetime(2015, 8, 2, 0, 0), datetime(2015, 8, 3, 0, 0), datetime(2015, 8, 10, 0, 0), datetime(2015, 8, 14, 0, 0)
]

model_field = recurrence.fields.RecurrenceField(include_dtend=False) # Test with include_dtend=False
rec_obj = model_field.to_python(value)
assert rec_obj == limits
# 14th of August (dtend) is not expected regardless of inc
assert [occ for occ in rec_obj.to_dateutil_rruleset(datetime(2015, 8, 2), datetime(2015, 8, 14))] == [
datetime(2015, 8, 2, 0, 0), datetime(2015, 8, 3, 0, 0), datetime(2015, 8, 10, 0, 0)]


def test_include_dtend_from_object():
rule = Rule(
recurrence.WEEKLY,
byday=recurrence.MONDAY
)

limits = Recurrence( # include_dtend=True (default)
rrules=[rule]
)

assert [occ for occ in limits.to_dateutil_rruleset(datetime(2015, 8, 2), datetime(2015, 8, 14))] == [
datetime(2015, 8, 2, 0, 0), datetime(2015, 8, 3, 0, 0), datetime(2015, 8, 10, 0, 0),
datetime(2015, 8, 14, 0, 0)
]

limits = Recurrence( # include_dtend=False (dtend is expected to not be included)
include_dtend=False,
rrules=[rule]
)

assert [occ for occ in limits.to_dateutil_rruleset(datetime(2015, 8, 2), datetime(2015, 8, 14))] == [
datetime(2015, 8, 2, 0, 0), datetime(2015, 8, 3, 0, 0), datetime(2015, 8, 10, 0, 0)
]

limits = Recurrence( # include_dtend=False (dtend dtstart are expected to not be included)
include_dtstart=False,
include_dtend=False,
rrules=[rule]
)

assert [occ for occ in limits.to_dateutil_rruleset(datetime(2015, 8, 2), datetime(2015, 8, 14))] == [
datetime(2015, 8, 3, 0, 0), datetime(2015, 8, 10, 0, 0)
]

def test_none_fieldvalue():
field = RecurrenceField()
value = None
Expand Down