0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-04-05 05:15:26 +00:00

Update transport classes to use Transport.last_ping() consistently

* Instead of check.n_pings (int) use last_ping().n
* Instead of check.last_ping (datetime) use last_ping().created

There is a time gap from creating a flip object to processing
it (sending out an alert). We want the notification to reflect
the check's state at the moment the flip was created. To do this,
we use the Transport.last_ping() helper method which retrieves
the last ping *that is not newer than the flip*.

This commit updates transport classes and templates to use
Transport.last_ping() consistently everywhere.
This commit is contained in:
Pēteris Caune 2024-04-15 15:09:17 +03:00
parent d8a46349a8
commit 83f161d657
No known key found for this signature in database
GPG key ID: E28D7679E9A9EDE2
49 changed files with 264 additions and 140 deletions

View file

@ -9,7 +9,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
try:
@ -30,9 +30,14 @@ class NotifyAppriseTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "apprise"
self.channel.value = "123"
@ -56,7 +61,7 @@ class NotifyAppriseTestCase(BaseTestCase):
body = mock_apprise.return_value.notify.call_args.kwargs["body"]
self.assertIn("Foo is DOWN", body)
self.assertIn("Last ping was an hour ago.", body)
self.assertIn("Last ping was 10 minutes ago.", body)
@patch("apprise.Apprise")
@override_settings(APPRISE_ENABLED=False)
@ -69,8 +74,7 @@ class NotifyAppriseTestCase(BaseTestCase):
@patch("apprise.Apprise")
@override_settings(APPRISE_ENABLED=True)
def test_it_handles_no_last_ping(self, mock_apprise: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_aobj = Mock()
mock_aobj.add.return_value = True

View file

@ -24,7 +24,7 @@ class NotifyCallTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.channel = Channel(project=self.project)

View file

@ -19,11 +19,12 @@ class NotifyDiscordTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=61)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
@ -54,6 +55,7 @@ class NotifyDiscordTestCase(BaseTestCase):
fields = {f["title"]: f["value"] for f in attachment["fields"]}
self.assertEqual(fields["Last Ping"], "Success, an hour ago")
self.assertEqual(fields["Total Pings"], "112233")
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_rewrites_discordapp_com(self, mock_post: Mock) -> None:

View file

@ -27,12 +27,12 @@ class NotifyEmailTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.n_pings = 112233
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.n = 1
self.ping.created = now() - td(minutes=61)
self.ping.n = 112233
self.ping.remote_addr = "1.2.3.4"
self.ping.body_raw = b"Body Line 1\nBody Line 2"
self.ping.save()
@ -103,6 +103,10 @@ class NotifyEmailTestCase(BaseTestCase):
self.assertIn("112233", email.body)
self.assertIn("112233", html)
# Last ping time
self.assertIn("an hour ago", email.body)
self.assertIn("an hour ago", html)
# Last ping body
self.assertIn("Body Line 1\nBody Line 2", email.body)
self.assertIn("Body Line 1<br>Body Line 2", html)
@ -136,7 +140,7 @@ class NotifyEmailTestCase(BaseTestCase):
code, n = get_object.call_args.args
self.assertEqual(code, str(self.check.code))
self.assertEqual(n, 1)
self.assertEqual(n, 112233)
def test_it_shows_cron_schedule(self) -> None:
self.check.kind = "cron"

View file

@ -8,7 +8,7 @@ from unittest.mock import Mock, patch
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -21,9 +21,14 @@ class NotifyGotidyTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "gotify"
self.channel.value = json.dumps({"url": "https://example.org", "token": "abc"})
@ -48,7 +53,7 @@ class NotifyGotidyTestCase(BaseTestCase):
payload = mock_post.call_args.kwargs["json"]
self.assertEqual(payload["title"], "Foo is DOWN")
self.assertIn(self.check.cloaked_url(), payload["message"])
self.assertIn("Last ping was an hour ago.", payload["message"])
self.assertIn("Last ping was 10 minutes ago.", payload["message"])
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_subpath(self, mock_post: Mock) -> None:
@ -137,8 +142,7 @@ class NotifyGotidyTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -22,7 +22,7 @@ class NotifyGroupTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.channel_email = Channel(project=self.project)

View file

