ignore shift swap generation for gcal events with specific keyword in title (#4228)

# What this PR does

Related to
[this](https://raintank-corp.slack.com/archives/C0229FD3CE9/p1713193452179019)
internal feature request

## 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] Added the relevant release notes label (see labels prefixed w/
`release:`). These labels dictate how your PR will
    show up in the autogenerated release notes.

---------

Co-authored-by: Matias Bordese <mbordese@gmail.com>
This commit is contained in:
Joey Orlando 2024-04-16 13:56:41 -04:00 committed by GitHub
parent 1b944605a7
commit 5a8d2baa28
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 48 additions and 10 deletions

View file

@ -109,6 +109,11 @@ under the Google Calendar tab. Once linked, you can further configure things, su
like us to consider for automatic shift swap generation (by default we will consider all of the schedules that you
are involved in).
### Ignoring events
If you would like to have Grafana OnCall ignore a specific Out of Office event from being considered for
Shift Swap Request generation, simply add `#grafana-oncall-ignore` to the Out of Office event's title.
### Configuring for open source
1. Follow the instructions [here](https://developers.google.com/identity/protocols/oauth2) to setup your Google OAuth2

View file

@ -1,3 +1,5 @@
EVENT_SUMMARY_IGNORE_KEYWORD = "#grafana-oncall-ignore"
GOOGLE_CALENDAR_EVENT_DATETIME_FORMAT = "%Y-%m-%dT%H:%M:%S%z"
"""
https://stackoverflow.com/a/17159470/3902555

View file

@ -2,6 +2,7 @@ import logging
from celery.utils.log import get_task_logger
from apps.google import constants
from apps.google.client import GoogleCalendarAPIClient
from apps.google.models import GoogleOAuth2User
from apps.schedules.models import OnCallSchedule, ShiftSwapRequest
@ -31,7 +32,10 @@ def sync_out_of_office_calendar_events_for_user(google_oauth2_user_pk: int) -> N
users_schedules = users_schedules.filter(public_primary_key__in=oncall_schedules_to_consider_for_shift_swaps)
for out_of_office_event in google_api_client.fetch_out_of_office_events():
event_id = out_of_office_event.raw_event["id"]
raw_event = out_of_office_event.raw_event
event_title = raw_event["summary"]
event_id = raw_event["id"]
start_time_utc = out_of_office_event.start_time_utc
end_time_utc = out_of_office_event.end_time_utc
@ -40,6 +44,13 @@ def sync_out_of_office_calendar_events_for_user(google_oauth2_user_pk: int) -> N
f"{end_time_utc} for user {user_id}"
)
if constants.EVENT_SUMMARY_IGNORE_KEYWORD in event_title.lower():
logger.info(
f"Skipping out of office event {event_id} because it contains the ignore keyword "
f"'{constants.EVENT_SUMMARY_IGNORE_KEYWORD}'"
)
continue
for schedule in users_schedules:
_, current_shifts, upcoming_shifts = schedule.shifts_for_user(
user,

View file

@ -8,7 +8,9 @@ from apps.google import constants, tasks
from apps.schedules.models import CustomOnCallShift, OnCallScheduleWeb, ShiftSwapRequest
def _create_mock_google_calendar_event(start_time: datetime.datetime, end_time: datetime.datetime):
def _create_mock_google_calendar_event(
start_time: datetime.datetime, end_time: datetime.datetime, summary="Out of office"
):
return {
"colorId": "4",
"created": "2024-03-22T23:06:39.000Z",
@ -50,7 +52,7 @@ def _create_mock_google_calendar_event(start_time: datetime.datetime, end_time:
"timeZone": "America/New_York",
},
"status": "confirmed",
"summary": "Out of office",
"summary": summary,
"updated": "2024-03-22T23:06:44.299Z",
"visibility": "public",
}
@ -156,11 +158,25 @@ def test_sync_out_of_office_calendar_events_for_user_no_ooo_events(mock_google_a
@patch("apps.google.client.build")
@pytest.mark.parametrize(
"out_of_office_event_title,should_create_ssr",
[
("Out of office", True),
("Out of office grafana-oncall-ignore", True),
("Out of office #grafana-oncall-ignore", False),
("Out of office #GRAFANA-ONCALL-IGNORE", False),
],
)
@pytest.mark.django_db
def test_sync_out_of_office_calendar_events_for_user_single_ooo_event(mock_google_api_client_build, test_setup):
def test_sync_out_of_office_calendar_events_for_user_single_ooo_event(
mock_google_api_client_build,
test_setup,
out_of_office_event_title,
should_create_ssr,
):
start_time, end_time = _create_event_start_and_end_times()
out_of_office_events = [
_create_mock_google_calendar_event(start_time, end_time),
_create_mock_google_calendar_event(start_time, end_time, out_of_office_event_title),
]
mock_google_api_client_build.return_value.events.return_value.list.return_value.execute.return_value = {
@ -173,13 +189,17 @@ def test_sync_out_of_office_calendar_events_for_user_single_ooo_event(mock_googl
tasks.sync_out_of_office_calendar_events_for_user(google_oauth2_user.pk)
ssrs = ShiftSwapRequest.objects.filter(beneficiary=user, schedule=schedule)
ssr = ssrs.first()
assert ssrs.count() == 1
if should_create_ssr:
assert ssrs.count() == 1
assert ssr.swap_start == start_time
assert ssr.swap_end == end_time
assert ssr.description == f"{user.name} will be out of office during this time according to Google Calendar"
ssr = ssrs.first()
assert ssr.swap_start == start_time
assert ssr.swap_end == end_time
assert ssr.description == f"{user.name} will be out of office during this time according to Google Calendar"
else:
assert ssrs.count() == 0
@patch("apps.google.client.build")