From 26921fc67a33f1ec1993881e2c5a9962d32861de Mon Sep 17 00:00:00 2001 From: Matias Bordese Date: Tue, 1 Aug 2023 12:32:51 -0300 Subject: [PATCH] Fix final_events datetime filtering when splitting override (#2715) --- CHANGELOG.md | 6 ++ .../apps/schedules/models/on_call_schedule.py | 4 +- .../schedules/tests/test_on_call_schedule.py | 82 +++++++++++++++++++ 3 files changed, 91 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e8b04c0d..e660e716 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,12 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## Unreleased + +### Fixed + +- Fix schedule final_events datetime filtering when splitting override ([#2715](https://github.com/grafana/oncall/pull/2715)) + ## v1.3.21 (2023-08-01) ### Added diff --git a/engine/apps/schedules/models/on_call_schedule.py b/engine/apps/schedules/models/on_call_schedule.py index 7ee10855..28c0dbc3 100644 --- a/engine/apps/schedules/models/on_call_schedule.py +++ b/engine/apps/schedules/models/on_call_schedule.py @@ -789,7 +789,9 @@ class OnCallSchedule(PolymorphicModel): if current_interval_idx >= len(intervals): # event outside scheduled intervals, add to resolved - resolved.append(ev) + # only if still starts before datetime_end + if ev["start"] < datetime_end: + resolved.append(ev) elif ev["start"] < intervals[current_interval_idx][0] and ev["end"] <= intervals[current_interval_idx][0]: # event starts and ends outside an already scheduled interval, add to resolved diff --git a/engine/apps/schedules/tests/test_on_call_schedule.py b/engine/apps/schedules/tests/test_on_call_schedule.py index 1e56761b..beb707d8 100644 --- a/engine/apps/schedules/tests/test_on_call_schedule.py +++ b/engine/apps/schedules/tests/test_on_call_schedule.py @@ -524,6 +524,88 @@ def test_final_schedule_override_no_priority_shift( assert returned_events == expected_events +@pytest.mark.django_db +def test_final_schedule_override_split( + make_organization, make_user_for_organization, make_on_call_shift, make_schedule +): + organization = make_organization() + schedule = make_schedule( + organization, + schedule_class=OnCallScheduleWeb, + name="test_web_schedule", + ) + + now = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) + start_date = now - timezone.timedelta(days=7) + + user_a, user_b = (make_user_for_organization(organization, username=i) for i in "AB") + # clear users pks <-> organization cache (persisting between tests) + memoized_users_in_ical.cache_clear() + + shifts = ( + # user, priority, start time (h), duration (hs) + (user_a, 0, 10, 5), # 10-15 / A + ) + for user, priority, start_h, duration in shifts: + data = { + "start": start_date + timezone.timedelta(hours=start_h), + "rotation_start": start_date + timezone.timedelta(hours=start_h), + "duration": timezone.timedelta(hours=duration), + "priority_level": priority, + "frequency": CustomOnCallShift.FREQUENCY_DAILY, + "schedule": schedule, + } + on_call_shift = make_on_call_shift( + organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data + ) + on_call_shift.add_rolling_users([[user]]) + + # override: 10-14 / B + override_start = start_date + timezone.timedelta(hours=10) + override_data = { + "start": override_start, + "rotation_start": override_start, + "duration": timezone.timedelta(hours=4), + "schedule": schedule, + } + override = make_on_call_shift( + organization=organization, shift_type=CustomOnCallShift.TYPE_OVERRIDE, **override_data + ) + override.add_rolling_users([[user_b]]) + + override_started = override_start + timezone.timedelta(seconds=1) + returned_events = schedule.final_events(override_started, override_started) + + expected = ( + # start (h), duration (H), user, priority, is_override + (10, 4, "B", None, True), # 10-14 B + ) + expected_events = [ + { + "calendar_type": 1 if is_override else 0, + "end": start_date + timezone.timedelta(hours=start + duration), + "is_override": is_override, + "priority_level": priority, + "start": start_date + timezone.timedelta(hours=start, milliseconds=1 if start == 0 else 0), + "user": user, + } + for start, duration, user, priority, is_override in expected + ] + returned_events = [ + { + "calendar_type": e["calendar_type"], + "end": e["end"], + "is_override": e["is_override"], + "priority_level": e["priority_level"], + "start": e["start"], + "user": e["users"][0]["display_name"] if e["users"] else None, + } + for e in returned_events + if not e["is_gap"] + ] + assert returned_events == expected_events + + @pytest.mark.django_db def test_final_schedule_splitting_events( make_organization, make_user_for_organization, make_on_call_shift, make_schedule