Merge remote-tracking branch 'origin/matiasb/fix-final-schedule-event-splitting' into new-schedules
This commit is contained in:
commit
ca3c81ed07
3 changed files with 117 additions and 22 deletions
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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."""
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue