Skip to content

Commit

Permalink
Fix missing users / empty shifts check
Browse files Browse the repository at this point in the history
  • Loading branch information
matiasb committed Dec 17, 2024
1 parent 1399ba9 commit ce535bf
Show file tree
Hide file tree
Showing 3 changed files with 81 additions and 7 deletions.
13 changes: 13 additions & 0 deletions engine/apps/schedules/ical_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,19 @@
logger.setLevel(logging.DEBUG)


class MissingUser:
"""Represent a missing user in a rolling users shift."""

DISPLAY_NAME = "(missing)"

def __init__(self, pk):
self.pk = pk

@property
def username(self):
return self.DISPLAY_NAME


EmptyShift = namedtuple(
"EmptyShift",
["start", "end", "summary", "description", "attendee", "all_day", "calendar_type", "calendar_tz", "shift_pk"],
Expand Down
23 changes: 16 additions & 7 deletions engine/apps/schedules/models/custom_on_call_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
from django.utils.functional import cached_property
from icalendar.cal import Event

from apps.schedules.ical_utils import MissingUser
from apps.schedules.tasks import (
check_gaps_and_empty_shifts_in_schedule,
drop_cached_ical_task,
Expand Down Expand Up @@ -645,10 +646,6 @@ def get_rolling_users(self):
all_users_pks = set()
users_queue = []
if self.rolling_users is not None:
# get all users pks from rolling_users field
for users_dict in self.rolling_users:
all_users_pks.update(users_dict.keys())
users = User.objects.filter(pk__in=all_users_pks)
# generate users_queue list with user objects
if self.start_rotation_from_user_index is not None:
rolling_users = (
Expand All @@ -657,10 +654,22 @@ def get_rolling_users(self):
)
else:
rolling_users = self.rolling_users

# get all users pks from rolling_users field
for users_dict in self.rolling_users:
all_users_pks.update(users_dict.keys())
users = User.objects.filter(pk__in=all_users_pks)
users_by_id = {user.pk: user for user in users}
for users_dict in rolling_users:
users_list = list(users.filter(pk__in=users_dict.keys()))
if users_list:
users_queue.append(users_list)
users_list = []
for user_pk in users_dict.keys():
try:
user_pk = int(user_pk)
users_list.append(users_by_id.get(user_pk, MissingUser(user_pk)))
except ValueError:
users_list.append(MissingUser(user_pk))
users_queue.append(users_list)

return users_queue

def add_rolling_users(self, rolling_users_list):
Expand Down
52 changes: 52 additions & 0 deletions engine/apps/schedules/tests/test_on_call_schedule.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
ICAL_STATUS_CANCELLED,
ICAL_SUMMARY,
)
from apps.schedules.ical_utils import MissingUser
from apps.schedules.models import (
CustomOnCallShift,
OnCallSchedule,
Expand Down Expand Up @@ -358,6 +359,57 @@ def test_filter_events_include_empty(make_organization, make_user_for_organizati
assert events == expected


@pytest.mark.django_db
def test_filter_events_include_empty_if_deleted(
make_organization, make_user_for_organization, make_schedule, make_on_call_shift
):
organization = make_organization()
schedule = make_schedule(
organization,
schedule_class=OnCallScheduleWeb,
name="test_web_schedule",
)
user = make_user_for_organization(organization)
now = timezone.now().replace(hour=0, minute=0, second=0, microsecond=0)
start_date = now - timezone.timedelta(days=7)

data = {
"start": start_date + timezone.timedelta(hours=10),
"rotation_start": start_date + timezone.timedelta(hours=10),
"duration": timezone.timedelta(hours=8),
"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([[user]])

# user is deleted, shift data still exists but the shift is empty
user.delete()

end_date = start_date + timezone.timedelta(days=1)
events = schedule.filter_events(start_date, end_date, filter_by=OnCallSchedule.TYPE_ICAL_PRIMARY, with_empty=True)
expected = [
{
"calendar_type": OnCallSchedule.TYPE_ICAL_PRIMARY,
"start": on_call_shift.start,
"end": on_call_shift.start + on_call_shift.duration,
"all_day": False,
"is_override": False,
"is_empty": True,
"is_gap": False,
"priority_level": on_call_shift.priority_level,
"missing_users": [MissingUser.DISPLAY_NAME],
"users": [],
"shift": {"pk": on_call_shift.public_primary_key},
"source": "api",
}
]
assert events == expected


@pytest.mark.django_db
def test_filter_events_ical_all_day(make_organization, make_user_for_organization, make_schedule, get_ical):
calendar = get_ical("calendar_with_all_day_event.ics")
Expand Down

0 comments on commit ce535bf

Please sign in to comment.