diff --git a/engine/apps/api/tests/test_schedules.py b/engine/apps/api/tests/test_schedules.py
index c788d521..cadcb5f4 100644
--- a/engine/apps/api/tests/test_schedules.py
+++ b/engine/apps/api/tests/test_schedules.py
@@ -424,6 +424,7 @@ def test_events_calendar(
"start": on_call_shift.start,
"end": on_call_shift.start + on_call_shift.duration,
"users": [{"display_name": user.username, "pk": user.public_primary_key}],
+ "missing_users": [],
"priority_level": on_call_shift.priority_level,
"source": "api",
"calendar_type": OnCallSchedule.PRIMARY,
@@ -487,6 +488,7 @@ def test_filter_events_calendar(
"start": mon_start,
"end": mon_start + on_call_shift.duration,
"users": [{"display_name": user.username, "pk": user.public_primary_key}],
+ "missing_users": [],
"priority_level": on_call_shift.priority_level,
"source": "api",
"calendar_type": OnCallSchedule.PRIMARY,
@@ -501,6 +503,7 @@ def test_filter_events_calendar(
"start": fri_start,
"end": fri_start + on_call_shift.duration,
"users": [{"display_name": user.username, "pk": user.public_primary_key}],
+ "missing_users": [],
"priority_level": on_call_shift.priority_level,
"source": "api",
"calendar_type": OnCallSchedule.PRIMARY,
@@ -567,6 +570,7 @@ def test_filter_events_range_calendar(
"start": fri_start,
"end": fri_start + on_call_shift.duration,
"users": [{"display_name": user.username, "pk": user.public_primary_key}],
+ "missing_users": [],
"priority_level": on_call_shift.priority_level,
"source": "api",
"calendar_type": OnCallSchedule.PRIMARY,
diff --git a/engine/apps/api/views/schedule.py b/engine/apps/api/views/schedule.py
index 06791a95..1de11b85 100644
--- a/engine/apps/api/views/schedule.py
+++ b/engine/apps/api/views/schedule.py
@@ -211,6 +211,7 @@ class ScheduleView(
}
for user in shift["users"]
],
+ "missing_users": shift["missing_users"],
"priority_level": shift["priority"] if shift["priority"] != 0 else None,
"source": shift["source"],
"calendar_type": shift["calendar_type"],
diff --git a/engine/apps/schedules/ical_utils.py b/engine/apps/schedules/ical_utils.py
index 2705c4db..a22ddee4 100644
--- a/engine/apps/schedules/ical_utils.py
+++ b/engine/apps/schedules/ical_utils.py
@@ -137,6 +137,7 @@ def list_of_oncall_shifts_from_ical(
"start": g.start if g.start else datetime_start,
"end": g.end if g.end else datetime_end,
"users": [],
+ "missing_users": [],
"priority": None,
"source": None,
"calendar_type": None,
@@ -157,6 +158,7 @@ def get_shifts_dict(calendar, calendar_type, schedule, datetime_start, datetime_
priority = parse_priority_from_string(event.get(ICAL_SUMMARY, "[L0]"))
pk, source = parse_event_uid(event.get(ICAL_UID))
users = get_users_from_ical_event(event, schedule.organization)
+ missing_users = get_missing_users_from_ical_event(event, schedule.organization)
# Define on-call shift out of ical event that has the actual user
if len(users) > 0 or with_empty_shifts:
if type(event[ICAL_DATETIME_START].dt) == datetime.date:
@@ -168,6 +170,7 @@ def get_shifts_dict(calendar, calendar_type, schedule, datetime_start, datetime_
"start": start,
"end": end,
"users": users,
+ "missing_users": missing_users,
"priority": priority,
"source": source,
"calendar_type": calendar_type,
@@ -183,6 +186,7 @@ def get_shifts_dict(calendar, calendar_type, schedule, datetime_start, datetime_
"start": start,
"end": end,
"users": users,
+ "missing_users": missing_users,
"priority": priority,
"source": source,
"calendar_type": calendar_type,
@@ -379,6 +383,14 @@ def get_usernames_from_ical_event(event):
return usernames_found, priority
+def get_missing_users_from_ical_event(event, organization):
+ all_usernames, _ = get_usernames_from_ical_event(event)
+ users = list(get_users_from_ical_event(event, organization))
+ found_usernames = [u.username for u in users]
+ found_emails = [u.email for u in users]
+ return [u for u in all_usernames if u != "" and u not in found_usernames and u not in found_emails]
+
+
def get_users_from_ical_event(event, organization):
usernames_from_ical, _ = get_usernames_from_ical_event(event)
users = []
diff --git a/grafana-plugin/src/models/schedule/schedule.types.ts b/grafana-plugin/src/models/schedule/schedule.types.ts
index 567be12b..dc3cc3f7 100644
--- a/grafana-plugin/src/models/schedule/schedule.types.ts
+++ b/grafana-plugin/src/models/schedule/schedule.types.ts
@@ -36,6 +36,7 @@ export interface ScheduleEvent {
users: User[];
is_empty: boolean;
is_gap: boolean;
+ missing_users: string[];
}
export interface CreateScheduleExportTokenResponse {
diff --git a/grafana-plugin/src/pages/schedules/Schedules.tsx b/grafana-plugin/src/pages/schedules/Schedules.tsx
index ff557400..d538a81d 100644
--- a/grafana-plugin/src/pages/schedules/Schedules.tsx
+++ b/grafana-plugin/src/pages/schedules/Schedules.tsx
@@ -495,7 +495,15 @@ const Event = ({ event }: EventProps) => {
) : (
- Empty shift (event without associated user or user with Viewer access)
+ Empty shift
+ {event.missing_users[0] && (
+
+ (check if {event.missing_users[0].includes(',') ? 'some of these users -' : 'user -'}{' '}
+ "{event.missing_users[0]}"{' '}
+ {event.missing_users[0].includes(',') ? 'are' : 'is'} existing in OnCall or{' '}
+ {event.missing_users[0].includes(',') ? 'have' : 'has'} Viewer role)
+
+ )}
)}
{event.source && — source: {event.source}}