Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update shift week_start when translating to UTC #2134

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion engine/apps/api/serializers/on_call_shifts.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ class OnCallShiftSerializer(EagerLoadingMixin, serializers.ModelSerializer):
shift_start = serializers.DateTimeField(source="start")
shift_end = serializers.SerializerMethodField()
by_day = serializers.ListField(required=False, allow_null=True)
week_start = serializers.CharField(required=False, allow_null=True)
rolling_users = RollingUsersField(
allow_null=True,
required=False,
Expand Down Expand Up @@ -51,6 +52,7 @@ class Meta:
"frequency",
"interval",
"by_day",
"week_start",
"source",
"rolling_users",
"updated_shift",
Expand All @@ -65,6 +67,11 @@ class Meta:
def get_shift_end(self, obj):
return obj.start + obj.duration

def to_representation(self, instance):
ret = super().to_representation(instance)
ret["week_start"] = CustomOnCallShift.ICAL_WEEKDAY_MAP[instance.week_start]
return ret

def to_internal_value(self, data):
data["source"] = CustomOnCallShift.SOURCE_WEB
if not data.get("shift_end"):
Expand All @@ -80,6 +87,15 @@ def validate_by_day(self, by_day):
raise serializers.ValidationError(["Invalid day value."])
return by_day

def validate_week_start(self, week_start):
if week_start is None:
week_start = CustomOnCallShift.MONDAY

if week_start not in CustomOnCallShift.WEB_WEEKDAY_MAP:
raise serializers.ValidationError(["Invalid week start value."])

return CustomOnCallShift.ICAL_WEEKDAY_REVERSE_MAP[week_start]

def validate_interval(self, interval):
if interval is not None:
if not isinstance(interval, int) or interval <= 0:
Expand Down Expand Up @@ -171,7 +187,7 @@ def _correct_validated_data(self, event_type, validated_data):
if validated_data.get("schedule"):
validated_data["team"] = validated_data["schedule"].team

validated_data["week_start"] = CustomOnCallShift.MONDAY
validated_data["week_start"] = validated_data.get("week_start", CustomOnCallShift.MONDAY)

return validated_data

Expand Down
17 changes: 14 additions & 3 deletions engine/apps/api/tests/test_oncall_shift.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def test_create_on_call_shift_rotation(on_call_shift_internal_api_setup, make_us
CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.MONDAY],
CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.FRIDAY],
],
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.MONDAY],
"rolling_users": [[user1.public_primary_key], [user2.public_primary_key]],
}

Expand Down Expand Up @@ -86,6 +87,7 @@ def test_create_on_call_shift_override(on_call_shift_internal_api_setup, make_us
"id": response.data["id"],
"updated_shift": None,
"rolling_users": returned_rolling_users,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.MONDAY],
}

assert response.status_code == status.HTTP_201_CREATED
Expand Down Expand Up @@ -130,6 +132,7 @@ def test_get_on_call_shift(
"frequency": None,
"interval": None,
"by_day": None,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.SUNDAY],
"rolling_users": [[user1.public_primary_key], [user2.public_primary_key]],
"updated_shift": None,
}
Expand Down Expand Up @@ -180,6 +183,7 @@ def test_list_on_call_shift(
"frequency": None,
"interval": None,
"by_day": None,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.SUNDAY],
"rolling_users": [[user1.public_primary_key], [user2.public_primary_key]],
"updated_shift": None,
}
Expand Down Expand Up @@ -237,6 +241,7 @@ def test_list_on_call_shift_filter_schedule_id(
"frequency": None,
"interval": None,
"by_day": None,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.SUNDAY],
"rolling_users": [[user1.public_primary_key], [user2.public_primary_key]],
"updated_shift": None,
}
Expand Down Expand Up @@ -318,6 +323,7 @@ def test_update_future_on_call_shift(
"frequency": None,
"interval": None,
"by_day": None,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.MONDAY],
"rolling_users": [[user1.public_primary_key]],
"updated_shift": None,
}
Expand Down Expand Up @@ -385,6 +391,7 @@ def test_update_started_on_call_shift(
"frequency": None,
"interval": None,
"by_day": None,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.MONDAY],
"rolling_users": [[user1.public_primary_key]],
"updated_shift": None,
}
Expand Down Expand Up @@ -413,19 +420,19 @@ def test_update_started_on_call_shift_force_update(
client = APIClient()
start_date = (timezone.now() - timezone.timedelta(hours=1)).replace(microsecond=0)

title = "Test Shift Rotation"
name = "Test Shift Rotation"
on_call_shift = make_on_call_shift(
schedule.organization,
shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT,
schedule=schedule,
title=title,
name=name,
start=start_date,
duration=timezone.timedelta(hours=3),
rotation_start=start_date,
rolling_users=[{user1.pk: user1.public_primary_key}],
)
data_to_update = {
"title": title,
"name": name,
"priority_level": 2,
"shift_start": start_date.strftime("%Y-%m-%dT%H:%M:%SZ"),
"shift_end": (start_date + timezone.timedelta(hours=1)).strftime("%Y-%m-%dT%H:%M:%SZ"),
Expand All @@ -434,6 +441,7 @@ def test_update_started_on_call_shift_force_update(
"frequency": None,
"interval": None,
"by_day": None,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.SUNDAY],
"rolling_users": [[user1.public_primary_key]],
}

Expand Down Expand Up @@ -525,6 +533,7 @@ def test_update_old_on_call_shift_with_future_version(
"type": CustomOnCallShift.TYPE_ROLLING_USERS_EVENT,
"schedule": schedule.public_primary_key,
"updated_shift": None,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.MONDAY],
}