@ -7,7 +7,7 @@ from unittest.mock import Mock, patch
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -20,9 +20,14 @@ class NotifyLineTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "linenotify"
self.channel.value = "fake-token"
@ -45,7 +50,7 @@ class NotifyLineTestCase(BaseTestCase):
params = mock_post.call_args.kwargs["params"]
self.assertEqual(headers["Authorization"], "Bearer fake-token")
self.assertIn("""The check "Foo" is DOWN""", params["message"])
self.assertIn("Last ping was an hour ago.", params["message"])
self.assertIn("Last ping was 10 minutes ago.", params["message"])
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_does_not_escape_message(self, mock_post: Mock) -> None:
@ -63,8 +68,7 @@ class NotifyLineTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -9,7 +9,7 @@ from urllib.parse import quote
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -25,9 +25,14 @@ class NotifyMatrixTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "matrix"
self.channel.value = "!foo:example.org"
@ -53,13 +58,12 @@ class NotifyMatrixTestCase(BaseTestCase):
payload = mock_post.call_args.kwargs["json"]
self.assertIn("Foo is DOWN.", payload["body"])
self.assertIn("Last ping was an hour ago.", payload["body"])
self.assertIn("Last ping was an hour ago.", payload["formatted_body"])
self.assertIn("Last ping was 10 minutes ago.", payload["body"])
self.assertIn("Last ping was 10 minutes ago.", payload["formatted_body"])
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -21,11 +21,12 @@ class NotifyMattermostTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=61)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
@ -54,6 +55,7 @@ class NotifyMattermostTestCase(BaseTestCase):
fields = {f["title"]: f["value"] for f in attachment["fields"]}
self.assertEqual(fields["Last Ping"], "Success, an hour ago")
self.assertEqual(fields["Total Pings"], "112233")
@override_settings(MATTERMOST_ENABLED=False)
def test_it_requires_mattermost_enabled(self) -> None:

View file

@ -21,11 +21,12 @@ class NotifyMsTeamsTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=61)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
@ -58,6 +59,7 @@ class NotifyMsTeamsTestCase(BaseTestCase):
facts = {f["name"]: f["value"] for f in payload["sections"][0]["facts"]}
self.assertEqual(facts["Last Ping:"], "Success, an hour ago")
self.assertEqual(facts["Total Pings:"], "112233")
# The payload should not contain check's code
serialized = json.dumps(payload)

View file

@ -23,11 +23,12 @@ class NotifyNtfyTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.n = 1
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.remote_addr = "1.2.3.4"
self.ping.save()
@ -61,8 +62,8 @@ class NotifyNtfyTestCase(BaseTestCase):
self.assertIn("Project: Alices Project", payload["message"])
self.assertIn("Tags: foo, bar", payload["message"])
self.assertIn("Period: 1 day", payload["message"])
self.assertIn("Total Pings: 123", payload["message"])
self.assertIn("Last Ping: Success, now", payload["message"])
self.assertIn("Total Pings: 112233", payload["message"])
self.assertIn("Last Ping: Success, 10 minutes ago", payload["message"])
self.assertEqual(payload["actions"][0]["url"], self.check.cloaked_url())
self.assertNotIn("All the other checks are up.", payload["message"])

View file

@ -9,7 +9,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -22,9 +22,14 @@ class NotifyOpsgenieTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "opsgenie"
self.channel.value = value
@ -49,8 +54,8 @@ class NotifyOpsgenieTestCase(BaseTestCase):
payload = mock_post.call_args.kwargs["json"]
self.assertIn("""The check "Foo" is DOWN.""", payload["message"])
self.assertIn("""The check "Foo" is DOWN.""", payload["description"])
self.assertIn("Last ping was an hour ago.", payload["description"])
self.assertIn("Last ping was an hour ago.", payload["note"])
self.assertIn("Last ping was 10 minutes ago.", payload["description"])
self.assertIn("Last ping was 10 minutes ago.", payload["note"])
@patch("hc.api.transports.curl.request", autospec=True)
def test_opsgenie_with_legacy_value(self, mock_post: Mock) -> None:
@ -124,8 +129,7 @@ class NotifyOpsgenieTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self._setup_data(json.dumps({"key": "123", "region": "us"}))
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 202
self.channel.notify(self.flip)

View file

@ -8,7 +8,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -21,9 +21,14 @@ class NotifyPagertreeTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "pagertree"
self.channel.value = "123"
@ -45,7 +50,7 @@ class NotifyPagertreeTestCase(BaseTestCase):
payload = mock_post.call_args.kwargs["json"]
self.assertEqual(payload["event_type"], "trigger")
self.assertIn("Foo is DOWN.", payload["description"])
self.assertIn("Last ping was an hour ago.", payload["description"])
self.assertIn("Last ping was 10 minutes ago.", payload["description"])
@override_settings(PAGERTREE_ENABLED=False)
def test_it_requires_pagertree_enabled(self) -> None:
@ -68,8 +73,7 @@ class NotifyPagertreeTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -9,7 +9,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -23,9 +23,14 @@ class NotifyPdTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "pd"
self.channel.value = value
@ -51,6 +56,8 @@ class NotifyPdTestCase(BaseTestCase):
self.assertEqual(payload["details"]["Description"], "Description goes here")
self.assertEqual(payload["event_type"], "trigger")
self.assertEqual(payload["service_key"], "123")
self.assertEqual(payload["details"]["Last ping"], "10 minutes ago")
self.assertEqual(payload["details"]["Total pings"], 112233)
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_shows_schedule_and_tz(self, mock_post: Mock) -> None:

