diff --git a/hc/api/management/commands/sendalerts.py b/hc/api/management/commands/sendalerts.py index 5da666d4..09fc8920 100644 --- a/hc/api/management/commands/sendalerts.py +++ b/hc/api/management/commands/sendalerts.py @@ -66,6 +66,11 @@ class Command(BaseCommand): q = Check.objects.filter(id=check.id, status=check.status) current_status = check.get_status() + + # During the grace period sendalerts considers the check as "up": + if current_status == "grace": + current_status = "up" + if check.status == current_status: # Stored status is already up-to-date. Update alert_after # as needed but don't send notifications diff --git a/hc/api/models.py b/hc/api/models.py index 1dff7948..969c41a4 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -128,7 +128,15 @@ class Check(models.Model): if now is None: now = timezone.now() - return "up" if self.get_grace_start() + self.grace > now else "down" + grace_start = self.get_grace_start() + grace_end = grace_start + self.grace + if now >= grace_end: + return "down" + + if now >= grace_start: + return "grace" + + return "up" def get_alert_after(self): """ Return the datetime when check potentially goes down. """ @@ -140,19 +148,6 @@ class Check(models.Model): return self.get_grace_start() + self.grace - def in_grace_period(self): - """ Return True if check is currently in grace period. """ - - if self.status in ("new", "paused"): - return False - - if self.last_ping_was_fail: - return False - - grace_start = self.get_grace_start() - grace_end = grace_start + self.grace - return grace_start < timezone.now() < grace_end - def assign_all_channels(self): if self.user: channels = Channel.objects.filter(user=self.user) diff --git a/hc/api/tests/test_check_model.py b/hc/api/tests/test_check_model.py index a5ab9e54..8a72980a 100644 --- a/hc/api/tests/test_check_model.py +++ b/hc/api/tests/test_check_model.py @@ -16,37 +16,26 @@ class CheckModelTestCase(TestCase): check.tags = " " self.assertEqual(check.tags_list(), []) - def test_in_grace_period_handles_new_check(self): + def test_get_status_handles_new_check(self): check = Check() - self.assertFalse(check.in_grace_period()) - - def test_in_grace_period_handles_fail_ping(self): - check = Check() - check.status = "up" - check.last_ping = timezone.now() - timedelta(days=1, minutes=30) - check.last_ping_was_fail = True - - # If last ping was signalling a failure, we're not in grace period, - # we're down - self.assertFalse(check.in_grace_period()) + self.assertEqual(check.get_status(), "new") def test_status_works_with_grace_period(self): check = Check() check.status = "up" check.last_ping = timezone.now() - timedelta(days=1, minutes=30) - self.assertTrue(check.in_grace_period()) - self.assertEqual(check.get_status(), "up") + self.assertEqual(check.get_status(), "grace") - def test_paused_check_is_not_in_grace_period(self): + def test_get_stauts_handles_paused_check(self): check = Check() check.status = "up" check.last_ping = timezone.now() - timedelta(days=1, minutes=30) - self.assertTrue(check.in_grace_period()) + self.assertEqual(check.get_status(), "grace") check.status = "paused" - self.assertFalse(check.in_grace_period()) + self.assertEqual(check.get_status(), "paused") def test_status_works_with_cron_syntax(self): dt = timezone.make_aware(datetime(2000, 1, 1), timezone=timezone.utc) @@ -58,12 +47,16 @@ class CheckModelTestCase(TestCase): check.status = "up" check.last_ping = dt - # 00:30am - now = dt + timedelta(days=1, minutes=30) + # 23:59pm + now = dt + timedelta(hours=23, minutes=59) self.assertEqual(check.get_status(now), "up") + # 00:00am + now = dt + timedelta(days=1) + self.assertEqual(check.get_status(now), "grace") + # 1:30am - now = dt + timedelta(days=1, minutes=90) + now = dt + timedelta(days=1, minutes=60) self.assertEqual(check.get_status(now), "down") def test_status_works_with_timezone(self): @@ -78,11 +71,15 @@ class CheckModelTestCase(TestCase): check.tz = "Australia/Brisbane" # UTC+10 # 10:30am - now = dt + timedelta(days=1, minutes=30) + now = dt + timedelta(hours=23, minutes=59) self.assertEqual(check.get_status(now), "up") + # 10:30am + now = dt + timedelta(days=1) + self.assertEqual(check.get_status(now), "grace") + # 11:30am - now = dt + timedelta(days=1, minutes=90) + now = dt + timedelta(days=1, minutes=60) self.assertEqual(check.get_status(now), "down") def test_next_ping_with_cron_syntax(self): diff --git a/hc/api/views.py b/hc/api/views.py index 609daa0d..0c0b4a51 100644 --- a/hc/api/views.py +++ b/hc/api/views.py @@ -177,10 +177,11 @@ def badge(request, username, signature, tag, format="svg"): if tag != "*" and tag not in check.tags_list(): continue - if status == "up" and check.in_grace_period(): + check_status = check.get_status() + if status == "up" and check_status == "grace": status = "late" - if check.get_status() == "down": + if check_status == "down": status = "down" break diff --git a/hc/front/views.py b/hc/front/views.py index 4b8a4800..e12ebfeb 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -37,11 +37,13 @@ VALID_SORT_VALUES = ("name", "-name", "last_ping", "-last_ping", "created") def _tags_statuses(checks): tags, down, grace, num_down = {}, {}, {}, 0 for check in checks: - if check.get_status() == "down": + status = check.get_status() + + if status == "down": num_down += 1 for tag in check.tags_list(): down[tag] = "down" - elif check.in_grace_period(): + elif status == "grace": for tag in check.tags_list(): grace[tag] = "grace" else: @@ -92,12 +94,10 @@ def status(request): details = [] tmpl = get_template("front/last_ping_cell.html") for check in checks: - status = "grace" if check.in_grace_period() else check.get_status() - ctx = {"check": check} details.append({ "code": str(check.code), - "status": status, + "status": check.get_status(), "last_ping": tmpl.render(ctx) }) diff --git a/templates/emails/summary-html.html b/templates/emails/summary-html.html index f9df7a31..a80a7248 100644 --- a/templates/emails/summary-html.html +++ b/templates/emails/summary-html.html @@ -17,7 +17,7 @@