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

Don't Reset next_occurrence For Cloned Objects #108

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
6 changes: 5 additions & 1 deletion ambition_utils/rrule/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -351,9 +351,11 @@ def set_date_objects(self):
"""
Ensure that all the date keys are properly set on all rrule params
"""
# A cloned object will not have a primary key but does have a next_occurrence that should not be reset
# to the first date in a series.
is_new = self.pk is None and self.last_occurrence is None

# Convert the rrule and exclusion rrule params to properly set date keys
is_new = self.pk is None
self.set_date_objects_for_params(self.rrule_params, is_new=is_new)
self.set_date_objects_for_params(self.rrule_exclusion_params, is_new=is_new)

Expand Down Expand Up @@ -470,11 +472,13 @@ def clone_with_day_offset(self, day_offset: int) -> RRule:
"""
Creates a clone of a passed RRule object with day_offset set.
clone() is not called to ensure .id is not set before .save() so offset is applied.
The clone's next_occurrence is set to the offset of this object.
:param day_offset: The number of days to offset the clone's start date. Can be negative.
"""
clone = copy.deepcopy(self)
clone.id = None
clone.day_offset = day_offset
clone.next_occurrence = clone.offset(clone.next_occurrence)
clone.save()
return clone

Expand Down
31 changes: 31 additions & 0 deletions ambition_utils/rrule/tests/model_tests.py
Original file line number Diff line number Diff line change
Expand Up @@ -1614,6 +1614,37 @@ def test_next_occurrences_day_offset(self):
self.assertEqual(rrule1.next_occurrence, datetime.datetime(2017, 1, 10))
self.assertEqual(rrule2.next_occurrence, datetime.datetime(2017, 1, 15))

def test_next_occurrence_clone_with_offset(self):
"""
Cloning an established object with an offset should yield a next_occurrence of the object's with the offset.
"""
# A daily recurrence with a multi-day offset to highlight differences clearly.
rrule1 = G(
RRule,
rrule_params={
'freq': rrule.DAILY,
'interval': 1,
'dtstart': datetime.datetime(2023, 1, 1),
},
occurrence_handler_path='ambition_utils.rrule.tests.model_tests.HandlerOne',
)

# Many days into the future
with freeze_time('1-10-2023'):
# Catch the recurrence object up.
while rrule1.next_occurrence <= datetime.datetime.now():
RRule.objects.handle_overdue()
rrule1.refresh_from_db()
self.assertEqual(rrule1.next_occurrence, datetime.datetime(2023, 1, 11))

# Create a clone with an offset and assert that the clone's next occurrence is 1/11 -5 = 1/6.
rrule2 = rrule1.clone_with_day_offset(day_offset=-5)
self.assertEqual(rrule2.next_occurrence, datetime.datetime(2023, 1, 6))

# First object is unchanged
rrule1.refresh_from_db()
self.assertEqual(rrule1.next_occurrence, datetime.datetime(2023, 1, 11))


class RRuleWithExclusionTest(TestCase):
def test_exclusion(self):
Expand Down
Loading