from datetime import datetime import asyncpg from utils.read.models import CaptureGap, CaptureStatus _STATUS = """ SELECT started_at, seen_at, seen_at >= now() - interval '150 seconds' AS live FROM capture_uptime WHERE account_id = $1 ORDER BY started_at DESC LIMIT 1 """ _GAPS = """ SELECT seen_at AS start, next_start AS "end" FROM ( SELECT seen_at, lead(started_at) OVER (ORDER BY started_at) AS next_start FROM capture_uptime WHERE account_id = $1 ) stretches WHERE next_start - seen_at > interval '30 minutes' AND ($2::timestamptz IS NULL OR next_start >= $2) AND ($3::timestamptz IS NULL OR seen_at <= $3) ORDER BY seen_at """ async def capture_status(pool: asyncpg.Pool, account_id: int) -> CaptureStatus: row = await pool.fetchrow(_STATUS, account_id) if row is None: return CaptureStatus(live=False, since=None, last_seen=None) return CaptureStatus( live=row["live"], since=row["started_at"], last_seen=row["seen_at"] ) async def capture_gaps( pool: asyncpg.Pool, account_id: int, *, date_from: datetime | None = None, date_to: datetime | None = None, ) -> list[CaptureGap]: rows = await pool.fetch(_GAPS, account_id, date_from, date_to) return [CaptureGap(**dict(row)) for row in rows]