Skip to content

Commit

Permalink
Don't Reset next_occurrence For Cloned Objects (#108)
Browse files Browse the repository at this point in the history
Amend logic determining if object is new by considering last_occurrence so that cloned objects will not be treated as new and have their next_occurrence reset to the first date in the series. Expanded clone_with_day_offset to copy of the source object's next_occurrence, with offset, so that cloned object will not have to processed stale next_occurrence dates to 'catch up'. Added test to flex.
  • Loading branch information
tylerneu authored Feb 5, 2024
1 parent e4e9c38 commit 936cc00
Show file tree
Hide file tree
Showing 2 changed files with 36 additions and 1 deletion.
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

0 comments on commit 936cc00

Please sign in to comment.