diff --git a/CHANGELOG.md b/CHANGELOG.md index e93c2b2a..b2fc4171 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,8 @@ All notable changes to this project will be documented in this file. ## Unreleased ### Improvements -- Show the number of outages and total downtime in monthly reports. (#104) +- Show the number of downtimes and total downtime minutes in monthly reports (#104) +- Show the number of downtimes and total downtime minutes in "Check Details" page ## 1.8.0 - 2019-07-08 @@ -13,8 +14,8 @@ All notable changes to this project will be documented in this file. - Add the `prunetokenbucket` management command - Show check counts in JSON "badges" (#251) - Webhooks support HTTP PUT (#249) -- Webhooks can use different req. bodies and headers for "up" and "down" events. (#249) -- Show check's code instead of full URL on 992px - 1200px wide screens. (#253) +- Webhooks can use different req. bodies and headers for "up" and "down" events (#249) +- Show check's code instead of full URL on 992px - 1200px wide screens (#253) - Add WhatsApp integration (uses Twilio same as the SMS integration) - Webhooks support the $TAGS placeholder - Don't include ping URLs in API responses when the read-only key is used @@ -59,7 +60,7 @@ All notable changes to this project will be documented in this file. ### Improvements - Database schema: add uniqueness constraint to Check.code -- Database schema: add Ping.kind field. Remove "start" and "fail" fields. +- Database schema: add Ping.kind field. Remove "start" and "fail" fields - Add "Email Settings..." dialog and "Subject Must Contain" setting - Database schema: add the Project model - Move project-specific settings to a new "Project Settings" page diff --git a/hc/api/models.py b/hc/api/models.py index d043dd00..d3f648b6 100644 --- a/hc/api/models.py +++ b/hc/api/models.py @@ -246,8 +246,8 @@ class Check(models.Model): ping.body = body[:10000] ping.save() - def outages_by_month(self, months=2): - """ Calculate the number of outages and downtime minutes per month. + def downtimes(self, months=2): + """ Calculate the number of downtimes and downtime minutes per month. Returns a list of (datetime, downtime_in_secs, number_of_outages) tuples. diff --git a/hc/api/tests/test_check_model.py b/hc/api/tests/test_check_model.py index b1315cf1..8b74067b 100644 --- a/hc/api/tests/test_check_model.py +++ b/hc/api/tests/test_check_model.py @@ -166,24 +166,24 @@ class CheckModelTestCase(BaseTestCase): d = check.to_dict() self.assertEqual(d["next_ping"], "2000-01-01T01:00:00+00:00") - def test_outages_by_month_handles_no_flips(self): + def test_downtimes_handles_no_flips(self): check = Check.objects.create(project=self.project) - r = check.outages_by_month(10) + r = check.downtimes(10) self.assertEqual(len(r), 10) for dt, downtime, outages in r: self.assertEqual(downtime.total_seconds(), 0) self.assertEqual(outages, 0) - def test_outages_by_month_handles_currently_down_check(self): + def test_downtimes_handles_currently_down_check(self): check = Check.objects.create(project=self.project, status="down") - r = check.outages_by_month(10) + r = check.downtimes(10) self.assertEqual(len(r), 10) for dt, downtime, outages in r: self.assertEqual(outages, 1) @patch("hc.api.models.timezone.now") - def test_outages_by_month_handles_flip_one_day_ago(self, mock_now): + def test_downtimes_handles_flip_one_day_ago(self, mock_now): mock_now.return_value = datetime(2019, 7, 19, tzinfo=timezone.utc) check = Check.objects.create(project=self.project, status="down") @@ -193,7 +193,7 @@ class CheckModelTestCase(BaseTestCase): flip.new_status = "down" flip.save() - r = check.outages_by_month(10) + r = check.downtimes(10) self.assertEqual(len(r), 10) for dt, downtime, outages in r: if dt.month == 7: @@ -204,7 +204,7 @@ class CheckModelTestCase(BaseTestCase): self.assertEqual(outages, 0) @patch("hc.api.models.timezone.now") - def test_outages_by_month_handles_flip_two_months_ago(self, mock_now): + def test_downtimes_handles_flip_two_months_ago(self, mock_now): mock_now.return_value = datetime(2019, 7, 19, tzinfo=timezone.utc) check = Check.objects.create(project=self.project, status="down") @@ -214,7 +214,7 @@ class CheckModelTestCase(BaseTestCase): flip.new_status = "down" flip.save() - r = check.outages_by_month(10) + r = check.downtimes(10) self.assertEqual(len(r), 10) for dt, downtime, outages in r: if dt.month == 7: diff --git a/hc/front/views.py b/hc/front/views.py index 3f5649e0..398cc1db 100644 --- a/hc/front/views.py +++ b/hc/front/views.py @@ -58,6 +58,7 @@ VALID_SORT_VALUES = ("name", "-name", "last_ping", "-last_ping", "created") STATUS_TEXT_TMPL = get_template("front/log_status_text.html") LAST_PING_TMPL = get_template("front/last_ping_cell.html") EVENTS_TMPL = get_template("front/details_events.html") +DOWNTIMES_TMPL = get_template("front/details_downtimes.html") ONE_HOUR = td(hours=1) TWELVE_HOURS = td(hours=12) @@ -474,6 +475,7 @@ def details(request, code): "check": check, "channels": channels, "timezones": pytz.all_timezones, + "downtimes": check.downtimes(months=3), } return render(request, "front/details.html", ctx) @@ -523,6 +525,7 @@ def status_single(request, code): if updated != request.GET.get("u"): doc["events"] = EVENTS_TMPL.render({"check": check, "events": events}) + doc["downtimes"] = DOWNTIMES_TMPL.render({"downtimes": check.downtimes(3)}) return JsonResponse(doc) diff --git a/static/css/details.css b/static/css/details.css index 030a833c..188953bc 100644 --- a/static/css/details.css +++ b/static/css/details.css @@ -80,3 +80,18 @@ color: #d43f3a; background: #FFF; } + +#downtimes table { + width: 350px; +} + +#downtimes tr:first-child td, #downtimes tr:first-child th { + border-top: 0; +} + +#downtimes th { + width: 100px; + text-align: right; + font-weight: normal; + color: #888; +} diff --git a/static/js/details.js b/static/js/details.js index b92192f2..2a0628ea 100644 --- a/static/js/details.js +++ b/static/js/details.js @@ -66,6 +66,10 @@ $(function () { switchDateFormat(lastFormat); } + if (data.downtimes) { + $("#downtimes").html(data.downtimes); + } + if (document.title != data.title) { document.title = data.title; } diff --git a/templates/emails/summary-downtimes-html.html b/templates/emails/summary-downtimes-html.html index 3b10b4cb..c653fd16 100644 --- a/templates/emails/summary-downtimes-html.html +++ b/templates/emails/summary-downtimes-html.html @@ -62,10 +62,10 @@ {% endif %} - {% for boundary, seconds, count in check.outages_by_month %} + {% for boundary, seconds, count in check.downtimes %} {% if count %} - {{ count }} outage{{ count|pluralize }}, + {{ count }} downtime{{ count|pluralize }},
{{ seconds|hc_approx_duration }} total diff --git a/templates/front/details.html b/templates/front/details.html index 44ef2ef9..34042088 100644 --- a/templates/front/details.html +++ b/templates/front/details.html @@ -87,8 +87,14 @@ - - {% include "front/log_status_text.html" %} + +

{% include "front/log_status_text.html" %}

+ + + + + + {% include "front/details_downtimes.html" %} @@ -196,7 +202,6 @@ -

Log diff --git a/templates/front/details_downtimes.html b/templates/front/details_downtimes.html new file mode 100644 index 00000000..bc614007 --- /dev/null +++ b/templates/front/details_downtimes.html @@ -0,0 +1,16 @@ +{% load hc_extras %} + + {% for boundary, seconds, count in downtimes reversed %} + + + + + {% endfor %} +
{{ boundary|date:"N Y"}} + {% if count %} + {{ count }} downtime{{ count|pluralize }}, + {{ seconds|hc_approx_duration }} total + {% else %} + All good! + {% endif %} +
\ No newline at end of file