Merge pull request #295 from grafana/matiasb-schedule-next-shifts-per-user

Add schedule next shifts per user endpoint to internal API
This commit is contained in:
Matias Bordese 2022-07-29 16:29:23 -03:00 committed by GitHub
commit 8fa547ee11
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 97 additions and 0 deletions

View file

@ -432,6 +432,7 @@ def test_events_calendar(
"calendar_type": OnCallSchedule.PRIMARY,
"is_empty": False,
"is_gap": False,
"is_override": False,
"shift": {
"pk": on_call_shift.public_primary_key,
},
@ -497,6 +498,7 @@ def test_filter_events_calendar(
"calendar_type": OnCallSchedule.PRIMARY,
"is_empty": False,
"is_gap": False,
"is_override": False,
"shift": {
"pk": on_call_shift.public_primary_key,
},
@ -512,6 +514,7 @@ def test_filter_events_calendar(
"calendar_type": OnCallSchedule.PRIMARY,
"is_empty": False,
"is_gap": False,
"is_override": False,
"shift": {
"pk": on_call_shift.public_primary_key,
},
@ -594,6 +597,7 @@ def test_filter_events_range_calendar(
"calendar_type": OnCallSchedule.PRIMARY,
"is_empty": False,
"is_gap": False,
"is_override": False,
"shift": {
"pk": on_call_shift.public_primary_key,
},
@ -675,6 +679,7 @@ def test_filter_events_overrides(
"calendar_type": OnCallSchedule.OVERRIDES,
"is_empty": False,
"is_gap": False,
"is_override": True,
"shift": {
"pk": override.public_primary_key,
},
@ -772,6 +777,7 @@ def test_filter_events_final_schedule(
"calendar_type": 1 if is_override else None if is_gap else 0,
"end": start_date + timezone.timedelta(hours=start + duration),
"is_gap": is_gap,
"is_override": is_override,
"priority_level": priority,
"start": start_date + timezone.timedelta(hours=start, milliseconds=1 if start == 0 else 0),
"user": user,
@ -783,6 +789,7 @@ def test_filter_events_final_schedule(
"calendar_type": e["calendar_type"],
"end": e["end"],
"is_gap": e["is_gap"],
"is_override": e["is_override"],
"priority_level": e["priority_level"],
"start": e["start"],
"user": e["users"][0]["display_name"] if e["users"] else None,
@ -792,6 +799,75 @@ def test_filter_events_final_schedule(
assert returned_events == expected_events
@pytest.mark.django_db
def test_next_shifts_per_user(
make_organization_and_user_with_plugin_token,
make_user_for_organization,
make_user_auth_headers,
make_schedule,
make_on_call_shift,
):
organization, user, token = make_organization_and_user_with_plugin_token()
client = APIClient()
schedule = make_schedule(
organization,
schedule_class=OnCallScheduleWeb,
name="test_web_schedule",
)
tomorrow = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0) + timezone.timedelta(days=1)
user_a, user_b, user_c = (make_user_for_organization(organization, username=i) for i in "ABC")
shifts = (
# user, priority, start time (h), duration (hs)
(user_a, 1, 8, 2), # r1-1: 8-10 / A
(user_a, 1, 15, 2), # r1-2: 15-17 / A
(user_b, 2, 7, 5), # r2-1: 7-12 / B
(user_b, 2, 16, 2), # r2-2: 16-18 / B
(user_c, 2, 18, 2), # r2-3: 18-20 / C
)
for user, priority, start_h, duration in shifts:
data = {
"start": tomorrow + timezone.timedelta(hours=start_h),
"rotation_start": tomorrow,
"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_RECURRENT_EVENT, **data
)
on_call_shift.users.add(user)
# override: 17-18 / C
override_data = {
"start": tomorrow + timezone.timedelta(hours=17),
"rotation_start": tomorrow,
"duration": timezone.timedelta(hours=1),
"schedule": schedule,
}
override = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_OVERRIDE, **override_data
)
override.add_rolling_users([[user_c]])
# final schedule: 7-12: B, 15-16: A, 16-17: B, 17-18: C (override), 18-20: C
url = reverse("api-internal:schedule-next-shifts-per-user", kwargs={"pk": schedule.public_primary_key})
response = client.get(url, format="json", **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_200_OK
expected = {
user_a.public_primary_key: (tomorrow + timezone.timedelta(hours=15), tomorrow + timezone.timedelta(hours=16)),
user_b.public_primary_key: (tomorrow + timezone.timedelta(hours=7), tomorrow + timezone.timedelta(hours=12)),
user_c.public_primary_key: (tomorrow + timezone.timedelta(hours=17), tomorrow + timezone.timedelta(hours=18)),
}
returned_data = {u: (ev["start"], ev["end"]) for u, ev in response.data["users"].items()}
assert returned_data == expected
@pytest.mark.django_db
def test_filter_events_invalid_type(
make_organization_and_user_with_plugin_token,

View file

@ -58,6 +58,7 @@ class ScheduleView(
*READ_ACTIONS,
"events",
"filter_events",
"next_shifts_per_user",
"notify_empty_oncall_options",
"notify_oncall_shift_freq_options",
"mention_options",
@ -222,6 +223,7 @@ class ScheduleView(
"calendar_type": shift["calendar_type"],
"is_empty": len(shift["users"]) == 0 and not is_gap,
"is_gap": is_gap,
"is_override": shift["calendar_type"] == OnCallSchedule.TYPE_ICAL_OVERRIDES,
"shift": {
"pk": shift["shift_pk"],
},
@ -395,6 +397,25 @@ class ScheduleView(
resolved.sort(key=lambda e: e["start"])
return resolved
@action(detail=True, methods=["get"])
def next_shifts_per_user(self, request, pk):
"""Return next shift for users in schedule."""
user_tz, _ = self.get_request_timezone()
now = timezone.now()
starting_date = now.date()
schedule = self.original_get_object()
shift_events = self._filter_events(schedule, user_tz, starting_date, days=30, with_empty=False, with_gap=False)
events = self._resolve_schedule(shift_events)
users = {}
for e in events:
user = e["users"][0]["pk"] if e["users"] else None
if user is not None and user not in users and e["end"] > now:
users[user] = e
result = {"users": users}
return Response(result, status=status.HTTP_200_OK)
@action(detail=False, methods=["get"])
def type_options(self, request):
# TODO: check if it needed