Add endpoint for getting schedules events for current user (#2928)
# What this PR does ## Which issue(s) this PR fixes https://github.com/grafana/oncall/issues/2915 ## Checklist - [x] Unit, integration, and e2e (if applicable) tests updated - [x] Documentation added (or `pr:no public docs` PR label added if not required) - [x] `CHANGELOG.md` updated (or `pr:no changelog` PR label added if not required)
This commit is contained in:
parent
a2851d3f81
commit
ecb4ba0057
6 changed files with 422 additions and 66 deletions
|
|
@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|||
|
||||
## Unreleased
|
||||
|
||||
### Added
|
||||
|
||||
- Add internal API endpoint for getting schedules shifts for current user by @Ferril([#2928](https://github.com/grafana/oncall/pull/2928))
|
||||
|
||||
### Changed
|
||||
|
||||
- Make Slack integration not post an alert group message if it's already deleted + refactor AlertGroup and
|
||||
|
|
|
|||
|
|
@ -2008,6 +2008,36 @@ def test_schedule_mention_options_permissions(
|
|||
assert response.status_code == expected_status
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
@pytest.mark.parametrize(
|
||||
"role,expected_status",
|
||||
[
|
||||
(LegacyAccessControlRole.ADMIN, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.EDITOR, status.HTTP_200_OK),
|
||||
(LegacyAccessControlRole.VIEWER, status.HTTP_200_OK),
|
||||
],
|
||||
)
|
||||
def test_current_user_events_permissions(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
role,
|
||||
expected_status,
|
||||
):
|
||||
organization, user, token = make_organization_and_user_with_plugin_token(role)
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:schedule-current-user-events")
|
||||
|
||||
with patch(
|
||||
"apps.api.views.schedule.ScheduleView.current_user_events",
|
||||
return_value=Response(
|
||||
status=status.HTTP_200_OK,
|
||||
),
|
||||
):
|
||||
response = client.get(url, format="json", **make_user_auth_headers(user, token))
|
||||
|
||||
assert response.status_code == expected_status
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_get_schedule_from_other_team_with_flag(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
|
|
@ -2065,3 +2095,172 @@ def test_get_schedule_on_call_now(
|
|||
"avatar_full": "https://example.com/avatar/test123",
|
||||
}
|
||||
]
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_current_user_events(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_user_for_organization,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization, current_user, token = make_organization_and_user_with_plugin_token()
|
||||
other_user = make_user_for_organization(organization)
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:schedule-current-user-events")
|
||||
|
||||
schedule_with_current_user = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
other_schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
|
||||
shifts = (
|
||||
# schedule, user, priority, start time (h), duration (seconds)
|
||||
(schedule_with_current_user, current_user, 1, 0, (24 * 60 * 60) - 1), # r1-1: 0-23:59:59
|
||||
(other_schedule, other_user, 1, 0, (24 * 60 * 60) - 1), # r1-1: 0-23:59:59
|
||||
)
|
||||
now = timezone.now()
|
||||
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
for schedule, user, priority, start_h, duration in shifts:
|
||||
data = {
|
||||
"start": today + timezone.timedelta(hours=start_h),
|
||||
"rotation_start": today + timezone.timedelta(hours=start_h),
|
||||
"duration": timezone.timedelta(seconds=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]])
|
||||
|
||||
schedule.refresh_ical_file()
|
||||
schedule.refresh_ical_final_schedule()
|
||||
|
||||
response = client.get(url, format="json", **make_user_auth_headers(current_user, token))
|
||||
result = response.json()
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert result["is_oncall"] is True
|
||||
assert len(result["schedules"]) == 1
|
||||
assert result["schedules"][0]["id"] == schedule_with_current_user.public_primary_key
|
||||
assert result["schedules"][0]["name"] == schedule_with_current_user.name
|
||||
assert len(result["schedules"][0]["events"]) > 0
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_current_user_events_out_of_range(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization, current_user, token = make_organization_and_user_with_plugin_token()
|
||||
client = APIClient()
|
||||
|
||||
schedule_with_current_user = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
|
||||
shifts = (
|
||||
# schedule, user, priority, start time (h), duration (seconds)
|
||||
(schedule_with_current_user, current_user, 1, 0, (24 * 60 * 60) - 1), # r1-1: 0-23:59:59
|
||||
)
|
||||
now = timezone.now()
|
||||
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
days = 3
|
||||
start_date = today + timezone.timedelta(days=days)
|
||||
for schedule, 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(seconds=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]])
|
||||
|
||||
schedule.refresh_ical_file()
|
||||
schedule.refresh_ical_final_schedule()
|
||||
|
||||
url = reverse("api-internal:schedule-current-user-events") + f"?days={days}"
|
||||
response = client.get(url, format="json", **make_user_auth_headers(current_user, token))
|
||||
result = response.json()
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert result["is_oncall"] is False
|
||||
assert len(result["schedules"]) == 0
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_current_user_events_no_schedules(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization, current_user, token = make_organization_and_user_with_plugin_token()
|
||||
client = APIClient()
|
||||
|
||||
url = reverse("api-internal:schedule-current-user-events")
|
||||
response = client.get(url, format="json", **make_user_auth_headers(current_user, token))
|
||||
result = response.json()
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert result["is_oncall"] is False
|
||||
assert len(result["schedules"]) == 0
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_current_user_events_multiple_schedules(
|
||||
make_organization_and_user_with_plugin_token,
|
||||
make_user_auth_headers,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization, current_user, token = make_organization_and_user_with_plugin_token()
|
||||
client = APIClient()
|
||||
url = reverse("api-internal:schedule-current-user-events")
|
||||
|
||||
schedule_1 = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
schedule_2 = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
|
||||
shifts = (
|
||||
# schedule, user, priority, start time (h), duration (seconds)
|
||||
(schedule_1, current_user, 1, 0, (24 * 60 * 60) - 1), # r1-1: 0-23:59:59
|
||||
(schedule_2, current_user, 1, 0, (24 * 60 * 60) - 1), # r1-1: 0-23:59:59
|
||||
)
|
||||
now = timezone.now()
|
||||
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
for schedule, user, priority, start_h, duration in shifts:
|
||||
data = {
|
||||
"start": today + timezone.timedelta(hours=start_h),
|
||||
"rotation_start": today + timezone.timedelta(hours=start_h),
|
||||
"duration": timezone.timedelta(seconds=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]])
|
||||
|
||||
schedule.refresh_ical_file()
|
||||
schedule.refresh_ical_final_schedule()
|
||||
|
||||
response = client.get(url, format="json", **make_user_auth_headers(current_user, token))
|
||||
result = response.json()
|
||||
|
||||
assert response.status_code == status.HTTP_200_OK
|
||||
assert result["is_oncall"] is True
|
||||
assert len(result["schedules"]) == 2
|
||||
assert result["schedules"][0]["id"] != result["schedules"][1]["id"]
|
||||
assert result["schedules"][0]["id"] in (schedule_1.public_primary_key, schedule_2.public_primary_key)
|
||||
assert result["schedules"][0]["name"] in (schedule_1.name, schedule_2.name)
|
||||
assert result["schedules"][1]["id"] in (schedule_1.public_primary_key, schedule_2.public_primary_key)
|
||||
assert result["schedules"][1]["name"] in (schedule_1.name, schedule_2.name)
|
||||
assert len(result["schedules"][0]["events"]) > 0
|
||||
assert len(result["schedules"][1]["events"]) > 0
|
||||
|
|
|
|||
|
|
@ -92,6 +92,7 @@ class ScheduleView(
|
|||
"notify_oncall_shift_freq_options": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"mention_options": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"related_escalation_chains": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"current_user_events": [RBACPermission.Permissions.SCHEDULES_READ],
|
||||
"create": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"update": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
"partial_update": [RBACPermission.Permissions.SCHEDULES_WRITE],
|
||||
|
|
@ -406,6 +407,29 @@ class ScheduleView(
|
|||
|
||||
return Response(schedule.quality_report(datetime_start, days))
|
||||
|
||||
@action(detail=False, methods=["get"])
|
||||
def current_user_events(self, request):
|
||||
user_tz, starting_date, days = get_date_range_from_request(self.request)
|
||||
pytz_tz = pytz.timezone(user_tz)
|
||||
datetime_start = datetime.datetime.combine(starting_date, datetime.time.min, tzinfo=pytz_tz)
|
||||
|
||||
schedules = OnCallSchedule.objects.related_to_user(self.request.user)
|
||||
schedules_events = []
|
||||
is_oncall = False
|
||||
for schedule in schedules:
|
||||
passed_shifts, current_shifts, upcoming_shifts = schedule.shifts_for_user(
|
||||
user=self.request.user, datetime_start=datetime_start, days=days
|
||||
)
|
||||
all_shifts = passed_shifts + current_shifts + upcoming_shifts
|
||||
if all_shifts:
|
||||
schedules_events.append(
|
||||
{"id": schedule.public_primary_key, "name": schedule.name, "events": all_shifts}
|
||||
)
|
||||
if current_shifts and not is_oncall:
|
||||
is_oncall = True
|
||||
result = {"schedules": schedules_events, "is_oncall": is_oncall}
|
||||
return Response(result, status=status.HTTP_200_OK)
|
||||
|
||||
@action(detail=False, methods=["get"])
|
||||
def type_options(self, request):
|
||||
# TODO: check if it needed
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ from django.conf import settings
|
|||
from django.core.exceptions import ObjectDoesNotExist
|
||||
from django.db.utils import IntegrityError
|
||||
from django.urls import reverse
|
||||
from django.utils import timezone
|
||||
from django_filters import rest_framework as filters
|
||||
from rest_framework import mixins, status, viewsets
|
||||
from rest_framework.decorators import action
|
||||
|
|
@ -577,21 +578,22 @@ class UserView(
|
|||
if days <= 0 or days > UPCOMING_SHIFTS_MAX_DAYS:
|
||||
return Response(status=status.HTTP_400_BAD_REQUEST)
|
||||
|
||||
now = timezone.now()
|
||||
# filter user-related schedules
|
||||
schedules = OnCallSchedule.objects.related_to_user(user)
|
||||
|
||||
# check upcoming shifts
|
||||
upcoming = []
|
||||
for schedule in schedules:
|
||||
current_shift, upcoming_shift = schedule.upcoming_shift_for_user(user, days=days)
|
||||
if current_shift or upcoming_shift:
|
||||
_, current_shifts, upcoming_shifts = schedule.shifts_for_user(user, datetime_start=now, days=days)
|
||||
if current_shifts or upcoming_shifts:
|
||||
upcoming.append(
|
||||
{
|
||||
"schedule_id": schedule.public_primary_key,
|
||||
"schedule_name": schedule.name,
|
||||
"is_oncall": current_shift is not None,
|
||||
"current_shift": current_shift,
|
||||
"next_shift": upcoming_shift,
|
||||
"is_oncall": len(current_shifts) > 0,
|
||||
"current_shift": current_shifts[0] if current_shifts else None,
|
||||
"next_shift": upcoming_shifts[0] if upcoming_shifts else None,
|
||||
}
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -505,33 +505,32 @@ class OnCallSchedule(PolymorphicModel):
|
|||
self.cached_ical_final_schedule = ical_data
|
||||
self.save(update_fields=["cached_ical_final_schedule"])
|
||||
|
||||
def upcoming_shift_for_user(self, user, days=7):
|
||||
def shifts_for_user(
|
||||
self, user: User, datetime_start: datetime.datetime, days: int = 7
|
||||
) -> typing.Tuple[ScheduleEvents, ScheduleEvents, ScheduleEvents]:
|
||||
now = timezone.now()
|
||||
# consider an extra day before to include events from UTC yesterday
|
||||
datetime_start = now - datetime.timedelta(days=1)
|
||||
datetime_end = datetime_start + datetime.timedelta(days=days)
|
||||
|
||||
current_shift = upcoming_shift = None
|
||||
passed_shifts: ScheduleEvents = []
|
||||
current_shifts: ScheduleEvents = []
|
||||
upcoming_shifts: ScheduleEvents = []
|
||||
|
||||
if self.cached_ical_final_schedule is None:
|
||||
# no final schedule info available
|
||||
return None, None
|
||||
return passed_shifts, current_shifts, upcoming_shifts
|
||||
|
||||
events = self.filter_events(datetime_start, datetime_end, all_day_datetime=True, from_cached_final=True)
|
||||
for e in events:
|
||||
if e["end"] < now:
|
||||
# shift is finished, ignore
|
||||
continue
|
||||
users = {u["pk"] for u in e["users"]}
|
||||
events.sort(key=lambda e: e["start"])
|
||||
for event in events:
|
||||
users = {u["pk"] for u in event["users"]}
|
||||
if user.public_primary_key in users:
|
||||
if e["start"] < now and e["end"] > now:
|
||||
# shift is in progress
|
||||
current_shift = e
|
||||
continue
|
||||
upcoming_shift = e
|
||||
break
|
||||
if event["end"] <= now:
|
||||
passed_shifts.append(event)
|
||||
elif event["start"] <= now < event["end"]:
|
||||
current_shifts.append(event)
|
||||
else:
|
||||
upcoming_shifts.append(event)
|
||||
|
||||
return current_shift, upcoming_shift
|
||||
return passed_shifts, current_shifts, upcoming_shifts
|
||||
|
||||
def quality_report(self, date: typing.Optional[datetime.datetime], days: typing.Optional[int]) -> QualityReport:
|
||||
"""
|
||||
|
|
|
|||
|
|
@ -1570,49 +1570,6 @@ def test_user_related_schedules_only_username(
|
|||
assert set(schedules) == {schedule1, schedule2}
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_upcoming_shift_for_user(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization = make_organization()
|
||||
admin = make_user_for_organization(organization)
|
||||
other_user = make_user_for_organization(organization)
|
||||
|
||||
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
shifts = (
|
||||
# user, priority, start time (h), duration (seconds)
|
||||
(admin, 1, 0, (24 * 60 * 60) - 1), # r1-1: 0-23:59:59
|
||||
)
|
||||
today = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
for user, priority, start_h, duration in shifts:
|
||||
data = {
|
||||
"start": today + timezone.timedelta(hours=start_h),
|
||||
"rotation_start": today + timezone.timedelta(hours=start_h),
|
||||
"duration": timezone.timedelta(seconds=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]])
|
||||
schedule.refresh_ical_file()
|
||||
schedule.refresh_ical_final_schedule()
|
||||
|
||||
current_shift, upcoming_shift = schedule.upcoming_shift_for_user(admin)
|
||||
assert current_shift is not None and current_shift["start"] == on_call_shift.start
|
||||
next_shift_start = on_call_shift.start + timezone.timedelta(days=1)
|
||||
assert upcoming_shift is not None and upcoming_shift["start"] == next_shift_start
|
||||
|
||||
current_shift, upcoming_shift = schedule.upcoming_shift_for_user(other_user)
|
||||
assert current_shift is None
|
||||
assert upcoming_shift is None
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_refresh_ical_final_schedule_ok(
|
||||
make_organization,
|
||||
|
|
@ -2552,3 +2509,174 @@ def test_filter_events_ical_duplicated_uid(make_organization, make_user_for_orga
|
|||
assert len(events) == 2
|
||||
assert events[0]["shift"]["pk"] == "eventuid@google.com_1"
|
||||
assert events[1]["shift"]["pk"] == "eventuid@google.com_2_1970-01-01T01:00:00+01:00"
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_shifts_for_user(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization = make_organization()
|
||||
admin = make_user_for_organization(organization)
|
||||
other_user = make_user_for_organization(organization)
|
||||
|
||||
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
shifts = (
|
||||
# user, priority, start time (h), duration (seconds)
|
||||
(admin, 1, 0, (24 * 60 * 60) - 1), # r1-1: 0-23:59:59
|
||||
)
|
||||
now = timezone.now()
|
||||
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
for user, priority, start_h, duration in shifts:
|
||||
data = {
|
||||
"start": today + timezone.timedelta(hours=start_h),
|
||||
"rotation_start": today + timezone.timedelta(hours=start_h),
|
||||
"duration": timezone.timedelta(seconds=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]])
|
||||
schedule.refresh_ical_file()
|
||||
schedule.refresh_ical_final_schedule()
|
||||
|
||||
passed_shifts, current_shifts, upcoming_shifts = schedule.shifts_for_user(admin, now)
|
||||
assert len(passed_shifts) == 0
|
||||
assert len(current_shifts) == 1
|
||||
assert len(upcoming_shifts) == 7
|
||||
|
||||
current_shift = current_shifts[0]
|
||||
assert current_shift is not None and current_shift["start"] == on_call_shift.start
|
||||
next_shift_start = on_call_shift.start + timezone.timedelta(days=1)
|
||||
upcoming_shift = upcoming_shifts[0]
|
||||
assert upcoming_shift is not None and upcoming_shift["start"] == next_shift_start
|
||||
for shifts in (passed_shifts, current_shifts, upcoming_shifts):
|
||||
for shift in shifts:
|
||||
users = {u["pk"] for u in shift["users"]}
|
||||
assert admin.public_primary_key in users
|
||||
|
||||
passed_shifts, current_shifts, upcoming_shifts = schedule.shifts_for_user(other_user, now)
|
||||
assert len(passed_shifts) == 0
|
||||
assert len(current_shifts) == 0
|
||||
assert len(upcoming_shifts) == 0
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_shifts_for_user_only_two_users_with_shifts(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization = make_organization()
|
||||
current_user = make_user_for_organization(organization)
|
||||
user2 = make_user_for_organization(organization)
|
||||
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
|
||||
now = timezone.now()
|
||||
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
start_date = today - timezone.timedelta(days=2)
|
||||
days = 7
|
||||
|
||||
data = {
|
||||
"start": now + timezone.timedelta(hours=1),
|
||||
"rotation_start": now + timezone.timedelta(hours=1),
|
||||
"duration": timezone.timedelta(hours=2),
|
||||
"priority_level": 1,
|
||||
"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([[current_user]])
|
||||
|
||||
# shift with another user
|
||||
data = {
|
||||
"start": start_date + timezone.timedelta(hours=10),
|
||||
"rotation_start": start_date + timezone.timedelta(hours=10),
|
||||
"duration": timezone.timedelta(hours=24),
|
||||
"priority_level": 1,
|
||||
"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([[user2]])
|
||||
|
||||
schedule.refresh_ical_final_schedule()
|
||||
|
||||
passed_shifts, current_shifts, upcoming_shifts = schedule.shifts_for_user(current_user, start_date, days)
|
||||
assert len(passed_shifts) == 0
|
||||
assert len(current_shifts) == 0
|
||||
assert len(upcoming_shifts) == 5
|
||||
for shift in upcoming_shifts:
|
||||
users = {u["pk"] for u in shift["users"]}
|
||||
assert current_user.public_primary_key in users
|
||||
assert shift["start"] > now
|
||||
|
||||
passed_shifts, current_shifts, upcoming_shifts = schedule.shifts_for_user(user2, start_date, days)
|
||||
assert len(passed_shifts) > 0
|
||||
assert len(current_shifts) > 0
|
||||
assert len(upcoming_shifts) > 0
|
||||
for shift in passed_shifts:
|
||||
users = {u["pk"] for u in shift["users"]}
|
||||
assert user2.public_primary_key in users
|
||||
assert shift["end"] < now
|
||||
for shift in current_shifts:
|
||||
users = {u["pk"] for u in shift["users"]}
|
||||
assert user2.public_primary_key in users
|
||||
assert shift["start"] <= now < shift["end"]
|
||||
for shift in upcoming_shifts:
|
||||
users = {u["pk"] for u in shift["users"]}
|
||||
assert user2.public_primary_key in users
|
||||
assert shift["start"] > now
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_shifts_for_user_no_events(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization = make_organization()
|
||||
current_user = make_user_for_organization(organization)
|
||||
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
|
||||
now = timezone.now()
|
||||
today = now.replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
start_date = today - timezone.timedelta(days=2)
|
||||
days = 7
|
||||
|
||||
passed_shifts, current_shifts, upcoming_shifts = schedule.shifts_for_user(current_user, start_date, days)
|
||||
assert len(passed_shifts) == 0
|
||||
assert len(current_shifts) == 0
|
||||
assert len(upcoming_shifts) == 0
|
||||
|
||||
|
||||
@pytest.mark.django_db
|
||||
def test_shifts_for_user_without_final_ical(
|
||||
make_organization,
|
||||
make_user_for_organization,
|
||||
make_schedule,
|
||||
make_on_call_shift,
|
||||
):
|
||||
organization = make_organization()
|
||||
user = make_user_for_organization(organization)
|
||||
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb)
|
||||
|
||||
today = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
|
||||
start_date = today - timezone.timedelta(days=2)
|
||||
days = 7
|
||||
|
||||
passed_shifts, current_shifts, upcoming_shifts = schedule.shifts_for_user(user, start_date, days)
|
||||
assert len(passed_shifts) == 0
|
||||
assert len(current_shifts) == 0
|
||||
assert len(upcoming_shifts) == 0
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue