forked from GithubBackups/healthchecks
In the checks list, indicate a started check with a progress spinner under the status icon (cc: #338)
This commit is contained in:
parent
a18eb134f5
commit
149096811d
@ -1,7 +1,7 @@
|
|||||||
# Changelog
|
# Changelog
|
||||||
All notable changes to this project will be documented in this file.
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
## Unreleased
|
## v1.16.0-dev - Unreleased
|
||||||
|
|
||||||
### Improvements
|
### Improvements
|
||||||
- Paused ping handling can be controlled via API (#376)
|
- Paused ping handling can be controlled via API (#376)
|
||||||
@ -9,7 +9,7 @@ All notable changes to this project will be documented in this file.
|
|||||||
- The /api/v1/checks/ endpoint now accepts either UUID or `unique_key` (#370)
|
- The /api/v1/checks/ endpoint now accepts either UUID or `unique_key` (#370)
|
||||||
- Added /api/v1/checks/uuid/flips/ endpoint (#349)
|
- Added /api/v1/checks/uuid/flips/ endpoint (#349)
|
||||||
- In the cron expression dialog, show a human-friendly version of the expression
|
- In the cron expression dialog, show a human-friendly version of the expression
|
||||||
|
- Indicate a started check with a progress spinner under status icon (#338)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
- Removing Pager Team integration, project appears to be discontinued
|
- Removing Pager Team integration, project appears to be discontinued
|
||||||
|
@ -119,7 +119,7 @@ class Check(models.Model):
|
|||||||
if self.last_duration and self.last_duration < MAX_DELTA:
|
if self.last_duration and self.last_duration < MAX_DELTA:
|
||||||
return self.last_duration
|
return self.last_duration
|
||||||
|
|
||||||
def get_grace_start(self):
|
def get_grace_start(self, with_started=True):
|
||||||
""" Return the datetime when the grace period starts.
|
""" Return the datetime when the grace period starts.
|
||||||
|
|
||||||
If the check is currently new, paused or down, return None.
|
If the check is currently new, paused or down, return None.
|
||||||
@ -142,7 +142,7 @@ class Check(models.Model):
|
|||||||
it = croniter(self.schedule, last_local)
|
it = croniter(self.schedule, last_local)
|
||||||
result = it.next(datetime)
|
result = it.next(datetime)
|
||||||
|
|
||||||
if self.last_start and self.status != "down":
|
if with_started and self.last_start and self.status != "down":
|
||||||
result = min(result, self.last_start)
|
result = min(result, self.last_start)
|
||||||
|
|
||||||
if result != NEVER:
|
if result != NEVER:
|
||||||
@ -175,7 +175,7 @@ class Check(models.Model):
|
|||||||
if self.status in ("new", "paused", "down"):
|
if self.status in ("new", "paused", "down"):
|
||||||
return self.status
|
return self.status
|
||||||
|
|
||||||
grace_start = self.get_grace_start()
|
grace_start = self.get_grace_start(with_started=with_started)
|
||||||
grace_end = grace_start + self.grace
|
grace_end = grace_start + self.grace
|
||||||
if now >= grace_end:
|
if now >= grace_end:
|
||||||
return "down"
|
return "down"
|
||||||
|
@ -119,8 +119,8 @@ class CheckModelTestCase(BaseTestCase):
|
|||||||
check.last_start = timezone.now() - timedelta(minutes=5)
|
check.last_start = timezone.now() - timedelta(minutes=5)
|
||||||
|
|
||||||
self.assertEqual(check.get_status(with_started=True), "started")
|
self.assertEqual(check.get_status(with_started=True), "started")
|
||||||
# Starting a check starts the grace period:
|
# A started check still is considered "up":
|
||||||
self.assertEqual(check.get_status(), "grace")
|
self.assertEqual(check.get_status(), "up")
|
||||||
|
|
||||||
def test_get_status_handles_up_then_started_and_expired(self):
|
def test_get_status_handles_up_then_started_and_expired(self):
|
||||||
check = Check(status="up")
|
check = Check(status="up")
|
||||||
|
@ -87,7 +87,7 @@ def last_ping_key(check):
|
|||||||
|
|
||||||
|
|
||||||
def not_down_key(check):
|
def not_down_key(check):
|
||||||
return check.get_status(with_started=True) != "down"
|
return check.get_status() != "down"
|
||||||
|
|
||||||
|
|
||||||
@register.filter
|
@register.filter
|
||||||
@ -126,7 +126,7 @@ def down_title(check):
|
|||||||
"""
|
"""
|
||||||
|
|
||||||
s = "%s – %s" % (check.name_then_code(), settings.SITE_NAME)
|
s = "%s – %s" % (check.name_then_code(), settings.SITE_NAME)
|
||||||
if check.get_status(with_started=True) == "down":
|
if check.get_status() == "down":
|
||||||
s = "DOWN – " + s
|
s = "DOWN – " + s
|
||||||
|
|
||||||
return s
|
return s
|
||||||
|
@ -207,8 +207,9 @@ def status(request, code):
|
|||||||
details.append(
|
details.append(
|
||||||
{
|
{
|
||||||
"code": str(check.code),
|
"code": str(check.code),
|
||||||
"status": check.get_status(with_started=True),
|
"status": check.get_status(),
|
||||||
"last_ping": LAST_PING_TMPL.render(ctx),
|
"last_ping": LAST_PING_TMPL.render(ctx),
|
||||||
|
"started": check.last_start is not None,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -594,7 +595,7 @@ def copy(request, code):
|
|||||||
def status_single(request, code):
|
def status_single(request, code):
|
||||||
check = _get_check_for_user(request, code)
|
check = _get_check_for_user(request, code)
|
||||||
|
|
||||||
status = check.get_status(with_started=True)
|
status = check.get_status()
|
||||||
events = _get_events(check, 20)
|
events = _get_events(check, 20)
|
||||||
updated = "1"
|
updated = "1"
|
||||||
if len(events):
|
if len(events):
|
||||||
|
@ -168,3 +168,51 @@ pre {
|
|||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.spinner {
|
||||||
|
display: none;
|
||||||
|
width: 24px;
|
||||||
|
height: 8px;
|
||||||
|
margin: 4px auto 0 auto;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner.started {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.spinner > div {
|
||||||
|
float: left;
|
||||||
|
width: 4px;
|
||||||
|
height: 4px;
|
||||||
|
margin: 0 2px;
|
||||||
|
|
||||||
|
background: #AAA;
|
||||||
|
border-radius: 2px;
|
||||||
|
animation-duration: 1s;
|
||||||
|
animation-name: spinner-pulse;
|
||||||
|
animation-iteration-count: infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner .d2 {
|
||||||
|
animation-delay: 0.15s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.spinner .d3 {
|
||||||
|
animation-delay: 0.3s;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes spinner-pulse {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
50% {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
to {
|
||||||
|
opacity: 0;
|
||||||
|
}
|
||||||
|
}
|
@ -199,6 +199,7 @@ $(function () {
|
|||||||
// Schedule refresh to run every 3s when tab is visible and user
|
// Schedule refresh to run every 3s when tab is visible and user
|
||||||
// is active, every 60s otherwise
|
// is active, every 60s otherwise
|
||||||
var lastStatus = {};
|
var lastStatus = {};
|
||||||
|
var lastStarted = {};
|
||||||
var lastPing = {};
|
var lastPing = {};
|
||||||
var statusUrl = $("#checks-table").data("status-url");
|
var statusUrl = $("#checks-table").data("status-url");
|
||||||
function refreshStatus() {
|
function refreshStatus() {
|
||||||
@ -213,6 +214,11 @@ $(function () {
|
|||||||
$("#" + el.code + " span.status").attr("class", "status icon-" + el.status);
|
$("#" + el.code + " span.status").attr("class", "status icon-" + el.status);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (lastStarted[el.code] != el.started) {
|
||||||
|
lastStarted[el.code] = el.started;
|
||||||
|
$("#" + el.code + " .spinner").toggleClass("started", el.started);
|
||||||
|
}
|
||||||
|
|
||||||
if (lastPing[el.code] != el.last_ping) {
|
if (lastPing[el.code] != el.last_ping) {
|
||||||
lastPing[el.code] = el.last_ping;
|
lastPing[el.code] = el.last_ping;
|
||||||
$("#lpd-" + el.code).html(el.last_ping);
|
$("#lpd-" + el.code).html(el.last_ping);
|
||||||
|
@ -111,7 +111,7 @@
|
|||||||
<table>
|
<table>
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
<span id="log-status-icon" class="status icon-{{ check.get_status_with_started }}"></span>
|
<span id="log-status-icon" class="status icon-{{ check.get_status }}"></span>
|
||||||
</td>
|
</td>
|
||||||
<td >
|
<td >
|
||||||
<p id="log-status-text">{% include "front/log_status_text.html" %}</p>
|
<p id="log-status-text">{% include "front/log_status_text.html" %}</p>
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{% load humanize %}
|
{% load humanize %}
|
||||||
{% with check.get_status_with_started as status %}
|
{% with check.get_status as status %}
|
||||||
{% if status == "down" %}
|
{% if status == "down" %}
|
||||||
This check is down. Last ping was {{ check.last_ping|naturaltime }}.
|
This check is down. Last ping was {{ check.last_ping|naturaltime }}.
|
||||||
{% elif status == "up" %}
|
{% elif status == "up" %}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
{% load hc_extras %}
|
{% load hc_extras static %}
|
||||||
<table
|
<table
|
||||||
id="checks-table"
|
id="checks-table"
|
||||||
class="table"
|
class="table"
|
||||||
@ -60,7 +60,12 @@
|
|||||||
{% if check in hidden_checks %}style="display: none"{% endif %}>
|
{% if check in hidden_checks %}style="display: none"{% endif %}>
|
||||||
|
|
||||||
<td class="indicator-cell">
|
<td class="indicator-cell">
|
||||||
<span class="status icon-{{ check.get_status_with_started }}" data-toggle="tooltip"></span>
|
<span class="status icon-{{ check.get_status }}" data-toggle="tooltip"></span>
|
||||||
|
<div class="spinner {% if check.last_start %}started{% endif %}">
|
||||||
|
<div class="d1"></div>
|
||||||
|
<div class="d2"></div>
|
||||||
|
<div class="d3"></div>
|
||||||
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
<div data-name="{{ check.name }}"
|
<div data-name="{{ check.name }}"
|
||||||
|
Loading…
x
Reference in New Issue
Block a user