View file

@ -7,7 +7,7 @@ from unittest.mock import Mock, patch
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -20,9 +20,14 @@ class NotifyPushbulletTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "pushbullet"
self.channel.value = "fake-token"
@ -47,7 +52,7 @@ class NotifyPushbulletTestCase(BaseTestCase):
payload = kwargs["json"]
self.assertEqual(payload["type"], "note")
self.assertIn("""The check "Foo" is DOWN.""", payload["body"])
self.assertIn("""Last ping was an hour ago.""", payload["body"])
self.assertIn("""Last ping was 10 minutes ago.""", payload["body"])
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_up(self, mock_post: Mock) -> None:
@ -79,8 +84,8 @@ class NotifyPushbulletTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.ping.delete()
self.check.status = "down"
self.check.last_ping = None
self.check.save()
mock_post.return_value.status_code = 200

View file

@ -8,7 +8,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification, TokenBucket
from hc.api.models import Channel, Check, Flip, Notification, Ping, TokenBucket
from hc.test import BaseTestCase
API = "https://api.pushover.net/1"
@ -24,9 +24,14 @@ class NotifyPushoverTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "po"
self.channel.value = value
@ -53,6 +58,9 @@ class NotifyPushoverTestCase(BaseTestCase):
payload = mock_post.call_args.kwargs["data"]
self.assertEqual(payload["title"], "🔴 Foo")
self.assertEqual(payload["url"], self.check.cloaked_url())
self.assertIn("112233", payload["message"])
self.assertIn("10 minutes ago", payload["message"])
# Only one check in the project, so there should be no note about
# other checks:
self.assertNotIn("All the other checks are up.", payload["message"])

View file

@ -20,11 +20,12 @@ class NotifyRocketChatTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=61)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
@ -54,6 +55,7 @@ class NotifyRocketChatTestCase(BaseTestCase):
attachment = mock_post.call_args.kwargs["json"]["attachments"][0]
fields = {f["title"]: f["value"] for f in attachment["fields"]}
self.assertEqual(fields["Last Ping"], "Success, an hour ago")
self.assertEqual(fields["Total Pings"], "112233")
@override_settings(ROCKETCHAT_ENABLED=False)
def test_it_requires_rocketchat_enabled(self) -> None:

View file

@ -81,12 +81,13 @@ class NotifySignalTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.n_pings = 123
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.n = 1
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.remote_addr = "1.2.3.4"
self.ping.body_raw = b"Body Line 1\nBody Line 2"
self.ping.save()
@ -127,8 +128,8 @@ class NotifySignalTestCase(BaseTestCase):
self.assertIn("Project: Alices Project", params["message"])
self.assertIn("Tags: foo, bar", params["message"])
self.assertIn("Period: 1 day", params["message"])
self.assertIn("Total Pings: 123", params["message"])
self.assertIn("Last Ping: Success, now", params["message"])
self.assertIn("Total Pings: 112233", params["message"])
self.assertIn("Last Ping: Success, 10 minutes ago", params["message"])
self.assertIn("+123456789", params["recipient"])
# Only one check in the project, so there should be no note about
@ -151,7 +152,7 @@ class NotifySignalTestCase(BaseTestCase):
assert socketobj.req
params = socketobj.req["params"]
self.assertIn("Last Ping: Exit status 123, now", params["message"])
self.assertIn("Last Ping: Exit status 123, 10 minutes ago", params["message"])
@patch("hc.api.transports.socket.socket")
def test_it_shows_schedule_and_tz(self, socket: Mock) -> None:

View file

@ -23,11 +23,12 @@ class NotifySlackTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=61)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
@ -60,6 +61,7 @@ class NotifySlackTestCase(BaseTestCase):
fields = {f["title"]: f["value"] for f in attachment["fields"]}
self.assertEqual(fields["Last Ping"], "Success, an hour ago")
self.assertEqual(fields["Total Pings"], "112233")
self.assertNotIn("Last Ping Body", fields)
# The payload should not contain check's code

View file

