Merge remote-tracking branch 'origin/matiasb/fix-final-schedule-event-splitting' into new-schedules

This commit is contained in:
Maxim 2022-08-18 18:14:27 +03:00
commit ca3c81ed07
3 changed files with 117 additions and 22 deletions

View file

@ -1311,3 +1311,98 @@ def test_on_call_shift_preview(
if not e["is_override"] and not e["is_gap"]
]
assert returned_events == expected_events
@pytest.mark.django_db
def test_on_call_shift_preview_merge_events(
make_organization_and_user_with_plugin_token,
make_user_for_organization,
make_user_auth_headers,
make_schedule,
):
organization, user, token = make_organization_and_user_with_plugin_token()
client = APIClient()
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)
request_date = start_date
user = make_user_for_organization(organization)
other_user = make_user_for_organization(organization)
url = "{}?date={}&days={}".format(
reverse("api-internal:oncall_shifts-preview"), request_date.strftime("%Y-%m-%d"), 1
)
shift_start = (start_date + timezone.timedelta(hours=12)).strftime("%Y-%m-%dT%H:%M:%SZ")
shift_end = (start_date + timezone.timedelta(hours=13)).strftime("%Y-%m-%dT%H:%M:%SZ")
shift_data = {
"schedule": schedule.public_primary_key,
"type": CustomOnCallShift.TYPE_ROLLING_USERS_EVENT,
"rotation_start": shift_start,
"shift_start": shift_start,
"shift_end": shift_end,
"rolling_users": [[user.public_primary_key, other_user.public_primary_key]],
"priority_level": 2,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
}
response = client.post(url, shift_data, format="json", **make_user_auth_headers(user, token))
assert response.status_code == status.HTTP_200_OK
# check rotation events
rotation_events = response.json()["rotation"]
expected_rotation_events = [
{
"calendar_type": OnCallSchedule.TYPE_ICAL_PRIMARY,
"start": shift_start,
"end": shift_end,
"all_day": False,
"is_override": False,
"is_empty": False,
"is_gap": False,
"priority_level": 2,
"missing_users": [],
"source": "web",
}
]
expected_users = sorted([user.username, other_user.username])
returned_event = rotation_events[0]
# there isn't a saved shift, we don't care/know the temp pk
returned_event.pop("shift")
returned_users = sorted(u["display_name"] for u in returned_event.pop("users"))
assert sorted(returned_users) == expected_users
assert rotation_events == expected_rotation_events
# check final schedule events
final_events = response.json()["final"]
expected = (
# start (h), duration (H), users, priority
(12, 1, expected_users, 2), # 12-13 other_user
)
expected_events = [
{
"end": (start_date + timezone.timedelta(hours=start + duration)).strftime("%Y-%m-%dT%H:%M:%SZ"),
"priority_level": priority,
"start": (start_date + timezone.timedelta(hours=start, milliseconds=1 if start == 0 else 0)).strftime(
"%Y-%m-%dT%H:%M:%SZ"
),
"users": users,
}
for start, duration, users, priority in expected
]
returned_events = [
{
"end": e["end"],
"priority_level": e["priority_level"],
"start": e["start"],
"users": sorted(u["display_name"] for u in e["users"]),
}
for e in final_events
if not e["is_override"] and not e["is_gap"]
]
assert returned_events == expected_events

View file

@ -240,9 +240,6 @@ class ScheduleView(
else: # return final schedule
events = schedule.final_events(user_tz, starting_date, days)
# combine multiple-users same-shift events into one
events = self._merge_events(events)
result = {
"id": schedule.public_primary_key,
"name": schedule.name,
@ -251,25 +248,6 @@ class ScheduleView(
}
return Response(result, status=status.HTTP_200_OK)
def _merge_events(self, events):
"""Merge user groups same-shift events."""
if events:
merged = [events[0]]
current = merged[0]
for next_event in events[1:]:
if (
current["start"] == next_event["start"]
and current["shift"]["pk"] is not None
and current["shift"]["pk"] == next_event["shift"]["pk"]
):
current["users"] += next_event["users"]
current["missing_users"] += next_event["missing_users"]
else:
merged.append(next_event)
current = next_event
events = merged
return events
@action(detail=True, methods=["get"])
def next_shifts_per_user(self, request, pk):
"""Return next shift for users in schedule."""

View file

@ -263,6 +263,9 @@ class OnCallSchedule(PolymorphicModel):
}
events.append(shift_json)
# combine multiple-users same-shift events into one
events = self._merge_events(events)
return events
def final_events(self, user_tz, starting_date, days):
@ -387,6 +390,25 @@ class OnCallSchedule(PolymorphicModel):
resolved.sort(key=lambda e: (e["start"], e["shift"]["pk"]))
return resolved
def _merge_events(self, events):
"""Merge user groups same-shift events."""
if events:
merged = [events[0]]
current = merged[0]
for next_event in events[1:]:
if (
current["start"] == next_event["start"]
and current["shift"]["pk"] is not None
and current["shift"]["pk"] == next_event["shift"]["pk"]
):
current["users"] += next_event["users"]
current["missing_users"] += next_event["missing_users"]
else:
merged.append(next_event)
current = next_event
events = merged
return events
class OnCallScheduleICal(OnCallSchedule):
# For the ical schedule both primary and overrides icals are imported via ical url