Refactor gaps and empty shift checks (#3785)

Refactor gaps and empty shift checks:
- Increase checking gaps and empty shifts frequency
- Unify gaps and empty shift checks
This commit is contained in:
Yulya Artyukhina 2024-01-31 15:25:06 +01:00 committed by GitHub
parent 801f1ad028
commit 16ce0136f3
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
18 changed files with 353 additions and 96 deletions

View file

@ -14,6 +14,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Changed
- Remove `/oncall` Slack slash command (ie. manual alert group creation command) by @joeyorlando ([#3790](https://github.com/grafana/oncall/pull/3790))
- Increase frequency of checking for gaps and empty shifts in schedules by @Ferril ([#3785](https://github.com/grafana/oncall/pull/3785))
### Fixed

View file

@ -88,9 +88,8 @@ class ScheduleBaseSerializer(EagerLoadingMixin, serializers.ModelSerializer):
def create(self, validated_data):
created_schedule = super().create(validated_data)
created_schedule.check_empty_shifts_for_next_week()
created_schedule.check_gaps_and_empty_shifts_for_next_week()
schedule_notify_about_empty_shifts_in_schedule.apply_async((created_schedule.pk,))
created_schedule.check_gaps_for_next_week()
schedule_notify_about_gaps_in_schedule.apply_async((created_schedule.pk,))
return created_schedule

View file

@ -57,8 +57,7 @@ class ScheduleCalendarCreateSerializer(ScheduleCalendarSerializer):
or old_enable_web_overrides != updated_enable_web_overrides
):
updated_schedule.drop_cached_ical()
updated_schedule.check_empty_shifts_for_next_week()
updated_schedule.check_gaps_for_next_week()
updated_schedule.check_gaps_and_empty_shifts_for_next_week()
schedule_notify_about_empty_shifts_in_schedule.apply_async((instance.pk,))
schedule_notify_about_gaps_in_schedule.apply_async((instance.pk,))
return updated_schedule

View file

@ -86,8 +86,7 @@ class ScheduleICalUpdateSerializer(ScheduleICalCreateSerializer):
if old_ical_url_primary != updated_ical_url_primary or old_ical_url_overrides != updated_ical_url_overrides:
updated_schedule.drop_cached_ical()
updated_schedule.check_empty_shifts_for_next_week()
updated_schedule.check_gaps_for_next_week()
updated_schedule.check_gaps_and_empty_shifts_for_next_week()
schedule_notify_about_empty_shifts_in_schedule.apply_async((instance.pk,))
schedule_notify_about_gaps_in_schedule.apply_async((instance.pk,))
# for iCal-based schedules we need to refresh final schedule information

View file

@ -40,8 +40,7 @@ class ScheduleWebCreateSerializer(ScheduleWebSerializer):
updated_time_zone = updated_schedule.time_zone
if old_time_zone != updated_time_zone:
updated_schedule.drop_cached_ical()
updated_schedule.check_empty_shifts_for_next_week()
updated_schedule.check_gaps_for_next_week()
updated_schedule.check_gaps_and_empty_shifts_for_next_week()
schedule_notify_about_empty_shifts_in_schedule.apply_async((instance.pk,))
schedule_notify_about_gaps_in_schedule.apply_async((instance.pk,))
return updated_schedule

View file

@ -455,8 +455,7 @@ class ScheduleView(
def reload_ical(self, request, pk):
schedule = self.get_object(annotate=False)
schedule.drop_cached_ical()
schedule.check_empty_shifts_for_next_week()
schedule.check_gaps_for_next_week()
schedule.check_gaps_and_empty_shifts_for_next_week()
if schedule.user_group is not None:
update_slack_user_group_for_schedules.apply_async((schedule.user_group.pk,))

View file

@ -19,6 +19,7 @@ from django.utils.functional import cached_property
from icalendar.cal import Event
from apps.schedules.tasks import (
check_gaps_and_empty_shifts_in_schedule,
drop_cached_ical_task,
refresh_ical_final_schedule,
schedule_notify_about_empty_shifts_in_schedule,
@ -692,6 +693,7 @@ class CustomOnCallShift(models.Model):
schedule = self.schedule.get_real_instance()
schedule.refresh_ical_file()
refresh_ical_final_schedule.apply_async((schedule.pk,))
check_gaps_and_empty_shifts_in_schedule.apply_async((schedule.pk,))
def start_drop_ical_and_check_schedule_tasks(self, schedule):
drop_cached_ical_task.apply_async((schedule.pk,))

View file

@ -34,6 +34,7 @@ from apps.schedules.constants import (
ICAL_UID,
)
from apps.schedules.ical_utils import (
EmptyShifts,
create_base_icalendar,
fetch_ical_file_or_get_error,
get_oncall_users_for_multiple_schedules,
@ -278,22 +279,34 @@ class OnCallSchedule(PolymorphicModel):
(self.prev_ical_file_overrides, self.cached_ical_file_overrides),
]
def check_gaps_for_next_week(self) -> bool:
def check_gaps_and_empty_shifts_for_next_week(self) -> None:
datetime_start = timezone.now()
datetime_end = datetime_start + datetime.timedelta(days=7)
# get empty shifts from all events and gaps from final events
events = self.filter_events(
datetime_start,
datetime_end,
with_empty=True,
with_gap=True,
all_day_datetime=True,
)
has_empty_shifts = len([event for event in events if event["is_empty"]]) != 0
final_events = self._resolve_schedule(events, datetime_start, datetime_end)
has_gaps = len([final_event for final_event in final_events if final_event["is_gap"]]) != 0
if has_gaps != self.has_gaps or has_empty_shifts != self.has_empty_shifts:
self.has_gaps = has_gaps
self.has_empty_shifts = has_empty_shifts
self.save(update_fields=["has_gaps", "has_empty_shifts"])
def get_gaps_for_next_week(self) -> ScheduleEvents:
today = timezone.now()
events = self.final_events(today, today + datetime.timedelta(days=7))
gaps = [event for event in events if event["is_gap"] and not event["is_empty"]]
has_gaps = len(gaps) != 0
self.has_gaps = has_gaps
self.save(update_fields=["has_gaps"])
return has_gaps
return [event for event in events if event["is_gap"]]
def check_empty_shifts_for_next_week(self):
def get_empty_shifts_for_next_week(self) -> EmptyShifts:
today = timezone.now().date()
empty_shifts = list_of_empty_shifts_in_schedule(self, today, today + datetime.timedelta(days=7))
has_empty_shifts = len(empty_shifts) != 0
self.has_empty_shifts = has_empty_shifts
self.save(update_fields=["has_empty_shifts"])
return has_empty_shifts
return list_of_empty_shifts_in_schedule(self, today, today + datetime.timedelta(days=7))
def drop_cached_ical(self):
self._drop_primary_ical_file()

View file

@ -1,3 +1,4 @@
from .check_gaps_and_empty_shifts import check_gaps_and_empty_shifts_in_schedule # noqa: F401
from .drop_cached_ical import drop_cached_ical_for_custom_events_for_organization, drop_cached_ical_task # noqa: F401
from .notify_about_empty_shifts_in_schedule import ( # noqa: F401
check_empty_shifts_in_schedule,

View file

@ -0,0 +1,23 @@
from celery.utils.log import get_task_logger
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
task_logger = get_task_logger(__name__)
@shared_dedicated_queue_retry_task()
def check_gaps_and_empty_shifts_in_schedule(schedule_pk):
from apps.schedules.models import OnCallSchedule
task_logger.info(f"Start check_gaps_and_empty_shifts_in_schedule {schedule_pk}")
try:
schedule = OnCallSchedule.objects.get(
pk=schedule_pk,
)
except OnCallSchedule.DoesNotExist:
task_logger.info(f"Tried to check_gaps_and_empty_shifts_in_schedule for non-existing schedule {schedule_pk}")
return
schedule.check_gaps_and_empty_shifts_for_next_week()
task_logger.info(f"Finish check_gaps_and_empty_shifts_in_schedule {schedule_pk}")

View file

@ -3,7 +3,6 @@ from celery.utils.log import get_task_logger
from django.core.cache import cache
from django.utils import timezone
from apps.schedules.ical_utils import list_of_empty_shifts_in_schedule
from apps.slack.utils import format_datetime_to_slack_with_time, post_message_to_channel
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
from common.utils import trim_if_needed
@ -11,36 +10,16 @@ from common.utils import trim_if_needed
task_logger = get_task_logger(__name__)
# deprecated # todo: delete this task from here and from task routes after the next release
@shared_dedicated_queue_retry_task()
def start_check_empty_shifts_in_schedule():
from apps.schedules.models import OnCallSchedule
task_logger.info("Start start_notify_about_empty_shifts_in_schedule")
schedules = OnCallSchedule.objects.all()
for schedule in schedules:
check_empty_shifts_in_schedule.apply_async((schedule.pk,))
task_logger.info("Finish start_notify_about_empty_shifts_in_schedule")
return
# deprecated # todo: delete this task from here and from task routes after the next release
@shared_dedicated_queue_retry_task()
def check_empty_shifts_in_schedule(schedule_pk):
from apps.schedules.models import OnCallSchedule
task_logger.info(f"Start check_empty_shifts_in_schedule {schedule_pk}")
try:
schedule = OnCallSchedule.objects.get(
pk=schedule_pk,
)
except OnCallSchedule.DoesNotExist:
task_logger.info(f"Tried to check_empty_shifts_in_schedule for non-existing schedule {schedule_pk}")
return
schedule.check_empty_shifts_for_next_week()
task_logger.info(f"Finish check_empty_shifts_in_schedule {schedule_pk}")
return
@shared_dedicated_queue_retry_task()
@ -54,6 +33,7 @@ def start_notify_about_empty_shifts_in_schedule():
schedules = OnCallScheduleICal.objects.filter(
empty_shifts_report_sent_at__lte=week_ago,
channel__isnull=False,
organization__deleted_at__isnull=True,
)
for schedule in schedules:
@ -79,9 +59,8 @@ def notify_about_empty_shifts_in_schedule_task(schedule_pk):
task_logger.info(f"Tried to notify_about_empty_shifts_in_schedule_task for non-existing schedule {schedule_pk}")
return
today = timezone.now().date()
empty_shifts = list_of_empty_shifts_in_schedule(schedule, today, today + timezone.timedelta(days=7))
schedule.empty_shifts_report_sent_at = today
empty_shifts = schedule.get_empty_shifts_for_next_week()
schedule.empty_shifts_report_sent_at = timezone.now().date()
if len(empty_shifts) != 0:
schedule.has_empty_shifts = True

View file

@ -1,5 +1,3 @@
import datetime
import pytz
from celery.utils.log import get_task_logger
from django.core.cache import cache
@ -11,36 +9,16 @@ from common.custom_celery_tasks import shared_dedicated_queue_retry_task
task_logger = get_task_logger(__name__)
# deprecated # todo: delete this task from here and from task routes after the next release
@shared_dedicated_queue_retry_task()
def start_check_gaps_in_schedule():
from apps.schedules.models import OnCallSchedule
task_logger.info("Start start_check_gaps_in_schedule")
schedules = OnCallSchedule.objects.all()
for schedule in schedules:
check_gaps_in_schedule.apply_async((schedule.pk,))
task_logger.info("Finish start_check_gaps_in_schedule")
return
# deprecated # todo: delete this task from here and from task routes after the next release
@shared_dedicated_queue_retry_task()
def check_gaps_in_schedule(schedule_pk):
from apps.schedules.models import OnCallSchedule
task_logger.info(f"Start check_gaps_in_schedule {schedule_pk}")
try:
schedule = OnCallSchedule.objects.get(
pk=schedule_pk,
)
except OnCallSchedule.DoesNotExist:
task_logger.info(f"Tried to check_gaps_in_schedule for non-existing schedule {schedule_pk}")
return
schedule.check_gaps_for_next_week()
task_logger.info(f"Finish check_gaps_in_schedule {schedule_pk}")
return
@shared_dedicated_queue_retry_task()
@ -54,6 +32,7 @@ def start_notify_about_gaps_in_schedule():
schedules = OnCallSchedule.objects.filter(
gaps_report_sent_at__lte=week_ago,
channel__isnull=False,
organization__deleted_at__isnull=True,
)
for schedule in schedules:
@ -80,10 +59,8 @@ def notify_about_gaps_in_schedule_task(schedule_pk):
task_logger.info(f"Tried to notify_about_gaps_in_schedule_task for non-existing schedule {schedule_pk}")
return
now = timezone.now()
events = schedule.final_events(now, now + datetime.timedelta(days=7))
gaps = [event for event in events if event["is_gap"] and not event["is_empty"]]
schedule.gaps_report_sent_at = now.date()
gaps = schedule.get_gaps_for_next_week()
schedule.gaps_report_sent_at = timezone.now().date()
if len(gaps) != 0:
schedule.has_gaps = True

View file

@ -2,7 +2,11 @@ from celery.utils.log import get_task_logger
from apps.alerts.tasks import notify_ical_schedule_shift # type: ignore[no-redef]
from apps.schedules.ical_utils import is_icals_equal, update_cached_oncall_users_for_schedule
from apps.schedules.tasks import notify_about_empty_shifts_in_schedule_task, notify_about_gaps_in_schedule_task
from apps.schedules.tasks import (
check_gaps_and_empty_shifts_in_schedule,
notify_about_empty_shifts_in_schedule_task,
notify_about_gaps_in_schedule_task,
)
from apps.slack.tasks import start_update_slack_user_group_for_schedules
from common.custom_celery_tasks import shared_dedicated_queue_retry_task
@ -84,6 +88,9 @@ def refresh_ical_file(schedule_pk):
# update cached schedule on-call users
update_cached_oncall_users_for_schedule(schedule)
check_gaps_and_empty_shifts_in_schedule.apply_async((schedule_pk,))
# todo: refactor tasks below to unify checking and notifying about gaps and empty shifts to avoid doing the same
# todo: work twice.
if run_task:
notify_about_empty_shifts_in_schedule_task.apply_async((schedule_pk,))
notify_about_gaps_in_schedule_task.apply_async((schedule_pk,))

View file

@ -0,0 +1,269 @@
import datetime
import pytest
from django.utils import timezone
from apps.api.permissions import LegacyAccessControlRole
from apps.schedules.models import CustomOnCallShift, OnCallScheduleWeb
@pytest.mark.django_db
def test_no_empty_shifts_no_gaps(
make_organization_and_user_with_slack_identities,
make_user,
make_schedule,
make_on_call_shift,
):
organization, _, _, _ = make_organization_and_user_with_slack_identities()
user1 = make_user(organization=organization, username="user1")
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb, name="test_schedule")
now = timezone.now().replace(microsecond=0)
start_date = now - datetime.timedelta(days=7, minutes=1)
data = {
"start": start_date,
"rotation_start": start_date,
"duration": datetime.timedelta(seconds=3600 * 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([[user1]])
schedule.refresh_ical_file()
schedule.check_gaps_and_empty_shifts_for_next_week()
schedule.refresh_from_db()
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is False
@pytest.mark.django_db
def test_no_empty_shifts_but_gaps_now(
make_organization_and_user_with_slack_identities,
make_user,
make_schedule,
make_on_call_shift,
):
organization, _, _, _ = make_organization_and_user_with_slack_identities()
user1 = make_user(organization=organization, username="user1")
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb, name="test_schedule")
now = timezone.now().replace(microsecond=0)
start_date = now - datetime.timedelta(days=1, minutes=1)
data = {
"start": start_date,
"rotation_start": start_date,
"duration": datetime.timedelta(seconds=3600 * 24),
"priority_level": 1,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
"schedule": schedule,
"interval": 2,
}
on_call_shift = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data
)
on_call_shift.add_rolling_users([[user1]])
schedule.refresh_ical_file()
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is False
schedule.check_gaps_and_empty_shifts_for_next_week()
schedule.refresh_from_db()
assert schedule.has_gaps is True
assert schedule.has_empty_shifts is False
@pytest.mark.django_db
def test_empty_shifts_no_gaps(
make_organization_and_user_with_slack_identities,
make_user,
make_schedule,
make_on_call_shift,
):
organization, _, _, _ = make_organization_and_user_with_slack_identities()
user1 = make_user(organization=organization, username="user1", role=LegacyAccessControlRole.VIEWER)
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb, name="test_schedule")
now = timezone.now().replace(microsecond=0)
start_date = now - datetime.timedelta(days=7, minutes=1)
data = {
"start": start_date,
"rotation_start": start_date,
"duration": datetime.timedelta(seconds=3600 * 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([[user1]])
schedule.refresh_ical_file()
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is False
schedule.check_gaps_and_empty_shifts_for_next_week()
schedule.refresh_from_db()
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is True
@pytest.mark.django_db
def test_empty_shifts_and_gaps(
make_organization_and_user_with_slack_identities,
make_user,
make_schedule,
make_on_call_shift,
):
organization, _, _, _ = make_organization_and_user_with_slack_identities()
user1 = make_user(organization=organization, username="user1", role=LegacyAccessControlRole.VIEWER)
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb, name="test_schedule")
now = timezone.now().replace(microsecond=0)
start_date = now - datetime.timedelta(days=7, minutes=1)
data = {
"start": start_date,
"rotation_start": start_date,
"duration": datetime.timedelta(seconds=3600 * 24),
"priority_level": 1,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
"schedule": schedule,
"interval": 2,
}
on_call_shift = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data
)
on_call_shift.add_rolling_users([[user1]])
schedule.refresh_ical_file()
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is False
schedule.check_gaps_and_empty_shifts_for_next_week()
schedule.refresh_from_db()
assert schedule.has_gaps is True
assert schedule.has_empty_shifts is True
@pytest.mark.django_db
def test_empty_shifts_and_gaps_in_the_past(
make_organization_and_user_with_slack_identities,
make_user,
make_schedule,
make_on_call_shift,
):
organization, _, _, _ = make_organization_and_user_with_slack_identities()
user1 = make_user(organization=organization, username="user1", role=LegacyAccessControlRole.VIEWER)
user2 = make_user(organization=organization, username="user2", role=LegacyAccessControlRole.ADMIN)
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb, name="test_schedule")
now = timezone.now().replace(microsecond=0)
start_date = now - datetime.timedelta(days=7, minutes=1)
until = start_date + datetime.timedelta(days=5, minutes=1)
data = {
"start": start_date,
"rotation_start": start_date,
"duration": datetime.timedelta(seconds=3600 * 24),
"priority_level": 1,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
"schedule": schedule,
"interval": 2,
"until": until,
}
on_call_shift = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data
)
on_call_shift.add_rolling_users([[user1]])
start_date2 = now - datetime.timedelta(days=4, minutes=1)
data2 = {
"start": start_date2,
"rotation_start": start_date2,
"duration": datetime.timedelta(seconds=3600 * 24),
"priority_level": 1,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
"schedule": schedule,
}
on_call_shift2 = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data2
)
on_call_shift2.add_rolling_users([[user2]])
schedule.refresh_ical_file()
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is False
schedule.check_gaps_and_empty_shifts_for_next_week()
schedule.refresh_from_db()
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is False
@pytest.mark.django_db
def test_empty_shifts_and_gaps_in_the_future(
make_organization_and_user_with_slack_identities,
make_user,
make_schedule,
make_on_call_shift,
):
organization, _, _, _ = make_organization_and_user_with_slack_identities()
user1 = make_user(organization=organization, username="user1", role=LegacyAccessControlRole.VIEWER)
user2 = make_user(organization=organization, username="user2", role=LegacyAccessControlRole.ADMIN)
schedule = make_schedule(organization, schedule_class=OnCallScheduleWeb, name="test_schedule")
# empty shift with gaps starts in 7 days 1 min
now = timezone.now().replace(microsecond=0)
start_date = now + datetime.timedelta(days=7, minutes=1)
data = {
"start": start_date,
"rotation_start": start_date,
"duration": datetime.timedelta(seconds=3600 * 24),
"priority_level": 1,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
"schedule": schedule,
"interval": 2,
}
on_call_shift = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data
)
on_call_shift.add_rolling_users([[user1]])
# normal shift ends in 7 days 1 min
start_date2 = now - datetime.timedelta(days=7, minutes=1)
until = now + datetime.timedelta(days=7, minutes=1)
data2 = {
"start": start_date2,
"rotation_start": start_date2,
"duration": datetime.timedelta(seconds=3600 * 24),
"priority_level": 1,
"frequency": CustomOnCallShift.FREQUENCY_DAILY,
"schedule": schedule,
"until": until,
}
on_call_shift2 = make_on_call_shift(
organization=organization, shift_type=CustomOnCallShift.TYPE_ROLLING_USERS_EVENT, **data2
)
on_call_shift2.add_rolling_users([[user2]])
schedule.refresh_ical_file()
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is False
schedule.check_gaps_and_empty_shifts_for_next_week()
schedule.refresh_from_db()
# no gaps and empty shifts in the next 7 days
assert schedule.has_gaps is False
assert schedule.has_empty_shifts is False

View file

@ -53,6 +53,7 @@ def test_no_empty_shifts_no_triggering_notification(
schedule.refresh_from_db()
assert empty_shifts_report_sent_at != schedule.empty_shifts_report_sent_at
assert schedule.has_empty_shifts is False
@pytest.mark.django_db

View file

@ -52,7 +52,7 @@ def test_no_gaps_no_triggering_notification(
schedule.refresh_from_db()
assert gaps_report_sent_at != schedule.gaps_report_sent_at
assert schedule.check_gaps_for_next_week() is False
assert schedule.has_gaps is False
@pytest.mark.django_db
@ -115,7 +115,7 @@ def test_gaps_in_the_past_no_triggering_notification(
schedule.refresh_from_db()
assert gaps_report_sent_at != schedule.gaps_report_sent_at
assert schedule.check_gaps_for_next_week() is False
assert schedule.has_gaps is False
@pytest.mark.django_db
@ -166,7 +166,6 @@ def test_gaps_now_trigger_notification(
schedule.refresh_from_db()
assert gaps_report_sent_at != schedule.gaps_report_sent_at
assert schedule.has_gaps is True
assert schedule.check_gaps_for_next_week() is True
@pytest.mark.django_db
@ -218,7 +217,6 @@ def test_gaps_near_future_trigger_notification(
schedule.refresh_from_db()
assert gaps_report_sent_at != schedule.gaps_report_sent_at
assert schedule.has_gaps is True
assert schedule.check_gaps_for_next_week() is True
@pytest.mark.django_db
@ -267,4 +265,4 @@ def test_gaps_later_than_7_days_no_triggering_notification(
schedule.refresh_from_db()
assert gaps_report_sent_at != schedule.gaps_report_sent_at
assert schedule.check_gaps_for_next_week() is False
assert schedule.has_gaps is False

View file

@ -506,21 +506,11 @@ CELERY_BEAT_SCHEDULE = {
"schedule": crontab(minute=1, hour=12, day_of_week="monday"),
"args": (),
},
"start_check_gaps_in_schedule": {
"task": "apps.schedules.tasks.notify_about_gaps_in_schedule.start_check_gaps_in_schedule",
"schedule": crontab(minute=0, hour=0),
"args": (),
},
"start_notify_about_empty_shifts_in_schedule": {
"task": "apps.schedules.tasks.notify_about_empty_shifts_in_schedule.start_notify_about_empty_shifts_in_schedule",
"schedule": crontab(minute=0, hour=12, day_of_week="monday"),
"args": (),
},
"start_check_empty_shifts_in_schedule": {
"task": "apps.schedules.tasks.notify_about_empty_shifts_in_schedule.start_check_empty_shifts_in_schedule",
"schedule": crontab(minute=0, hour=0),
"args": (),
},
"populate_slack_usergroups": {
"task": "apps.slack.tasks.populate_slack_usergroups",
"schedule": crontab(minute=0, hour=9, day_of_week="monday,wednesday,friday"),

View file

@ -30,6 +30,7 @@ CELERY_TASK_ROUTES = {
"apps.schedules.tasks.refresh_ical_files.start_refresh_ical_files": {"queue": "default"},
"apps.schedules.tasks.refresh_ical_files.refresh_ical_final_schedule": {"queue": "default"},
"apps.schedules.tasks.refresh_ical_files.start_refresh_ical_final_schedules": {"queue": "default"},
"apps.schedules.tasks.check_gaps_and_empty_shifts.check_gaps_and_empty_shifts_in_schedule": {"queue": "default"},
"apps.schedules.tasks.notify_about_gaps_in_schedule.check_empty_shifts_in_schedule": {"queue": "default"},
"apps.schedules.tasks.notify_about_gaps_in_schedule.start_notify_about_gaps_in_schedule": {"queue": "default"},
"apps.schedules.tasks.notify_about_gaps_in_schedule.check_gaps_in_schedule": {"queue": "default"},