mirror of
https://github.com/healthchecks/healthchecks.git
synced 2025-04-07 22:25:35 +00:00
parent
2f562bb502
commit
51fd339602
6 changed files with 85 additions and 37 deletions
|
@ -15,6 +15,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- Add the "Badges" page in docs
|
- Add the "Badges" page in docs
|
||||||
- Add support for multiple recipients in incoming email (#669)
|
- Add support for multiple recipients in incoming email (#669)
|
||||||
- Upgrade to fido2 1.0.0, requests 2.28.1, segno 1.5.2
|
- Upgrade to fido2 1.0.0, requests 2.28.1, segno 1.5.2
|
||||||
|
- Implement auto-refresh and running indicator in the My Projects page (#681)
|
||||||
|
|
||||||
### Bug Fixes
|
### Bug Fixes
|
||||||
- Fix the display of ignored pings with non-zero exit status
|
- Fix the display of ignored pings with non-zero exit status
|
||||||
|
|
|
@ -394,19 +394,6 @@ class Project(models.Model):
|
||||||
for profile in q:
|
for profile in q:
|
||||||
profile.update_next_nag_date()
|
profile.update_next_nag_date()
|
||||||
|
|
||||||
def overall_status(self):
|
|
||||||
if not hasattr(self, "_overall_status"):
|
|
||||||
self._overall_status = "up"
|
|
||||||
for check in self.check_set.all():
|
|
||||||
check_status = check.get_status()
|
|
||||||
if check_status == "grace" and self._overall_status == "up":
|
|
||||||
self._overall_status = "grace"
|
|
||||||
elif check_status == "down":
|
|
||||||
self._overall_status = "down"
|
|
||||||
break
|
|
||||||
|
|
||||||
return self._overall_status
|
|
||||||
|
|
||||||
def get_n_down(self):
|
def get_n_down(self):
|
||||||
result = 0
|
result = 0
|
||||||
for check in self.check_set.all():
|
for check in self.check_set.all():
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
from collections import defaultdict
|
||||||
from datetime import timedelta as td
|
from datetime import timedelta as td
|
||||||
import email
|
import email
|
||||||
import json
|
import json
|
||||||
|
@ -298,29 +299,51 @@ def switch_channel(request, code, channel_code):
|
||||||
return HttpResponse()
|
return HttpResponse()
|
||||||
|
|
||||||
|
|
||||||
|
def _get_project_summary(profile):
|
||||||
|
statuses = defaultdict(lambda: {"status": "up", "started": False})
|
||||||
|
q = profile.checks_from_all_projects()
|
||||||
|
q = q.annotate(project_code=F("project__code"))
|
||||||
|
for check in q:
|
||||||
|
summary = statuses[check.project_code]
|
||||||
|
if check.last_start:
|
||||||
|
summary["started"] = True
|
||||||
|
|
||||||
|
if summary["status"] != "down":
|
||||||
|
status = check.get_status()
|
||||||
|
if status == "down" or (status == "grace" and summary["status"] == "up"):
|
||||||
|
summary["status"] = status
|
||||||
|
|
||||||
|
return statuses
|
||||||
|
|
||||||
|
|
||||||
def index(request):
|
def index(request):
|
||||||
if request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
project_ids = request.profile.projects().values("id")
|
return redirect("hc-login")
|
||||||
|
|
||||||
q = Project.objects.filter(id__in=project_ids)
|
summary = _get_project_summary(request.profile)
|
||||||
q = q.annotate(n_checks=Count("check", distinct=True))
|
if "refresh" in request.GET:
|
||||||
q = q.annotate(n_channels=Count("channel", distinct=True))
|
return JsonResponse({str(k): v for k, v in summary.items()})
|
||||||
q = q.annotate(owner_email=F("owner__email"))
|
|
||||||
|
|
||||||
projects = list(q)
|
q = request.profile.projects()
|
||||||
# Primary sort key: projects with overall_status=down go first
|
q = q.annotate(n_checks=Count("check", distinct=True))
|
||||||
# Secondary sort key: project's name
|
q = q.annotate(n_channels=Count("channel", distinct=True))
|
||||||
projects.sort(key=lambda p: (p.overall_status() != "down", p.name))
|
q = q.annotate(owner_email=F("owner__email"))
|
||||||
|
projects = list(q)
|
||||||
|
for project in projects:
|
||||||
|
project.overall_status = summary[project.code]["status"]
|
||||||
|
project.any_started = summary[project.code]["started"]
|
||||||
|
|
||||||
ctx = {
|
# Primary sort key: projects with overall_status=down go first
|
||||||
"page": "projects",
|
# Secondary sort key: project's name
|
||||||
"projects": projects,
|
projects.sort(key=lambda p: (p.overall_status != "down", p.name))
|
||||||
"last_project_id": request.session.get("last_project_id"),
|
|
||||||
}
|
|
||||||
|
|
||||||
return render(request, "front/projects.html", ctx)
|
ctx = {
|
||||||
|
"page": "projects",
|
||||||
|
"projects": projects,
|
||||||
|
"last_project_id": request.session.get("last_project_id"),
|
||||||
|
}
|
||||||
|
|
||||||
return redirect("hc-login")
|
return render(request, "front/projects.html", ctx)
|
||||||
|
|
||||||
|
|
||||||
def dashboard(request):
|
def dashboard(request):
|
||||||
|
|
|
@ -26,11 +26,6 @@
|
||||||
text-overflow: ellipsis;
|
text-overflow: ellipsis;
|
||||||
}
|
}
|
||||||
|
|
||||||
#project-selector .project .status {
|
|
||||||
position: absolute;
|
|
||||||
left: 24px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#project-selector #add-project .project {
|
#project-selector #add-project .project {
|
||||||
border-style: dashed;
|
border-style: dashed;
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
@ -46,3 +41,8 @@
|
||||||
color: var(--text-color);
|
color: var(--text-color);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.indicators {
|
||||||
|
position: absolute;
|
||||||
|
left: 24px;
|
||||||
|
width: 24px;
|
||||||
|
}
|
32
static/js/projects.js
Normal file
32
static/js/projects.js
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
$(function () {
|
||||||
|
var base = document.getElementById("base-url").getAttribute("href").slice(0, -1);
|
||||||
|
|
||||||
|
// Schedule refresh to run every 3s when tab is visible and user
|
||||||
|
// is active, every 60s otherwise
|
||||||
|
var lastStatus = {};
|
||||||
|
var lastStarted = {};
|
||||||
|
function refreshStatus() {
|
||||||
|
$.ajax({
|
||||||
|
url: base + "?refresh=1",
|
||||||
|
dataType: "json",
|
||||||
|
timeout: 2000,
|
||||||
|
success: function(data) {
|
||||||
|
for (var code in data) {
|
||||||
|
var el = data[code];
|
||||||
|
|
||||||
|
if (el.status != lastStatus[code]) {
|
||||||
|
$("#" + code + " div.status").attr("class", "status ic-" + el.status);
|
||||||
|
lastStatus[code] = el.status;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (el.started != lastStarted[code]) {
|
||||||
|
$("#" + code + " div.spinner").toggleClass("started", el.started);
|
||||||
|
lastStarted[code] = el.started;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
adaptiveSetInterval(refreshStatus);
|
||||||
|
});
|
|
@ -14,9 +14,12 @@
|
||||||
<div id="project-selector" class="row">
|
<div id="project-selector" class="row">
|
||||||
{% for project in projects %}
|
{% for project in projects %}
|
||||||
<a href="{% url 'hc-checks' project.code %}">
|
<a href="{% url 'hc-checks' project.code %}">
|
||||||
<div class="col-sm-6 col-md-4">
|
<div id="{{ project.code }}" class="col-sm-6 col-md-4">
|
||||||
<div class="panel project {% if project.id == last_project_id %}selected{% endif %}">
|
<div class="panel project {% if project.id == last_project_id %}selected{% endif %}">
|
||||||
<div class="status ic-{{ project.overall_status }}"></div>
|
<div class="indicators">
|
||||||
|
<div class="status ic-{{ project.overall_status }}"></div>
|
||||||
|
<div class="spinner {% if project.any_started %}started{% endif %}"></div>
|
||||||
|
</div>
|
||||||
<h4>{{ project }}</h4>
|
<h4>{{ project }}</h4>
|
||||||
<div>
|
<div>
|
||||||
{{ project.n_checks }} check{{ project.n_checks|pluralize }},
|
{{ project.n_checks }} check{{ project.n_checks|pluralize }},
|
||||||
|
@ -51,6 +54,8 @@
|
||||||
{% compress js %}
|
{% compress js %}
|
||||||
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
|
<script src="{% static 'js/jquery-3.6.0.min.js' %}"></script>
|
||||||
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
<script src="{% static 'js/bootstrap.min.js' %}"></script>
|
||||||
|
<script src="{% static 'js/adaptive-setinterval.js' %}"></script>
|
||||||
<script src="{% static 'js/add_project_modal.js' %}"></script>
|
<script src="{% static 'js/add_project_modal.js' %}"></script>
|
||||||
|
<script src="{% static 'js/projects.js' %}"></script>
|
||||||
{% endcompress %}
|
{% endcompress %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
Loading…
Add table
Reference in a new issue