mirror of
https://github.com/healthchecks/healthchecks.git
synced 2025-04-07 14:15:34 +00:00
Update Check.downtimes() to return a list of namedtuples
This way it should be easier to add extra fields like uptime to the returned data structure.
This commit is contained in:
parent
e48d361331
commit
f0085933c3
4 changed files with 35 additions and 34 deletions
hc/api
templates
|
@ -7,7 +7,7 @@ import uuid
|
|||
from datetime import datetime
|
||||
from datetime import timedelta as td
|
||||
from datetime import timezone
|
||||
from typing import Any, TypedDict
|
||||
from typing import Any, NamedTuple, TypedDict
|
||||
from urllib.parse import urlencode
|
||||
from zoneinfo import ZoneInfo
|
||||
|
||||
|
@ -130,6 +130,12 @@ class CheckDict(TypedDict, total=False):
|
|||
tz: str
|
||||
|
||||
|
||||
class DowntimeRecord(NamedTuple):
|
||||
boundary: datetime
|
||||
duration: td
|
||||
count: int | None
|
||||
|
||||
|
||||
class DowntimeSummary(object):
|
||||
def __init__(self, boundaries: list[datetime]) -> None:
|
||||
self.boundaries = list(sorted(boundaries, reverse=True))
|
||||
|
@ -143,8 +149,12 @@ class DowntimeSummary(object):
|
|||
self.counts[i] += 1
|
||||
return
|
||||
|
||||
def as_tuples(self) -> zip[tuple[datetime, td, int]]:
|
||||
return zip(self.boundaries, self.durations, self.counts)
|
||||
def as_records(self) -> list[DowntimeRecord]:
|
||||
result = []
|
||||
for b, d, c in zip(self.boundaries, self.durations, self.counts):
|
||||
result.append(DowntimeRecord(b, d, c))
|
||||
|
||||
return result
|
||||
|
||||
|
||||
class Check(models.Model):
|
||||
|
@ -485,9 +495,7 @@ class Check(models.Model):
|
|||
threshold = self.n_pings - self.project.owner_profile.ping_log_limit
|
||||
return self.ping_set.filter(n__gt=threshold)
|
||||
|
||||
def downtimes_by_boundary(
|
||||
self, boundaries: list[datetime]
|
||||
) -> list[tuple[datetime, td | None, int | None]]:
|
||||
def downtimes_by_boundary(self, boundaries: list[datetime]) -> list[DowntimeRecord]:
|
||||
"""Calculate downtime counts and durations for the given time intervals.
|
||||
|
||||
Returns a list of (datetime, downtime_in_secs, number_of_outages) tuples
|
||||
|
@ -517,24 +525,21 @@ class Check(models.Model):
|
|||
if prev_status != "---":
|
||||
status = prev_status
|
||||
|
||||
# Convert to a list of tuples and set counters to None
|
||||
# for intervals when the check didn't exist yet
|
||||
# Set count to None for intervals when the check didn't exist yet
|
||||
prev_boundary = None
|
||||
result: list[tuple[datetime, td | None, int | None]] = []
|
||||
for triple in summary.as_tuples():
|
||||
result: list[DowntimeRecord] = []
|
||||
for record in summary.as_records():
|
||||
if prev_boundary and self.created > prev_boundary:
|
||||
result.append((triple[0], None, None))
|
||||
result.append(DowntimeRecord(record.boundary, record.duration, None))
|
||||
continue
|
||||
|
||||
prev_boundary = triple[0]
|
||||
result.append(triple)
|
||||
prev_boundary = record.boundary
|
||||
result.append(record)
|
||||
|
||||
result.sort()
|
||||
return result
|
||||
|
||||
def downtimes(
|
||||
self, months: int, tz: str
|
||||
) -> list[tuple[datetime, td | None, int | None]]:
|
||||
def downtimes(self, months: int, tz: str) -> list[DowntimeRecord]:
|
||||
boundaries = month_boundaries(months, tz)
|
||||
return self.downtimes_by_boundary(boundaries)
|
||||
|
||||
|
|
|
@ -301,16 +301,13 @@ class CheckModelTestCase(BaseTestCase):
|
|||
nov, dec, jan = check.downtimes(3, "UTC")
|
||||
|
||||
# Nov. 2019
|
||||
self.assertIsNone(nov[1])
|
||||
self.assertIsNone(nov[2])
|
||||
self.assertIsNone(nov.count)
|
||||
|
||||
# Dec. 2019
|
||||
self.assertIsNone(dec[1])
|
||||
self.assertIsNone(dec[2])
|
||||
self.assertIsNone(dec.count)
|
||||
|
||||
# Jan. 2020
|
||||
self.assertEqual(jan[1], td())
|
||||
self.assertEqual(jan[2], 0)
|
||||
self.assertEqual(jan.count, 0)
|
||||
|
||||
@override_settings(S3_BUCKET=None)
|
||||
def test_it_prunes(self) -> None:
|
||||
|
|
|
@ -64,16 +64,16 @@
|
|||
</table>
|
||||
{% endif %}
|
||||
</td>
|
||||
{% for boundary, seconds, count in check.past_downtimes %}
|
||||
{% if count %}
|
||||
{% for d in check.past_downtimes %}
|
||||
{% if d.count %}
|
||||
<td style="border-top: 1px solid #EDEFF2; padding: 16px 8px; font-family: Helvetica, Arial, sans-serif;">
|
||||
{{ count }} downtime{{ count|pluralize }},
|
||||
{{ d.count }} downtime{{ d.count|pluralize }},
|
||||
<br />
|
||||
{{ seconds|hc_approx_duration }} total
|
||||
{{ d.duration|hc_approx_duration }} total
|
||||
</td>
|
||||
{% else %}
|
||||
<td style="border-top: 1px solid #EDEFF2; padding: 16px 8px; font-family: Helvetica, Arial, sans-serif; color: #9BA2AB;">
|
||||
{% if count is None %}
|
||||
{% if d.count is None %}
|
||||
{% comment %} The check didn't exist yet {% endcomment %}
|
||||
{% else %}
|
||||
All good!
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
{% load hc_extras tz %}
|
||||
{% timezone tz %}
|
||||
<table id="downtimes" class="table">
|
||||
{% for boundary, down_duration_secs, count in downtimes reversed %}
|
||||
{% for d in downtimes reversed %}
|
||||
<tr>
|
||||
<th>{{ boundary|date:"N Y"}}</th>
|
||||
<th>{{ d.boundary|date:"N Y"}}</th>
|
||||
<td>
|
||||
{% if count %}
|
||||
{{ count }} downtime{{ count|pluralize }},
|
||||
{{ down_duration_secs|hc_approx_duration }} total
|
||||
{% elif count is None %}
|
||||
{% if d.count %}
|
||||
{{ d.count }} downtime{{ d.count|pluralize }}, {{ d.duration|hc_approx_duration }} total
|
||||
{% elif d.count is None %}
|
||||
–
|
||||
{% else %}
|
||||
All good!
|
||||
|
@ -17,4 +16,4 @@
|
|||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
{% endtimezone %}
|
||||
{% endtimezone %}
|
||||
|
|
Loading…
Add table
Reference in a new issue