@ -10,7 +10,7 @@ from django.core import mail
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -24,9 +24,14 @@ class NotifySmsTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
spec = {"value": "+1234567890", "up": False, "down": True}
self.channel = Channel(project=self.project, kind="sms")
self.channel.value = json.dumps(spec)
@ -50,7 +55,7 @@ class NotifySmsTestCase(BaseTestCase):
self.assertEqual(payload["From"], "+000")
self.assertNotIn("\xa0", payload["Body"])
self.assertIn("""The check "Foo" is DOWN.""", payload["Body"])
self.assertIn("""Last ping was an hour ago.""", payload["Body"])
self.assertIn("""Last ping was 10 minutes ago.""", payload["Body"])
n = Notification.objects.get()
callback_path = f"/api/v3/notifications/{n.code}/status"
@ -175,8 +180,7 @@ class NotifySmsTestCase(BaseTestCase):
@override_settings(TWILIO_FROM="+000", TWILIO_MESSAGING_SERVICE_SID=None)
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -8,7 +8,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -21,9 +21,14 @@ class NotifySpikeTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "spike"
self.channel.value = "https://spike.example.org"
@ -46,7 +51,7 @@ class NotifySpikeTestCase(BaseTestCase):
self.assertEqual(payload["check_id"], str(self.check.code))
self.assertEqual(payload["title"], "Foo is DOWN")
self.assertIn("Foo is DOWN.", payload["message"])
self.assertIn("Last ping was an hour ago.", payload["message"])
self.assertIn("Last ping was 10 minutes ago.", payload["message"])
@override_settings(SPIKE_ENABLED=False)
def test_it_requires_spike_enabled(self) -> None:
@ -70,8 +75,7 @@ class NotifySpikeTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -22,12 +22,13 @@ class NotifyTelegramTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.n_pings = 1
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=61)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
@ -58,8 +59,8 @@ class NotifyTelegramTestCase(BaseTestCase):
self.assertIn("<b>Project:</b> Alices Project\n", payload["text"])
self.assertIn("<b>Tags:</b> foo, bar, baz\n", payload["text"])
self.assertIn("<b>Period:</b> 1 day\n", payload["text"])
self.assertIn("<b>Total Pings:</b> 1\n", payload["text"])
self.assertIn("<b>Last Ping:</b> Success, an hour ago", payload["text"])
self.assertIn("<b>Total Pings:</b> 112233\n", payload["text"])
self.assertIn("<b>Last Ping:</b> Success, 10 minutes ago", payload["text"])
# Only one check in the project, so there should be no note about
# other checks:
@ -76,7 +77,9 @@ class NotifyTelegramTestCase(BaseTestCase):
self.channel.notify(self.flip)
payload = mock_post.call_args.kwargs["json"]
self.assertIn("<b>Last Ping:</b> Exit status 123, an hour ago", payload["text"])
self.assertIn(
"<b>Last Ping:</b> Exit status 123, 10 minutes ago", payload["text"]
)
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_sends_to_thread(self, mock_post: Mock) -> None:

View file

