This PR adds support for migrating data from OpsGenie to Grafana IRM. Closes https://github.com/grafana/irm/issues/1179
2346 lines
84 KiB
Python
2346 lines
84 KiB
Python
import datetime
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from lib.pagerduty.resources.schedules import (
|
|
Restriction,
|
|
Schedule,
|
|
filter_schedules,
|
|
match_schedule,
|
|
)
|
|
|
|
user_id_map = {
|
|
"USER_ID_1": "USER_ID_1",
|
|
"USER_ID_2": "USER_ID_2",
|
|
"USER_ID_3": "USER_ID_3",
|
|
}
|
|
|
|
|
|
@pytest.fixture
|
|
def mock_schedule():
|
|
return {
|
|
"id": "SCHEDULE1",
|
|
"name": "Test Schedule",
|
|
"teams": [{"summary": "Team 1"}],
|
|
"schedule_layers": [
|
|
{
|
|
"users": [
|
|
{"user": {"id": "USER1"}},
|
|
{"user": {"id": "USER2"}},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
|
|
|
|
@patch("lib.pagerduty.resources.schedules.PAGERDUTY_FILTER_TEAM", "Team 1")
|
|
def test_filter_schedules_by_team(mock_schedule):
|
|
schedules = [
|
|
mock_schedule,
|
|
{**mock_schedule, "teams": [{"summary": "Team 2"}]},
|
|
]
|
|
filtered = filter_schedules(schedules)
|
|
assert len(filtered) == 1
|
|
assert filtered[0]["id"] == "SCHEDULE1"
|
|
|
|
|
|
@patch("lib.pagerduty.resources.schedules.PAGERDUTY_FILTER_USERS", ["USER1"])
|
|
def test_filter_schedules_by_users(mock_schedule):
|
|
schedules = [
|
|
mock_schedule,
|
|
{
|
|
**mock_schedule,
|
|
"schedule_layers": [{"users": [{"user": {"id": "USER3"}}]}],
|
|
},
|
|
]
|
|
filtered = filter_schedules(schedules)
|
|
assert len(filtered) == 1
|
|
assert filtered[0]["id"] == "SCHEDULE1"
|
|
|
|
|
|
@patch("lib.pagerduty.resources.schedules.PAGERDUTY_FILTER_SCHEDULE_REGEX", "^Test")
|
|
def test_filter_schedules_by_regex(mock_schedule):
|
|
schedules = [
|
|
mock_schedule,
|
|
{**mock_schedule, "name": "Another Schedule"},
|
|
]
|
|
filtered = filter_schedules(schedules)
|
|
assert len(filtered) == 1
|
|
assert filtered[0]["id"] == "SCHEDULE1"
|
|
|
|
|
|
@patch("lib.pagerduty.resources.schedules.PAGERDUTY_FILTER_TEAM", "Team 1")
|
|
@patch("lib.pagerduty.resources.schedules.PAGERDUTY_FILTER_USERS", ["USER3"])
|
|
def test_filter_schedules_with_multiple_filters_or_logic(mock_schedule):
|
|
"""Test that OR logic is applied between filters - a schedule matching any filter is included"""
|
|
schedules = [
|
|
mock_schedule, # Has Team 1 but not USER3
|
|
{
|
|
"id": "SCHEDULE2",
|
|
"name": "Test Schedule 2",
|
|
"teams": [{"summary": "Team 2"}], # Not Team 1
|
|
"schedule_layers": [{"users": [{"user": {"id": "USER3"}}]}], # Has USER3
|
|
},
|
|
{
|
|
"id": "SCHEDULE3",
|
|
"name": "Test Schedule 3",
|
|
"teams": [{"summary": "Team 3"}], # Not Team 1
|
|
"schedule_layers": [{"users": [{"user": {"id": "USER4"}}]}], # Not USER3
|
|
},
|
|
]
|
|
filtered = filter_schedules(schedules)
|
|
# SCHEDULE1 matches team filter, SCHEDULE2 matches user filter, SCHEDULE3 matches neither
|
|
assert len(filtered) == 2
|
|
assert {s["id"] for s in filtered} == {"SCHEDULE1", "SCHEDULE2"}
|
|
|
|
|
|
def test_match_schedule_name_case_insensitive():
|
|
pd_schedule = {"name": "Test"}
|
|
oncall_schedules = [{"name": "test"}]
|
|
|
|
match_schedule(pd_schedule, oncall_schedules, user_id_map={})
|
|
assert pd_schedule["oncall_schedule"] == oncall_schedules[0]
|
|
|
|
|
|
def test_match_schedule_name_extra_spaces():
|
|
pd_schedule = {"name": " test "}
|
|
oncall_schedules = [{"name": "test"}]
|
|
|
|
match_schedule(pd_schedule, oncall_schedules, user_id_map={})
|
|
assert pd_schedule["oncall_schedule"] == oncall_schedules[0]
|
|
|
|
|
|
def test_merge_restrictions():
|
|
restrictions = [
|
|
Restriction(
|
|
type="daily_restriction",
|
|
start_time_of_day=datetime.time(10, 0),
|
|
duration=datetime.timedelta(hours=1),
|
|
start_day_of_week=None,
|
|
),
|
|
Restriction(
|
|
type="daily_restriction",
|
|
start_time_of_day=datetime.time(9, 0),
|
|
duration=datetime.timedelta(hours=8),
|
|
start_day_of_week=None,
|
|
),
|
|
Restriction(
|
|
type="daily_restriction",
|
|
start_time_of_day=datetime.time(10, 30),
|
|
duration=datetime.timedelta(hours=9),
|
|
start_day_of_week=None,
|
|
),
|
|
Restriction(
|
|
type="daily_restriction",
|
|
start_time_of_day=datetime.time(22, 0),
|
|
duration=datetime.timedelta(hours=4),
|
|
start_day_of_week=None,
|
|
),
|
|
]
|
|
|
|
weekly_restrictions = []
|
|
for restriction in restrictions:
|
|
weekly_restrictions += restriction.to_weekly_restrictions()
|
|
|
|
merged = Restriction.merge_restrictions(weekly_restrictions)
|
|
|
|
expected = []
|
|
for weekday in range(7):
|
|
expected += [
|
|
Restriction(
|
|
type=Restriction.Type.WEEKLY,
|
|
start_time_of_day=datetime.time(9, 0),
|
|
duration=datetime.timedelta(hours=10, minutes=30),
|
|
start_day_of_week=weekday,
|
|
),
|
|
Restriction(
|
|
type=Restriction.Type.WEEKLY,
|
|
start_time_of_day=datetime.time(22, 0),
|
|
duration=datetime.timedelta(hours=4),
|
|
start_day_of_week=weekday,
|
|
),
|
|
]
|
|
|
|
assert merged == expected
|
|
|
|
|
|
def test_current_or_next_restriction():
|
|
restrictions = [
|
|
Restriction(
|
|
type=Restriction.Type.WEEKLY,
|
|
start_time_of_day=datetime.time(10, 0),
|
|
duration=datetime.timedelta(hours=1),
|
|
start_day_of_week=0,
|
|
),
|
|
Restriction(
|
|
type=Restriction.Type.WEEKLY,
|
|
start_time_of_day=datetime.time(22, 0),
|
|
duration=datetime.timedelta(hours=28),
|
|
start_day_of_week=5,
|
|
),
|
|
]
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions,
|
|
datetime.datetime(2023, 2, 20, 10, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[0],
|
|
datetime.datetime(2023, 2, 20, 10, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions, datetime.datetime(2023, 2, 20, 8, 0, tzinfo=datetime.timezone.utc)
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[0],
|
|
datetime.datetime(2023, 2, 20, 10, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions,
|
|
datetime.datetime(2023, 2, 20, 11, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[1],
|
|
datetime.datetime(2023, 2, 25, 22, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions,
|
|
datetime.datetime(2023, 2, 22, 11, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[1],
|
|
datetime.datetime(2023, 2, 25, 22, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions,
|
|
datetime.datetime(2023, 2, 26, 12, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[1],
|
|
datetime.datetime(2023, 2, 25, 22, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions,
|
|
datetime.datetime(2023, 2, 26, 22, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[1],
|
|
datetime.datetime(2023, 2, 25, 22, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions, datetime.datetime(2023, 2, 27, 0, 0, tzinfo=datetime.timezone.utc)
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[1],
|
|
datetime.datetime(2023, 2, 25, 22, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions, datetime.datetime(2023, 2, 27, 2, 0, tzinfo=datetime.timezone.utc)
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[0],
|
|
datetime.datetime(2023, 2, 27, 10, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restrictions = [
|
|
Restriction(
|
|
type=Restriction.Type.WEEKLY,
|
|
start_time_of_day=datetime.time(10, 0),
|
|
duration=datetime.timedelta(hours=20),
|
|
start_day_of_week=2,
|
|
),
|
|
]
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions,
|
|
datetime.datetime(2023, 2, 23, 10, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[0],
|
|
datetime.datetime(2023, 3, 1, 10, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
restriction, date = Restriction.current_or_next_restriction(
|
|
restrictions, datetime.datetime(2023, 2, 23, 5, 0, tzinfo=datetime.timezone.utc)
|
|
)
|
|
assert (restriction, date) == (
|
|
restrictions[0],
|
|
datetime.datetime(2023, 2, 22, 10, 0, tzinfo=datetime.timezone.utc),
|
|
)
|
|
|
|
|
|
def test_deactivated_users():
|
|
pd_schedule = {
|
|
"name": "No restrictions",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-19T19:25:55Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-07T19:00:00Z",
|
|
"rotation_turn_length_seconds": 1209600,
|
|
"restrictions": [],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_DEACTIVATED"}},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
assert errors == [
|
|
"Layer 1: Users with IDs ['USER_ID_DEACTIVATED'] not found. The users probably have been deactivated in PagerDuty."
|
|
]
|
|
|
|
|
|
def test_no_restrictions():
|
|
pd_schedule = {
|
|
"name": "No restrictions",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 5",
|
|
"start": "2023-02-19T19:25:55Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-07T19:00:00Z",
|
|
"rotation_turn_length_seconds": 1209600,
|
|
"restrictions": [],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 4",
|
|
"start": "2023-02-21T14:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-19T14:00:00Z",
|
|
"rotation_turn_length_seconds": 172800,
|
|
"restrictions": [],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 3",
|
|
"start": "2023-02-21T19:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-19T18:00:00Z",
|
|
"rotation_turn_length_seconds": 25200,
|
|
"restrictions": [],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 2",
|
|
"start": "2023-02-19T18:08:11Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-15T18:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-20T17:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-20T17:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
],
|
|
}
|
|
|
|
expected = {
|
|
"name": "No restrictions",
|
|
"team_id": None,
|
|
"time_zone": "Europe/London",
|
|
"type": "web",
|
|
"shifts": [
|
|
{
|
|
"level": 1,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-20T17:00:00",
|
|
"until": None,
|
|
"start": "2023-02-20T17:00:00",
|
|
"duration": 86400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 2,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-19T18:08:11",
|
|
"until": None,
|
|
"start": "2023-02-15T18:00:00",
|
|
"duration": 604800,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 3,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T19:00:00",
|
|
"until": None,
|
|
"start": "2023-02-19T18:00:00",
|
|
"duration": 25200,
|
|
"frequency": "hourly",
|
|
"interval": 7,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 4,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T14:00:00",
|
|
"until": None,
|
|
"start": "2023-02-19T14:00:00",
|
|
"duration": 172800,
|
|
"frequency": "daily",
|
|
"interval": 2,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 5,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-19T19:25:55",
|
|
"until": None,
|
|
"start": "2023-02-07T19:00:00",
|
|
"duration": 1209600,
|
|
"frequency": "weekly",
|
|
"interval": 2,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
],
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
for shift in oncall_schedule["shifts"]:
|
|
shift.pop("name")
|
|
|
|
assert errors == []
|
|
assert oncall_schedule == expected
|
|
|
|
|
|
def test_daily_with_daily_restrictions():
|
|
pd_schedule = {
|
|
"name": "Daily with daily restrictions",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 10",
|
|
"start": "2023-02-21T20:00:09Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T20:00:00Z",
|
|
"rotation_turn_length_seconds": 259200,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "17:00:00",
|
|
"duration_seconds": 57600,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 9",
|
|
"start": "2023-02-21T17:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T17:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "15:00:00",
|
|
"duration_seconds": 14400,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}],
|
|
},
|
|
{
|
|
"name": "Layer 8",
|
|
"start": "2023-02-21T00:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T00:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "20:00:00",
|
|
"duration_seconds": 39600,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "21:00:00",
|
|
"duration_seconds": 25200,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "23:00:00",
|
|
"duration_seconds": 21600,
|
|
"start_day_of_week": None,
|
|
},
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 7",
|
|
"start": "2023-02-19T18:49:46Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-19T03:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "20:00:00",
|
|
"duration_seconds": 39600,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 6",
|
|
"start": "2023-02-19T18:49:46Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-19T18:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 25200,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "03:00:00",
|
|
"duration_seconds": 7200,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "04:30:00",
|
|
"duration_seconds": 16200,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "11:00:00",
|
|
"duration_seconds": 34200,
|
|
"start_day_of_week": None,
|
|
},
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 5",
|
|
"start": "2023-02-21T14:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-19T14:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "04:00:00",
|
|
"duration_seconds": 14400,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "13:00:00",
|
|
"duration_seconds": 18000,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "19:00:00",
|
|
"duration_seconds": 12600,
|
|
"start_day_of_week": None,
|
|
},
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 4",
|
|
"start": "2023-02-21T16:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-19T16:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 3",
|
|
"start": "2023-02-21T20:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-18T20:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 2",
|
|
"start": "2023-02-21T05:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-19T05:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-21T09:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-19T09:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
],
|
|
}
|
|
|
|
expected = {
|
|
"name": "Daily with daily restrictions",
|
|
"team_id": None,
|
|
"time_zone": "Europe/London",
|
|
"type": "web",
|
|
"shifts": [
|
|
{
|
|
"level": 1,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T09:00:00",
|
|
"until": None,
|
|
"start": "2023-02-19T09:00:00",
|
|
"duration": 28800,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 2,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T05:00:00",
|
|
"until": None,
|
|
"start": "2023-02-19T09:00:00",
|
|
"duration": 28800,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 3,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T20:00:00",
|
|
"until": None,
|
|
"start": "2023-02-19T09:00:00",
|
|
"duration": 28800,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 4,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T16:00:00",
|
|
"until": None,
|
|
"start": "2023-02-19T16:00:00",
|
|
"duration": 3600,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 4,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T16:00:00",
|
|
"until": None,
|
|
"start": "2023-02-20T09:00:00",
|
|
"duration": 25200,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 5,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T14:00:00",
|
|
"until": None,
|
|
"start": "2023-02-19T14:00:00",
|
|
"duration": 14400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 5,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T14:00:00",
|
|
"until": None,
|
|
"start": "2023-02-19T19:00:00",
|
|
"duration": 12600,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 5,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T14:00:00",
|
|
"until": None,
|
|
"start": "2023-02-20T04:00:00",
|
|
"duration": 14400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 5,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T14:00:00",
|
|
"until": None,
|
|
"start": "2023-02-20T13:00:00",
|
|
"duration": 3600,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 6,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-19T18:49:46",
|
|
"until": None,
|
|
"start": "2023-02-19T18:00:00",
|
|
"duration": 9000,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 6,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-19T18:49:46",
|
|
"until": None,
|
|
"start": "2023-02-20T00:00:00",
|
|
"duration": 32400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 6,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-19T18:49:46",
|
|
"until": None,
|
|
"start": "2023-02-20T11:00:00",
|
|
"duration": 25200,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 7,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-19T18:49:46",
|
|
"until": None,
|
|
"start": "2023-02-19T03:00:00",
|
|
"duration": 14400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 7,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-19T18:49:46",
|
|
"until": None,
|
|
"start": "2023-02-19T20:00:00",
|
|
"duration": 25200,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 8,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T00:00:00",
|
|
"until": None,
|
|
"start": "2023-02-21T00:00:00",
|
|
"duration": 25200,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 8,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T00:00:00",
|
|
"until": None,
|
|
"start": "2023-02-21T20:00:00",
|
|
"duration": 14400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 9,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T17:00:00",
|
|
"until": None,
|
|
"start": "2023-02-21T17:00:00",
|
|
"duration": 7200,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 9,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-21T17:00:00",
|
|
"until": None,
|
|
"start": "2023-02-22T15:00:00",
|
|
"duration": 7200,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": None,
|
|
"rolling_users": [["USER_ID_1"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 46800,
|
|
"frequency": "daily",
|
|
"interval": 3,
|
|
"level": 10,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T20:00:09",
|
|
"start": "2023-02-21T20:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 57600,
|
|
"frequency": "daily",
|
|
"interval": 3,
|
|
"level": 10,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T20:00:09",
|
|
"start": "2023-02-22T17:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 57600,
|
|
"frequency": "daily",
|
|
"interval": 3,
|
|
"level": 10,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T20:00:09",
|
|
"start": "2023-02-23T17:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 10800,
|
|
"frequency": "daily",
|
|
"interval": 3,
|
|
"level": 10,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T20:00:09",
|
|
"start": "2023-02-24T17:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
],
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
for shift in oncall_schedule["shifts"]:
|
|
shift.pop("name")
|
|
|
|
assert errors == []
|
|
assert oncall_schedule == expected
|
|
|
|
|
|
def test_weekly_with_daily_restrictions():
|
|
pd_schedule = {
|
|
"name": "Weekly with daily restrictions",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 6",
|
|
"start": "2023-02-21T14:04:37Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T17:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 5",
|
|
"start": "2023-02-21T14:04:37Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T08:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 4",
|
|
"start": "2023-02-21T14:04:37Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T09:00:00Z",
|
|
"rotation_turn_length_seconds": 1814400,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "22:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
},
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 3",
|
|
"start": "2023-02-20T15:23:15Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-15T08:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 72000,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "10:00:00",
|
|
"duration_seconds": 54000,
|
|
"start_day_of_week": None,
|
|
},
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "06:00:00",
|
|
"duration_seconds": 3600,
|
|
"start_day_of_week": None,
|
|
},
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 2",
|
|
"start": "2023-02-20T12:50:08Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-14T09:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-20T12:50:08Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-20T09:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
],
|
|
}
|
|
|
|
expected = {
|
|
"name": "Weekly with daily restrictions",
|
|
"team_id": None,
|
|
"time_zone": "Europe/London",
|
|
"type": "web",
|
|
"shifts": [
|
|
{
|
|
"level": 1,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-20T12:50:08",
|
|
"until": None,
|
|
"start": "2023-02-20T09:00:00",
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "MO",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"level": 2,
|
|
"type": "rolling_users",
|
|
"rotation_start": "2023-02-20T12:50:08",
|
|
"until": None,
|
|
"start": "2023-02-14T09:00:00",
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"start_rotation_from_user_index": 0,
|
|
"week_start": "TU",
|
|
"time_zone": "UTC",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 72000,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 3,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"rotation_start": "2023-02-20T15:23:15",
|
|
"start": "2023-02-15T09:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "WE",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 3600,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 3,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"rotation_start": "2023-02-20T15:23:15",
|
|
"start": "2023-02-16T06:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "TH",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T14:04:37",
|
|
"start": "2023-02-21T09:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "TU",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T14:04:37",
|
|
"start": "2023-02-28T09:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "TU",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T14:04:37",
|
|
"start": "2023-03-07T09:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "TU",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T14:04:37",
|
|
"start": "2023-02-21T22:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "TU",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T14:04:37",
|
|
"start": "2023-02-28T22:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "TU",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T14:04:37",
|
|
"start": "2023-03-07T22:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "TU",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 5,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T14:04:37",
|
|
"start": "2023-02-21T09:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "TU",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 28800,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR", "SA", "SU"],
|
|
"level": 6,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T14:04:37",
|
|
"start": "2023-02-22T09:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "WE",
|
|
"source": 0,
|
|
},
|
|
],
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
assert errors == []
|
|
|
|
for shift in oncall_schedule["shifts"]:
|
|
shift.pop("name")
|
|
assert oncall_schedule == expected
|
|
|
|
|
|
def test_daily_with_weekly_restrictions():
|
|
pd_schedule = {
|
|
"name": "Daily with weekly restrictions",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 4",
|
|
"start": "2023-02-21T19:42:57Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-20T05:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "05:00:00",
|
|
"duration_seconds": 172800,
|
|
"start_day_of_week": 2,
|
|
},
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "05:00:00",
|
|
"duration_seconds": 172800,
|
|
"start_day_of_week": 6,
|
|
},
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 3",
|
|
"start": "2023-02-20T17:48:08Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-13T00:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 432000,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 2",
|
|
"start": "2023-02-21T16:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-20T16:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "16:00:00",
|
|
"duration_seconds": 86400,
|
|
"start_day_of_week": 1,
|
|
},
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "16:00:00",
|
|
"duration_seconds": 172800,
|
|
"start_day_of_week": 3,
|
|
},
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-21T17:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-20T17:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "17:00:00",
|
|
"duration_seconds": 86400,
|
|
"start_day_of_week": 1,
|
|
},
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "17:00:00",
|
|
"duration_seconds": 259200,
|
|
"start_day_of_week": 3,
|
|
},
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
|
|
expected = {
|
|
"name": "Daily with weekly restrictions",
|
|
"team_id": None,
|
|
"time_zone": "Europe/London",
|
|
"type": "web",
|
|
"shifts": [
|
|
{
|
|
"duration": 86400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": ["MO", "WE", "TH", "FR"],
|
|
"level": 1,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T17:00:00",
|
|
"start": "2023-02-20T17:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 86400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": ["MO", "WE", "TH"],
|
|
"level": 2,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T16:00:00",
|
|
"start": "2023-02-20T16:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 86400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": ["MO", "TU", "WE", "TH", "FR"],
|
|
"level": 3,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-20T17:48:08",
|
|
"start": "2023-02-13T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"duration": 86400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"by_day": ["TU", "WE", "SA", "SU"],
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T19:42:57",
|
|
"start": "2023-02-21T05:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
],
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
assert errors == []
|
|
|
|
for shift in oncall_schedule["shifts"]:
|
|
shift.pop("name")
|
|
assert oncall_schedule == expected
|
|
|
|
|
|
def test_weekly_with_weekly_restrictions():
|
|
pd_schedule = {
|
|
"name": "Weekly (weekly)",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 6",
|
|
"start": "2023-02-21T13:32:17Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-24T13:00:00Z",
|
|
"rotation_turn_length_seconds": 1814400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 349200,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 5",
|
|
"start": "2023-02-21T13:26:21Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-25T13:00:00Z",
|
|
"rotation_turn_length_seconds": 1814400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 313200,
|
|
"start_day_of_week": 6,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 4",
|
|
"start": "2023-02-21T11:14:44Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-20T03:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "10:00:00",
|
|
"duration_seconds": 154800,
|
|
"start_day_of_week": 6,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 3",
|
|
"start": "2023-02-20T18:10:46Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-14T05:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 86400,
|
|
"start_day_of_week": 3,
|
|
},
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 298800,
|
|
"start_day_of_week": 5,
|
|
},
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 2",
|
|
"start": "2023-02-20T18:10:46Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-16T17:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "17:00:00",
|
|
"duration_seconds": 345600,
|
|
"start_day_of_week": 6,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-20T18:10:46Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-20T00:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 432000,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
],
|
|
}
|
|
|
|
expected = {
|
|
"name": "Weekly (weekly)",
|
|
"shifts": [
|
|
{
|
|
"by_day": None,
|
|
"duration": 432000,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"level": 1,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-20T18:10:46",
|
|
"start": "2023-02-20T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 345600,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"level": 2,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-20T18:10:46",
|
|
"start": "2023-02-18T17:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 86400,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"level": 3,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"rotation_start": "2023-02-20T18:10:46",
|
|
"start": "2023-02-15T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 298800,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"level": 3,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"]],
|
|
"rotation_start": "2023-02-20T18:10:46",
|
|
"start": "2023-02-17T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 7200,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T11:14:44",
|
|
"start": "2023-02-20T03:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 147600,
|
|
"frequency": "weekly",
|
|
"interval": 1,
|
|
"level": 4,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T11:14:44",
|
|
"start": "2023-02-25T10:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 266400,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"level": 5,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T13:26:21",
|
|
"start": "2023-02-25T13:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 313200,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"level": 5,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T13:26:21",
|
|
"start": "2023-03-04T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 313200,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"level": 5,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T13:26:21",
|
|
"start": "2023-03-11T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 46800,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"level": 5,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T13:26:21",
|
|
"start": "2023-03-18T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 349200,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"level": 6,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T13:32:17",
|
|
"start": "2023-02-27T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 349200,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"level": 6,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T13:32:17",
|
|
"start": "2023-03-06T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
{
|
|
"by_day": None,
|
|
"duration": 349200,
|
|
"frequency": "weekly",
|
|
"interval": 3,
|
|
"level": 6,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T13:32:17",
|
|
"start": "2023-03-13T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
],
|
|
"team_id": None,
|
|
"time_zone": "Europe/London",
|
|
"type": "web",
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
assert errors == []
|
|
|
|
for shift in oncall_schedule["shifts"]:
|
|
shift.pop("name")
|
|
assert oncall_schedule == expected
|
|
|
|
|
|
def test_errors():
|
|
pd_schedule = {
|
|
"name": "Errors",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 11",
|
|
"start": "2023-02-21T17:39:43Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-18T00:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 43200,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 10",
|
|
"start": "2023-02-21T17:39:43Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-18T16:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 403200,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 9",
|
|
"start": "2023-02-21T11:06:04Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T11:00:00Z",
|
|
"rotation_turn_length_seconds": 25200,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}],
|
|
},
|
|
{
|
|
"name": "Layer 8",
|
|
"start": "2023-02-21T11:06:04Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T11:00:00Z",
|
|
"rotation_turn_length_seconds": 25200,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 345600,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}],
|
|
},
|
|
{
|
|
"name": "Layer 7",
|
|
"start": "2023-02-21T11:06:04Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T11:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 345600,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}],
|
|
},
|
|
{
|
|
"name": "Layer 6",
|
|
"start": "2023-02-21T11:06:04Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T11:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}],
|
|
},
|
|
{
|
|
"name": "Layer 5",
|
|
"start": "2023-02-21T11:06:04Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T11:00:00Z",
|
|
"rotation_turn_length_seconds": 25200,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 349200,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 4",
|
|
"start": "2023-02-21T11:06:04Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T10:00:00Z",
|
|
"rotation_turn_length_seconds": 25200,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 64800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 3",
|
|
"start": "2023-02-21T13:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-21T13:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [
|
|
{
|
|
"type": "weekly_restriction",
|
|
"start_time_of_day": "00:00:00",
|
|
"duration_seconds": 406800,
|
|
"start_day_of_week": 1,
|
|
}
|
|
],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
},
|
|
{
|
|
"name": "Layer 2",
|
|
"start": "2023-02-21T11:06:04Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-20T10:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 7200,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-21T12:00:00Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-15T12:00:00Z",
|
|
"rotation_turn_length_seconds": 604800,
|
|
"restrictions": [
|
|
{
|
|
"type": "daily_restriction",
|
|
"start_time_of_day": "09:00:00",
|
|
"duration_seconds": 28800,
|
|
"start_day_of_week": None,
|
|
}
|
|
],
|
|
"users": [{"user": {"id": "USER_ID_1"}}, {"user": {"id": "USER_ID_2"}}],
|
|
},
|
|
],
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
assert errors == [
|
|
"Layer 1: Cannot migrate 1-weekly rotation with daily restrictions that are split by handoff.",
|
|
"Layer 2: Cannot migrate 1-weekly rotation with daily restrictions that are split by handoff.",
|
|
"Layer 3: Cannot migrate 1-daily rotation with weekly restrictions that start at a different time than the shift start.",
|
|
"Layer 4: Cannot migrate 7-hourly rotation with daily restrictions.",
|
|
"Layer 5: Cannot migrate 7-hourly rotation with weekly restrictions.",
|
|
"Layer 6: Cannot migrate 1-weekly rotation with daily restrictions that are split by handoff. Layer has a single user, consider simplifying the rotation in PD.",
|
|
"Layer 7: Cannot migrate 1-daily rotation with weekly restrictions that start at a different time than the shift start. Layer has a single user, consider simplifying the rotation in PD.",
|
|
"Layer 8: Cannot migrate 7-hourly rotation with weekly restrictions. Layer has a single user, consider simplifying the rotation in PD.",
|
|
"Layer 9: Cannot migrate 7-hourly rotation with daily restrictions. Layer has a single user, consider simplifying the rotation in PD.",
|
|
"Layer 10: Cannot migrate 1-daily rotation with weekly restrictions that start at a different time than the shift start.",
|
|
"Layer 11: Cannot migrate 1-daily rotation with weekly restrictions that have durations that are not a multiple of a 24 hours.",
|
|
]
|
|
assert oncall_schedule is None
|
|
|
|
|
|
def test_time_zone():
|
|
pd_schedule = {
|
|
"name": "Time zone",
|
|
"time_zone": "Europe/Paris",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-21T17:39:43Z",
|
|
"end": None,
|
|
"rotation_virtual_start": "2023-02-18T00:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
}
|
|
],
|
|
}
|
|
|
|
expected = {
|
|
"name": "Time zone",
|
|
"shifts": [
|
|
{
|
|
"duration": 86400,
|
|
"frequency": "daily",
|
|
"interval": 1,
|
|
"level": 1,
|
|
"rolling_users": [["USER_ID_1"], ["USER_ID_2"], ["USER_ID_3"]],
|
|
"rotation_start": "2023-02-21T17:39:43",
|
|
"start": "2023-02-18T00:00:00",
|
|
"start_rotation_from_user_index": 0,
|
|
"time_zone": "UTC",
|
|
"type": "rolling_users",
|
|
"until": None,
|
|
"week_start": "MO",
|
|
"source": 0,
|
|
},
|
|
],
|
|
"team_id": None,
|
|
"time_zone": "Europe/Paris",
|
|
"type": "web",
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
assert errors == []
|
|
|
|
for shift in oncall_schedule["shifts"]:
|
|
shift.pop("name")
|
|
assert oncall_schedule == expected
|
|
|
|
|
|
def test_removed_layers():
|
|
pd_schedule = {
|
|
"name": "Removed layer",
|
|
"time_zone": "Europe/Paris",
|
|
"overrides": [],
|
|
"schedule_layers": [
|
|
{
|
|
"name": "Layer 1",
|
|
"start": "2023-02-21T17:39:43Z",
|
|
"end": "2023-02-21T17:39:43Z",
|
|
"rotation_virtual_start": "2023-02-18T00:00:00Z",
|
|
"rotation_turn_length_seconds": 86400,
|
|
"restrictions": [],
|
|
"users": [
|
|
{"user": {"id": "USER_ID_1"}},
|
|
{"user": {"id": "USER_ID_2"}},
|
|
{"user": {"id": "USER_ID_3"}},
|
|
],
|
|
}
|
|
],
|
|
}
|
|
|
|
expected = {
|
|
"name": "Removed layer",
|
|
"shifts": [],
|
|
"team_id": None,
|
|
"time_zone": "Europe/Paris",
|
|
"type": "web",
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
assert errors == []
|
|
|
|
for shift in oncall_schedule["shifts"]:
|
|
shift.pop("name")
|
|
assert oncall_schedule == expected
|
|
|
|
|
|
def test_overrides():
|
|
pd_schedule = {
|
|
"name": "Overrides",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [
|
|
{
|
|
"start": "2023-03-02T11:00:00Z",
|
|
"end": "2023-03-02T12:00:00Z",
|
|
"user": {"id": "USER_ID_1"},
|
|
},
|
|
{
|
|
"start": "2023-03-02T11:00:00Z",
|
|
"end": "2023-03-02T12:00:00Z",
|
|
"user": {"id": "USER_ID_1"},
|
|
},
|
|
{
|
|
"start": "2023-03-02T11:00:00Z",
|
|
"end": "2023-03-02T12:00:00Z",
|
|
"user": {"id": "USER_ID_1"},
|
|
},
|
|
{
|
|
"start": "2023-03-02T11:00:00Z",
|
|
"end": "2023-03-02T12:00:00Z",
|
|
"user": {"id": "USER_ID_1"},
|
|
},
|
|
],
|
|
"schedule_layers": [],
|
|
}
|
|
|
|
expected = {
|
|
"name": "Overrides",
|
|
"shifts": [
|
|
{
|
|
"team_id": None,
|
|
"duration": 3600,
|
|
"users": ["USER_ID_1"],
|
|
"rotation_start": "2023-03-02T11:00:00",
|
|
"start": "2023-03-02T11:00:00",
|
|
"time_zone": "UTC",
|
|
"type": "override",
|
|
"source": 0,
|
|
},
|
|
]
|
|
* 4, # all shifts are the same
|
|
"team_id": None,
|
|
"time_zone": "Europe/London",
|
|
"type": "web",
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
assert errors == []
|
|
|
|
for shift in oncall_schedule["shifts"]:
|
|
shift.pop("name")
|
|
assert oncall_schedule == expected
|
|
|
|
|
|
def test_override_deactivated_user():
|
|
pd_schedule = {
|
|
"name": "Overrides",
|
|
"time_zone": "Europe/London",
|
|
"overrides": [
|
|
{
|
|
"start": "2023-03-02T11:00:00",
|
|
"end": "2023-03-02T12:00:00",
|
|
"user": {"id": "USER_ID_4"},
|
|
},
|
|
],
|
|
"schedule_layers": [],
|
|
}
|
|
|
|
oncall_schedule, errors = Schedule.from_dict(pd_schedule).to_oncall_schedule(
|
|
user_id_map
|
|
)
|
|
|
|
assert errors == [
|
|
"Override: User with ID 'USER_ID_4' not found. The user probably has been deactivated in PagerDuty."
|
|
]
|
|
assert oncall_schedule is None
|