mirror of
https://github.com/healthchecks/healthchecks.git
synced 2025-04-08 06:30:05 +00:00
parent
42f88f4fb0
commit
16450a66c7
6 changed files with 61 additions and 36 deletions
|
@ -7,6 +7,7 @@ All notable changes to this project will be documented in this file.
|
|||
- Update Opsgenie instructions
|
||||
- Update Spike.sh instructions
|
||||
- Add system check to validate settings.SITE_ROOT (#895)
|
||||
- Add tooltips to tag buttons in the checks list screen (#911)
|
||||
|
||||
### Bug Fixes
|
||||
- Increase uWSGI buffer size to allow requests with large cookies (#925)
|
||||
|
|
|
@ -117,7 +117,9 @@ class MyChecksTestCase(BaseTestCase):
|
|||
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.get(self.url)
|
||||
self.assertContains(r, """<div class="btn btn-xs down ">foo</div>""")
|
||||
self.assertContains(
|
||||
r, """<div data-tooltip="1 of 1 down" class="btn btn-xs down ">foo</div>"""
|
||||
)
|
||||
|
||||
def test_it_shows_grace_badge(self) -> None:
|
||||
self.check.last_ping = now() - td(days=1, minutes=10)
|
||||
|
@ -127,7 +129,9 @@ class MyChecksTestCase(BaseTestCase):
|
|||
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.get(self.url)
|
||||
self.assertContains(r, """<div class="btn btn-xs grace ">foo</div>""")
|
||||
self.assertContains(
|
||||
r, """<div data-tooltip="1 up" class="btn btn-xs grace ">foo</div>"""
|
||||
)
|
||||
|
||||
def test_it_shows_grace_started_badge(self) -> None:
|
||||
self.check.last_start = now()
|
||||
|
@ -138,7 +142,9 @@ class MyChecksTestCase(BaseTestCase):
|
|||
|
||||
self.client.login(username="alice@example.org", password="password")
|
||||
r = self.client.get(self.url)
|
||||
self.assertContains(r, """<div class="btn btn-xs grace ">foo</div>""")
|
||||
self.assertContains(
|
||||
r, """<div data-tooltip="1 up" class="btn btn-xs grace ">foo</div>"""
|
||||
)
|
||||
|
||||
def test_it_hides_actions_from_readonly_users(self) -> None:
|
||||
self.bobs_membership.role = "r"
|
||||
|
|
|
@ -19,7 +19,7 @@ class StatusTestCase(BaseTestCase):
|
|||
self.assertEqual(r.status_code, 200)
|
||||
doc = r.json()
|
||||
|
||||
self.assertEqual(doc["tags"]["foo"], "up")
|
||||
self.assertEqual(doc["tags"]["foo"], ["up", "1 up"])
|
||||
|
||||
detail = doc["details"][0]
|
||||
self.assertEqual(detail["code"], str(self.check.code))
|
||||
|
|
|
@ -7,7 +7,7 @@ import os
|
|||
import re
|
||||
import sqlite3
|
||||
import uuid
|
||||
from collections import defaultdict
|
||||
from collections import Counter, defaultdict
|
||||
from collections.abc import Iterable
|
||||
from datetime import datetime
|
||||
from datetime import timedelta as td
|
||||
|
@ -77,25 +77,32 @@ EVENTS_TMPL = get_template("front/details_events.html")
|
|||
DOWNTIMES_TMPL = get_template("front/details_downtimes.html")
|
||||
|
||||
|
||||
def _tags_statuses(checks: Iterable[Check]) -> tuple[dict[str, str], int]:
|
||||
tags, down, grace, num_down = {}, {}, {}, 0
|
||||
def _tags_counts(checks: Iterable[Check]) -> tuple[list[tuple[str, str, str]], int]:
|
||||
num_down = 0
|
||||
grace = set()
|
||||
counts: Counter[str] = Counter()
|
||||
down_counts: Counter[str] = Counter()
|
||||
for check in checks:
|
||||
status = check.get_status()
|
||||
|
||||
counts.update(check.tags_list())
|
||||
if status == "down":
|
||||
num_down += 1
|
||||
for tag in check.tags_list():
|
||||
down[tag] = "down"
|
||||
down_counts.update(check.tags_list())
|
||||
elif status == "grace":
|
||||
for tag in check.tags_list():
|
||||
grace[tag] = "grace"
|
||||
else:
|
||||
for tag in check.tags_list():
|
||||
tags[tag] = "up"
|
||||
grace.update(check.tags_list())
|
||||
|
||||
tags.update(grace)
|
||||
tags.update(down)
|
||||
return tags, num_down
|
||||
result = []
|
||||
for tag in counts:
|
||||
if tag in down_counts:
|
||||
status = "down"
|
||||
text = f"{down_counts[tag]} of {counts[tag]} down"
|
||||
else:
|
||||
status = "grace" if tag in grace else "up"
|
||||
text = f"{counts[tag]} up"
|
||||
|
||||
result.append((tag, status, text))
|
||||
|
||||
return result, num_down
|
||||
|
||||
|
||||
def _get_check_for_user(
|
||||
|
@ -222,9 +229,8 @@ def checks(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
|
|||
checks = list(q.prefetch_related("channel_set"))
|
||||
sortchecks(checks, request.profile.sort)
|
||||
|
||||
tags_statuses, num_down = _tags_statuses(checks)
|
||||
pairs = list(tags_statuses.items())
|
||||
pairs.sort(key=lambda pair: pair[0].lower())
|
||||
tags_counts, num_down = _tags_counts(checks)
|
||||
tags_counts.sort(key=lambda item: item[0].lower())
|
||||
|
||||
is_group = Case(When(kind="group", then=0), default=1)
|
||||
channels = project.channel_set.annotate(is_group=is_group)
|
||||
|
@ -269,7 +275,7 @@ def checks(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
|
|||
"checks": checks,
|
||||
"channels": channels,
|
||||
"num_down": num_down,
|
||||
"tags": pairs,
|
||||
"tags": tags_counts,
|
||||
"ping_endpoint": settings.PING_ENDPOINT,
|
||||
"timezones": all_timezones,
|
||||
"project": project,
|
||||
|
@ -302,9 +308,10 @@ def status(request: AuthenticatedHttpRequest, code: UUID) -> HttpResponse:
|
|||
}
|
||||
)
|
||||
|
||||
tags_statuses, num_down = _tags_statuses(checks)
|
||||
tags_counts, num_down = _tags_counts(checks)
|
||||
tags = {tag: (status, tooltip) for tag, status, tooltip in tags_counts}
|
||||
return JsonResponse(
|
||||
{"details": details, "tags": tags_statuses, "title": num_down_title(num_down)}
|
||||
{"details": details, "tags": tags, "title": num_down_title(num_down)}
|
||||
)
|
||||
|
||||
|
||||
|
@ -2433,14 +2440,20 @@ def metrics(request: HttpRequest, code: UUID, key: str) -> HttpResponse:
|
|||
value = 1 if check.last_start is not None else 0
|
||||
yield TMPL % (esc(check.name), esc(check.tags), check.unique_key, value)
|
||||
|
||||
tags_statuses, num_down = _tags_statuses(checks)
|
||||
all_tags, down_tags, num_down = set(), set(), 0
|
||||
for check in checks:
|
||||
all_tags.update(check.tags_list())
|
||||
if check.get_status() == "down":
|
||||
num_down += 1
|
||||
down_tags.update(check.tags_list())
|
||||
|
||||
yield "\n"
|
||||
help = "Whether all checks with this tag are up (1 for yes, 0 for no)."
|
||||
yield f"# HELP hc_tag_up {help}\n"
|
||||
yield "# TYPE hc_tag_up gauge\n"
|
||||
TMPL = """hc_tag_up{tag="%s"} %d\n"""
|
||||
for tag in sorted(tags_statuses):
|
||||
value = 0 if tags_statuses[tag] == "down" else 1
|
||||
for tag in sorted(all_tags):
|
||||
value = 0 if tag in down_tags else 1
|
||||
yield TMPL % (esc(tag), value)
|
||||
|
||||
yield "\n"
|
||||
|
|
|
@ -67,6 +67,10 @@ $(function () {
|
|||
title: 'The word "confirm" was found in request body'
|
||||
});
|
||||
|
||||
$("#my-checks-tags .btn").tooltip({
|
||||
title: function() {return this.getAttribute("data-tooltip");}
|
||||
});
|
||||
|
||||
function applyFilters() {
|
||||
// Make a list of currently checked tags:
|
||||
var checked = [];
|
||||
|
@ -226,13 +230,14 @@ $(function () {
|
|||
}
|
||||
}
|
||||
|
||||
$("#my-checks-tags div").each(function(a) {
|
||||
var status = data.tags[this.innerText];
|
||||
if (lastStatus[this.innerText] == status)
|
||||
return;
|
||||
|
||||
$(this).removeClass("up grace down").addClass(status);
|
||||
lastStatus[this.innerText] = status;
|
||||
$("#my-checks-tags > div.btn").each(function(a) {
|
||||
tag = this.innerText;
|
||||
this.setAttribute("data-tooltip", data.tags[tag][1]);
|
||||
var status = data.tags[tag][0];
|
||||
if (lastStatus[tag] != status) {
|
||||
$(this).removeClass("up grace down").addClass(status);
|
||||
lastStatus[tag] = status;
|
||||
}
|
||||
});
|
||||
|
||||
if (document.title != data.title) {
|
||||
|
|
|
@ -55,8 +55,8 @@
|
|||
{% if checks %}
|
||||
<div class="row">
|
||||
<div id="my-checks-tags" class="col-sm-9">
|
||||
{% for tag, status in tags %}
|
||||
<div class="btn btn-xs {{ status }} {% if tag in selected_tags %}checked{% endif%}">{{ tag }}</div>
|
||||
{% for tag, status, tooltip in tags %}
|
||||
<div data-tooltip="{{ tooltip }}" class="btn btn-xs {{ status }} {% if tag in selected_tags %}checked{% endif%}">{{ tag }}</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
<div class="col-sm-3">
|
||||
|
|
Loading…
Add table
Reference in a new issue