@ -7,7 +7,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -21,9 +21,14 @@ class NotifyTrelloTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "trello"
self.channel.value = json.dumps(
@ -53,7 +58,7 @@ class NotifyTrelloTestCase(BaseTestCase):
self.assertEqual(params["idList"], "fake-list-id")
self.assertEqual(params["name"], "Down: Foo")
self.assertIn("Full Details", params["desc"])
self.assertIn("**Last Ping:** an hour ago", params["desc"])
self.assertIn("**Last Ping:** 10 minutes ago", params["desc"])
self.assertEqual(params["key"], "fake-trello-app-key")
self.assertEqual(params["token"], "fake-token")
@ -92,8 +97,7 @@ class NotifyTrelloTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -8,7 +8,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -21,9 +21,14 @@ class NotifyVictorOpsTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "victorops"
self.channel.value = "123"
@ -45,7 +50,7 @@ class NotifyVictorOpsTestCase(BaseTestCase):
payload = mock_post.call_args.kwargs["json"]
self.assertEqual(payload["message_type"], "CRITICAL")
self.assertIn("Foo is DOWN.", payload["state_message"])
self.assertIn("Last ping was an hour ago.", payload["state_message"])
self.assertIn("Last ping was 10 minutes ago.", payload["state_message"])
@override_settings(VICTOROPS_ENABLED=False)
def test_it_requires_victorops_enabled(self) -> None:
@ -89,8 +94,7 @@ class NotifyVictorOpsTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -22,11 +22,12 @@ class NotifyWebhookTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = self.check.last_ping
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.body_raw = b"Body Line 1\nBody Line 2"
self.ping.exitstatus = 123
self.ping.save()

View file

@ -32,7 +32,7 @@ class NotifyWhatsAppTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
definition = {"value": "+1234567890", "up": True, "down": True}

View file

@ -9,7 +9,7 @@ from unittest.mock import Mock, patch
from django.test.utils import override_settings
from django.utils.timezone import now
from hc.api.models import Channel, Check, Flip, Notification
from hc.api.models import Channel, Check, Flip, Notification, Ping
from hc.test import BaseTestCase
@ -22,9 +22,14 @@ class NotifyZulipTestCase(BaseTestCase):
# Transport classes should use flip.new_status,
# so the status "paused" should not appear anywhere
self.check.status = "paused"
self.check.last_ping = now() - td(minutes=61)
self.check.last_ping = now()
self.check.save()
self.ping = Ping(owner=self.check)
self.ping.created = now() - td(minutes=10)
self.ping.n = 112233
self.ping.save()
self.channel = Channel(project=self.project)
self.channel.kind = "zulip"
self.channel.value = json.dumps(self.definition())
@ -59,7 +64,7 @@ class NotifyZulipTestCase(BaseTestCase):
payload = mock_post.call_args.kwargs["data"]
self.assertEqual(payload["topic"], "Foobar is DOWN")
self.assertIn("is **DOWN**.", payload["content"])
self.assertIn("Last ping was an hour ago.", payload["content"])
self.assertIn("Last ping was 10 minutes ago.", payload["content"])
# payload should not contain check's code
serialized = json.dumps(payload)
@ -137,8 +142,7 @@ class NotifyZulipTestCase(BaseTestCase):
@patch("hc.api.transports.curl.request", autospec=True)
def test_it_handles_no_last_ping(self, mock_post: Mock) -> None:
self.check.last_ping = None
self.check.save()
self.ping.delete()
mock_post.return_value.status_code = 200
self.channel.notify(self.flip)

View file

@ -463,13 +463,13 @@ class Slackalike(HttpTransport):
fields.add("Schedule", fix_asterisks(check.schedule))
fields.add("Time Zone", check.tz)
fields.add("Total Pings", str(check.n_pings))
if ping := self.last_ping(flip):
fields.add("Total Pings", str(ping.n))
created_str = naturaltime(ping.created)
formatted_kind = ping.get_kind_display()
fields.add("Last Ping", f"{formatted_kind}, {created_str}")
else:
fields.add("Total Pings", "0")
fields.add("Last Ping", "Never")
body = get_ping_body(ping, maxlen=1000)
@ -553,10 +553,11 @@ class Opsgenie(HttpTransport):
payload: JSONDict = {"alias": str(check.code), "source": settings.SITE_NAME}
if flip.new_status == "down":
ctx = {"check": check, "ping": self.last_ping(flip)}
payload["tags"] = cast(JSONValue, check.tags_list())
payload["message"] = tmpl("opsgenie_message.html", check=check)
payload["note"] = tmpl("opsgenie_note.html", check=check)
payload["description"] = tmpl("opsgenie_description.html", check=check)
payload["message"] = tmpl("opsgenie_message.html", **ctx)
payload["note"] = tmpl("opsgenie_note.html", **ctx)
payload["description"] = tmpl("opsgenie_description.html", **ctx)
url = "https://api.opsgenie.com/v2/alerts"
if self.channel.opsgenie.region == "eu":
@ -576,10 +577,11 @@ class PagerDuty(HttpTransport):
raise TransportError("PagerDuty notifications are not enabled.")
check = flip.owner
ping = self.last_ping(flip)
details = {
"Project": check.project.name,
"Total pings": check.n_pings,
"Last ping": tmpl("pd_last_ping.html", check=check),
"Total pings": ping.n if ping else 0,
"Last ping": tmpl("pd_last_ping.html", ping=ping),
}
if check.desc:
details["Description"] = check.desc
@ -612,7 +614,11 @@ class PagerTree(HttpTransport):
url = self.channel.value
headers = {"Content-Type": "application/json"}
ctx = {"check": flip.owner, "status": flip.new_status}
ctx = {
"check": flip.owner,
"status": flip.new_status,
"ping": self.last_ping(flip),
}
payload = {
"incident_key": str(flip.owner.code),
"event_type": "trigger" if flip.new_status == "down" else "resolve",
@ -628,12 +634,17 @@ class PagerTree(HttpTransport):
class Pushbullet(HttpTransport):
def notify(self, flip: Flip, notification: Notification) -> None:
text = tmpl("pushbullet_message.html", check=flip.owner, status=flip.new_status)
url = "https://api.pushbullet.com/v2/pushes"
headers = {
"Access-Token": self.channel.value,
"Content-Type": "application/json",
}
text = tmpl(
"pushbullet_message.html",
check=flip.owner,
status=flip.new_status,
ping=self.last_ping(flip),
)
payload = {"type": "note", "title": settings.SITE_NAME, "body": text}
self.post(url, json=payload, headers=headers)
@ -754,9 +765,8 @@ class RocketChat(HttpTransport):
fields.add("Schedule", fix_asterisks(check.schedule))
fields.add("Time Zone", check.tz)
fields.add("Total Pings", str(check.n_pings))
if ping := self.last_ping(flip):
fields.add("Total Pings", str(ping.n))
created_str = naturaltime(ping.created)
formatted_kind = ping.get_kind_display()
fields.add("Last Ping", f"{formatted_kind}, {created_str}")
@ -766,6 +776,7 @@ class RocketChat(HttpTransport):
text = f"{body_size} {bytes_str}, [show body]({ping_url})"
fields.add("Last Ping Body", text)
else:
fields.add("Total Pings", "0")
fields.add("Last Ping", "Never")
return result
@ -788,7 +799,11 @@ class VictorOps(HttpTransport):
if not settings.VICTOROPS_ENABLED:
raise TransportError("Splunk On-Call notifications are not enabled.")
ctx = {"check": flip.owner, "status": flip.new_status}
ctx = {
"check": flip.owner,
"status": flip.new_status,
"ping": self.last_ping(flip),
}
mtype = "CRITICAL" if flip.new_status == "down" else "RECOVERY"
payload = {
"entity_id": str(flip.owner.code),
@ -812,7 +827,11 @@ class Matrix(HttpTransport):
return url
def notify(self, flip: Flip, notification: Notification) -> None:
ctx = {"check": flip.owner, "status": flip.new_status}
ctx = {
"check": flip.owner,
"status": flip.new_status,
"ping": self.last_ping(flip),
}
plain = tmpl("matrix_description.html", **ctx)
formatted = tmpl("matrix_description_formatted.html", **ctx)
payload = {
@ -943,6 +962,7 @@ class Sms(HttpTransport):
"sms_message.html",
check=flip.owner,
status=flip.new_status,
ping=self.last_ping(flip),
site_name=settings.SITE_NAME,
)
@ -1085,10 +1105,15 @@ class Trello(HttpTransport):
if not settings.TRELLO_APP_KEY:
raise TransportError("Trello notifications are not enabled.")
ctx = {
"check": flip.owner,
"status": flip.new_status,
"ping": self.last_ping(flip),
}
params = {
"idList": self.channel.trello.list_id,
"name": tmpl("trello_name.html", check=flip.owner, status=flip.new_status),
"desc": tmpl("trello_desc.html", check=flip.owner),
"name": tmpl("trello_name.html", **ctx),
"desc": tmpl("trello_desc.html", **ctx),
"key": settings.TRELLO_APP_KEY,
"token": self.channel.trello.token,
}
@ -1103,9 +1128,9 @@ class Apprise(HttpTransport):
raise TransportError("Apprise is disabled and/or not installed")
a = apprise.Apprise()
check, status = flip.owner, flip.new_status
check, status, ping = flip.owner, flip.new_status, self.last_ping(flip)
title = tmpl("apprise_title.html", check=check, status=status)
body = tmpl("apprise_description.html", check=check, status=status)
body = tmpl("apprise_description.html", check=check, status=status, ping=ping)
a.add(self.channel.value)
@ -1150,12 +1175,12 @@ class MsTeams(HttpTransport):
facts.append({"name": "Schedule:", "value": fix_asterisks(check.schedule)})
facts.append({"name": "Time Zone:", "value": check.tz})
facts.append({"name": "Total Pings:", "value": str(check.n_pings)})
if ping := self.last_ping(flip):
facts.append({"name": "Total Pings:", "value": str(ping.n)})
text = f"{ping.get_kind_display()}, {naturaltime(ping.created)}"
facts.append({"name": "Last Ping:", "value": text})
else:
facts.append({"name": "Total Pings:", "value": "0"})
facts.append({"name": "Last Ping:", "value": "Never"})
body = get_ping_body(ping, maxlen=1000)
@ -1197,7 +1222,12 @@ class Zulip(HttpTransport):
url = self.channel.zulip.site + "/api/v1/messages"
auth = (self.channel.zulip.bot_email, self.channel.zulip.api_key)
content = tmpl("zulip_content.html", check=flip.owner, status=flip.new_status)
content = tmpl(
"zulip_content.html",
check=flip.owner,
status=flip.new_status,
ping=self.last_ping(flip),
)
data = {
"type": self.channel.zulip.mtype,
"to": self.channel.zulip.to,
@ -1215,7 +1245,11 @@ class Spike(HttpTransport):
url = self.channel.value
headers = {"Content-Type": "application/json"}
ctx = {"check": flip.owner, "status": flip.new_status}
ctx = {
"check": flip.owner,
"status": flip.new_status,
"ping": self.last_ping(flip),
}
payload = {
"check_id": str(flip.owner.code),
"title": tmpl("spike_title.html", **ctx),
@ -1234,7 +1268,12 @@ class LineNotify(HttpTransport):
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": "Bearer %s" % self.channel.linenotify_token,
}
msg = tmpl("linenotify_message.html", check=flip.owner, status=flip.new_status)
ctx = {
"check": flip.owner,
"status": flip.new_status,
"ping": self.last_ping(flip),
}
msg = tmpl("linenotify_message.html", **ctx)
self.post(self.URL, headers=headers, params={"message": msg})
@ -1406,6 +1445,7 @@ class Gotify(HttpTransport):
ctx = {
"check": flip.owner,
"status": flip.new_status,
"ping": self.last_ping(flip),
"down_checks": self.down_checks(flip.owner),
}
payload = {

View file

@ -58,7 +58,7 @@
<td style="padding-right: 32px; padding-bottom: 8px; vertical-align: top;">
<b>Total Pings</b><br>
{{ check.n_pings }}
{% if ping %}{{ ping.n }}{% else %}0{% endif %}
{% if check.created %}(since {{ check.created|date:'M j, Y' }}){% endif %}
</td>
</tr>

View file

@ -34,7 +34,7 @@
{% line %}Time Zone: {{ check.tz }}{% endline %}
{% endif %}
{% line %}Total pings: {{ check.n_pings }} {% if check.created %}(since {{ check.created|date:'M j, Y' }}){% endif %}{% endline %}
{% line %}Total pings: {% if ping %}{{ ping.n }}{% else %}0{% endif %} {% if check.created %}(since {{ check.created|date:'M j, Y' }}){% endif %}{% endline %}
{% if ping %}
{% line %}Last ping: {{ ping.created|naturaltime }}{% if ping.remote_addr %}, from {{ ping.remote_addr }}{% endif %}{% endline %}

View file

@ -1,5 +1,5 @@
{% load humanize %}
{{ check.name_then_code }} is {{ status|upper }}.
{% if status == "down" and check.last_ping %}
Last ping was {{ check.last_ping|naturaltime }}.
{% if status == "down" and ping %}
Last ping was {{ ping.created|naturaltime }}.
{% endif %}

View file

@ -1,6 +1,6 @@
{% load humanize linemode %}{% linemode %}
{% if status == "down" %}
{% line %}🔴 The check [{{ check.name_then_code }}]({{ check.cloaked_url }}) is **DOWN**.{% if check.last_ping %} Last ping was {{ check.last_ping|naturaltime }}.{% endif %}{% endline %}
{% line %}🔴 The check [{{ check.name_then_code }}]({{ check.cloaked_url }}) is **DOWN**.{% if ping %} Last ping was {{ ping.created|naturaltime }}.{% endif %}{% endline %}
{% else %}
{% line %}🟢 The check [{{ check.name_then_code }}]({{ check.cloaked_url }}) is now **UP**.{% endline %}
{% endif %}

View file

@ -1,6 +1,6 @@
{% load humanize %}
{% if status == "down" %}
The check "{{ check.name_then_code|safe }}" is DOWN.{% if check.last_ping %} Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
The check "{{ check.name_then_code|safe }}" is DOWN.{% if ping %} Last ping was {{ ping.created|naturaltime }}.{% endif %}
{% else %}
The check "{{ check.name_then_code|safe }}" is now UP.
{% endif %}

View file

@ -1,6 +1,6 @@
{% load humanize %}
{% if status == "down" %}
{{ check.name_then_code }} is DOWN.{% if check.last_ping %} Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
{{ check.name_then_code }} is DOWN.{% if ping %} Last ping was {{ ping.created|naturaltime }}.{% endif %}
{% else %}
{{ check.name_then_code }} is now UP.
{% endif %}

View file

@ -1,6 +1,6 @@
{% load humanize %}
{% if status == "down" %}
🔴 <b>{{ check.name_then_code }}</b> is <b>DOWN</b>.{% if check.last_ping %} Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
🔴 <b>{{ check.name_then_code }}</b> is <b>DOWN</b>.{% if ping %} Last ping was {{ ping.created|naturaltime }}.{% endif %}
{% else %}
🟢 <b>{{ check.name_then_code }}</b> is now <b>UP</b>.
{% endif %}

View file

@ -17,7 +17,7 @@
{% line %}Time Zone: {{ check.tz }}{% endline %}
{% endif %}
{% line %}Total Pings: {{ check.n_pings }}{% endline %}
{% line %}Total Pings: {% if ping %}{{ ping.n }}{% else %}0{% endif %}{% endline %}
{% if ping is None %}
{% line %}Last Ping: Never{% endline %}

View file

@ -1,4 +1,4 @@
{% load hc_extras humanize %}
The check "{{ check.name_then_code }}" is DOWN.
{% if check.kind == "simple" %}Expecting to receive a ping every {{ check.timeout|hc_duration }}.{% endif %}
{% if check.last_ping %}Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
{% if ping %}Last ping was {{ ping.created|naturaltime }}.{% endif %}

View file

@ -1,4 +1,4 @@
{% load hc_extras humanize %}
{% if check.kind == "simple" %}Expecting to receive a ping every {{ check.timeout|hc_duration }}.{% endif %}
{% if check.last_ping %}Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
{% if ping %}Last ping was {{ ping.created|naturaltime }}.{% endif %}

View file

@ -1,5 +1,5 @@
{% load humanize %}
{{ check.name_then_code }} is {{ status|upper }}.
{% if status == "down" and check.last_ping %}
Last ping was {{ check.last_ping|naturaltime }}.
{% if status == "down" and ping %}
Last ping was {{ ping.created|naturaltime }}.
{% endif %}

View file

@ -1,6 +1,6 @@
{% load humanize %}
{% if check.last_ping %}
{{ check.last_ping|naturaltime }}
{% if ping %}
{{ ping.created|naturaltime }}
{% else %}
Never
{% endif %}

View file

@ -1,7 +1,7 @@
{% load humanize %}
{% if status == "down" %}
The check "{{ check.name_then_code|safe }}" is DOWN.{% if check.last_ping %} Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
The check "{{ check.name_then_code|safe }}" is DOWN.{% if ping %} Last ping was {{ ping.created|naturaltime }}.{% endif %}
{% else %}
The check "{{ check.name_then_code|safe }}" received a ping and is now UP.
{% endif %}

View file

@ -24,7 +24,7 @@
{% line %}<b>Time Zone:</b> {{ check.tz }}{% endline %}
{% endif %}
{% line %}<b>Total Pings:</b> {{ check.n_pings }}{% endline %}
{% line %}<b>Total Pings:</b> {% if ping %}{{ ping.n }}{% else %}0{% endif %}{% endline %}
{% if ping is None %}
{% line %}<b>Last Ping:</b> Never{% endline %}

View file

@ -24,7 +24,7 @@
{% line %}<b>Time Zone:</b> {{ check.tz }}{% endline %}
{% endif %}
{% line %}<b>Total Pings:</b> {{ check.n_pings }}{% endline %}
{% line %}<b>Total Pings:</b> {% if ping %}{{ ping.n }}{% else %}0{% endif %}{% endline %}
{% if ping is None %}
{% line %}<b>Last Ping:</b> Never{% endline %}

View file

@ -1,6 +1,6 @@
{% load humanize %}
{% if status == "down" %}
{{ site_name }}: The check "{{ check.name_then_code|safe }}" is DOWN.{% if check.last_ping %} Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
{{ site_name }}: The check "{{ check.name_then_code|safe }}" is DOWN.{% if ping %} Last ping was {{ ping.created|naturaltime }}.{% endif %}
{% else %}
{{ site_name }}: The check "{{ check.name_then_code|safe }}" is UP.
{% endif %}

View file

@ -1,5 +1,5 @@
{% load humanize %}
{{ check.name_then_code|safe }} is {{ status|upper }}.
{% if status == "down" and check.last_ping %}
Last ping was {{ check.last_ping|naturaltime }}.
{% if status == "down" and ping %}
Last ping was {{ ping.created|naturaltime }}.
{% endif %}

View file

@ -23,7 +23,7 @@
{% line %}<b>Time Zone:</b> {{ check.tz }}{% endline %}
{% endif %}
{% line %}<b>Total Pings:</b> {{ check.n_pings }}{% endline %}
{% line %}<b>Total Pings:</b> {% if ping %}{{ ping.n }}{% else %}0{% endif %}{% endline %}
{% if ping is None %}
{% line %}<b>Last Ping:</b> Never{% endline %}

View file

@ -11,6 +11,6 @@
**Time Zone:** {{ check.tz }}
{% endif %}
**Last Ping:** {{ check.last_ping|naturaltime|default:"never" }}
**Last Ping:** {% if ping %}{{ ping.created|naturaltime }}{% else %}never{% endif %}
[Full Details @ {% site_name %}]({{ check.cloaked_url }})

View file

@ -1,7 +1,7 @@
{% load humanize %}
{% if status == "down" %}
{{ check.name_then_code|safe }} is DOWN.
{% if check.last_ping %}Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
{% if ping %}Last ping was {{ ping.created|naturaltime }}.{% endif %}
{% else %}
{{ check.name_then_code|safe }} received a ping and is now UP
{% endif %}

View file

@ -1,6 +1,6 @@
{% load hc_extras humanize %}
[{{ check.name_then_code }}]({{ check.cloaked_url }}) is **{{ status|upper }}**. {% if status == "down" and check.last_ping %}Last ping was {{ check.last_ping|naturaltime }}.{% endif %}
[{{ check.name_then_code }}]({{ check.cloaked_url }}) is **{{ status|upper }}**. {% if status == "down" and ping %}Last ping was {{ ping.created|naturaltime }}.{% endif %}
{% if check.desc %}
---