0
0
Fork 0
mirror of https://github.com/healthchecks/healthchecks.git synced 2025-04-07 22:25:35 +00:00

Simplify DowntimeSummary

This commit is contained in:
Pēteris Caune 2023-09-27 17:16:16 +03:00
parent f7af738c76
commit 58d7c8cc55
No known key found for this signature in database
GPG key ID: E28D7679E9A9EDE2
4 changed files with 30 additions and 32 deletions

View file

@ -241,11 +241,14 @@ class Profile(models.Model):
for check in checks:
downtimes = check.downtimes_by_boundary(boundaries, self.tz)
# downtimes_by_boundary returns records in descending order.
# Switch to ascending order:
# downtimes_by_boundary returns records in descending order,
# but the template will need them in ascending order:
downtimes.reverse()
setattr(check, "past_downtimes", downtimes[:-1])
# boundaries are in descending order, but the template
# will need them in ascending order:
boundaries.reverse()
ctx["checks"] = checks
ctx["boundaries"] = boundaries[:-1]
ctx["monthly_or_weekly"] = self.reports

View file

@ -149,25 +149,19 @@ class DowntimeRecord:
class DowntimeSummary(object):
def __init__(self, boundaries: list[datetime], tz: str) -> None:
self.boundaries = list(sorted(boundaries, reverse=True))
self.durations = [td() for _ in boundaries]
self.counts = [0 for _ in boundaries]
self.tz = tz
"""
`boundaries` are timezone-aware datetimes of the first days of time intervals
(months or weeks), and should be pre-sorted in descending order.
"""
self.records = [DowntimeRecord(b, tz, td(), 0) for b in boundaries]
def add(self, when: datetime, duration: td) -> None:
for i in range(0, len(self.boundaries)):
if when >= self.boundaries[i]:
self.durations[i] += duration
self.counts[i] += 1
for record in self.records:
if when >= record.boundary:
record.duration += duration
record.count += 1
return
def as_records(self) -> list[DowntimeRecord]:
result = []
for b, d, c in zip(self.boundaries, self.durations, self.counts):
result.append(DowntimeRecord(b, self.tz, d, c))
return result
class Check(models.Model):
name = models.CharField(max_length=100, blank=True)
@ -514,8 +508,8 @@ class Check(models.Model):
Returns a list of DowntimeRecord instances in descending datetime order.
`boundaries` are the datetimes of the first days of time intervals
(months or weeks) we're interested in, in ascending order.
`boundaries` are timezone-aware datetimes of the first days of time intervals
(months or weeks), and should be pre-sorted in descending order.
"""
@ -543,14 +537,13 @@ class Check(models.Model):
# Set count to None for intervals when the check didn't exist yet
prev_boundary = None
results = summary.as_records()
for record in results:
for record in summary.records:
if prev_boundary and self.created > prev_boundary:
record.count = None
prev_boundary = record.boundary
return results
return summary.records
def downtimes(self, months: int, tz: str) -> list[DowntimeRecord]:
boundaries = month_boundaries(months, tz)

View file

@ -75,13 +75,14 @@ def format_approx_duration(duration: timedelta) -> str:
def month_boundaries(months: int, tzstr: str) -> list[datetime]:
"""Return month start times in descending order starting from the current month."""
tz = ZoneInfo(tzstr)
result: list[datetime] = []
now_value = now().astimezone(tz)
y, m = now_value.year, now_value.month
for x in range(0, months):
result.insert(0, datetime(y, m, 1, tzinfo=tz))
result.append(datetime(y, m, 1, tzinfo=tz))
m -= 1
if m == 0:
@ -92,13 +93,14 @@ def month_boundaries(months: int, tzstr: str) -> list[datetime]:
def week_boundaries(weeks: int, tzstr: str) -> list[datetime]:
"""Return week start times in descending order starting from the current week."""
tz = ZoneInfo(tzstr)
result: list[datetime] = []
today = now().astimezone(tz).date()
needle = today - timedelta(days=today.weekday())
for x in range(0, weeks):
result.insert(0, datetime(needle.year, needle.month, needle.day, tzinfo=tz))
result.append(datetime(needle.year, needle.month, needle.day, tzinfo=tz))
needle -= timedelta(days=7)
return result

View file

@ -42,30 +42,30 @@ class DateFormattingTestCase(TestCase):
class MonthBoundaryTestCase(TestCase):
def test_utc_works(self) -> None:
result = month_boundaries(3, "UTC")
self.assertEqual(result[0].isoformat(), "2019-11-01T00:00:00+00:00")
self.assertEqual(result[0].isoformat(), "2020-01-01T00:00:00+00:00")
self.assertEqual(result[1].isoformat(), "2019-12-01T00:00:00+00:00")
self.assertEqual(result[2].isoformat(), "2020-01-01T00:00:00+00:00")
self.assertEqual(result[2].isoformat(), "2019-11-01T00:00:00+00:00")
def test_non_utc_works(self) -> None:
result = month_boundaries(3, "Europe/Riga")
self.assertEqual(result[0].isoformat(), "2019-11-01T00:00:00+02:00")
self.assertEqual(result[0].isoformat(), "2020-01-01T00:00:00+02:00")
self.assertEqual(result[1].isoformat(), "2019-12-01T00:00:00+02:00")
self.assertEqual(result[2].isoformat(), "2020-01-01T00:00:00+02:00")
self.assertEqual(result[2].isoformat(), "2019-11-01T00:00:00+02:00")
@patch("hc.lib.date.now", MOCK_NOW)
class WeekBoundaryTestCase(TestCase):
def test_utc_works(self) -> None:
result = week_boundaries(3, "UTC")
self.assertEqual(result[0].isoformat(), "2019-12-30T00:00:00+00:00")
self.assertEqual(result[0].isoformat(), "2020-01-13T00:00:00+00:00")
self.assertEqual(result[1].isoformat(), "2020-01-06T00:00:00+00:00")
self.assertEqual(result[2].isoformat(), "2020-01-13T00:00:00+00:00")
self.assertEqual(result[2].isoformat(), "2019-12-30T00:00:00+00:00")
def test_non_utc_works(self) -> None:
result = week_boundaries(3, "Europe/Riga")
self.assertEqual(result[0].isoformat(), "2019-12-30T00:00:00+02:00")
self.assertEqual(result[0].isoformat(), "2020-01-13T00:00:00+02:00")
self.assertEqual(result[1].isoformat(), "2020-01-06T00:00:00+02:00")
self.assertEqual(result[2].isoformat(), "2020-01-13T00:00:00+02:00")
self.assertEqual(result[2].isoformat(), "2019-12-30T00:00:00+02:00")
class SecondsInMonthTestCase(TestCase):