from typing import List, Sequence from .monitor_check_status import Check from .monitor_uptime import uptime from .time_unix_to_iso import unix_to_iso from .monitor_uptime_bars_types import BarState, DayBar _DAY = 86400 def _whole(value: object) -> bool: return isinstance(value, int) and not isinstance(value, bool) def uptime_bars(checks: Sequence[Check], now: int, days: int, max_gap_seconds: int) -> List[DayBar]: """The last `days` UTC days ending with today, oldest first, each measured by monitor.uptime. Today's bar ends at `now`, so it never shows the rest of today as unknown.""" if not _whole(now): raise ValueError("now must be whole seconds") if not _whole(days) or days < 1 or days > 366: raise ValueError("days must be 1 to 366, received %s" % (days,)) # Floored, not truncated: before 1970 today starts on the earlier midnight. today_start = now - now % _DAY bars: List[DayBar] = [] for i in range(days - 1, -1, -1): start = today_start - i * _DAY end = now if i == 0 else start + _DAY r = uptime(checks, start, end, max_gap_seconds) state: BarState if r.uptime_basis_points is None: state = "no-data" elif r.down_seconds > 0: state = "outage" elif r.degraded_seconds > 0: state = "degraded" else: state = "operational" bars.append(DayBar( date=unix_to_iso(start)[:10], uptime_basis_points=r.uptime_basis_points, up_seconds=r.up_seconds, degraded_seconds=r.degraded_seconds, down_seconds=r.down_seconds, unknown_seconds=r.unknown_seconds, state=state, )) return bars