assert response.status_code == status.HTTP_200_OK
Expand Down Expand Up @@ -579,6 +588,7 @@ def test_update_started_on_call_shift_name(
"frequency": None,
"interval": None,
"by_day": None,
"week_start": "MO",
"rolling_users": [[user1.public_primary_key]],
}

Expand All @@ -593,6 +603,7 @@ def test_update_started_on_call_shift_name(
"type": CustomOnCallShift.TYPE_ROLLING_USERS_EVENT,
"schedule": schedule.public_primary_key,
"updated_shift": None,
"week_start": CustomOnCallShift.ICAL_WEEKDAY_MAP[CustomOnCallShift.MONDAY],
}

assert response.status_code == status.HTTP_200_OK
Expand Down
10 changes: 8 additions & 2 deletions grafana-plugin/src/containers/RotationForm/RotationForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,13 @@ import { Schedule, Shift } from 'models/schedule/schedule.types';
import { getTzOffsetString } from 'models/timezone/timezone.helpers';
import { Timezone } from 'models/timezone/timezone.types';
import { User } from 'models/user/user.types';
import { getDateTime, getStartOfWeek, getUTCByDay, getUTCString } from 'pages/schedule/Schedule.helpers';
import {
getDateTime,
getStartOfWeek,
getUTCByDay,
getUTCString,
getUTCWeekStart,
} from 'pages/schedule/Schedule.helpers';
import { useStore } from 'state/useStore';
import { getCoords, waitForElement } from 'utils/DOM';
import { GRAFANA_HEADER_HEIGTH } from 'utils/consts';
Expand Down Expand Up @@ -204,7 +210,7 @@ const RotationForm2 = observer((props: RotationForm2Props) => {
interval: repeatEveryValue,
frequency: repeatEveryPeriod,
by_day: getUTCByDay(store.scheduleStore.byDayOptions, selectedDays, shiftStart),

week_start: getUTCWeekStart(store.scheduleStore.byDayOptions, shiftStart),
priority_level: shiftId === 'new' ? layerPriority : shift?.priority_level,
title: rotationTitle,
}),
Expand Down
1 change: 1 addition & 0 deletions grafana-plugin/src/models/schedule/schedule.types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export interface CreateScheduleExportTokenResponse {

export interface Shift {
by_day: string[];
week_start: string;
frequency: number | null;
id: string;
interval: number;
Expand Down
20 changes: 20 additions & 0 deletions grafana-plugin/src/pages/schedule/Schedule.helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,26 @@ export const getUTCByDay = (dayOptions: SelectOption[], by_day: string[], moment
return by_day;
};

export const getUTCWeekStart = (dayOptions: SelectOption[], moment: dayjs.Dayjs) => {
let week_start_index = 0;
let byDayOptions = [];
dayOptions.forEach(({ value }) => byDayOptions.push(value));
if (moment.day() !== moment.utc().day()) {
// when converting to UTC, shift starts on a different day,
// so we may need to change when week starts based on the UTC start time
// depending on the UTC side, move one day before or after
let offset = moment.utcOffset();
if (offset < 0) {
// move one day after
week_start_index = (week_start_index + 1) % 7;
} else {
// move one day before
week_start_index = (((week_start_index - 1) % 7) + 7) % 7;
}
}
return byDayOptions[week_start_index];
};

export const getColorSchemeMappingForUsers = (
store: RootStore,
scheduleId: string,
Expand Down