from datetime import UTC, datetime, timedelta import asyncpg from utils.read.accounts import self_user_id from utils.read.models import ResponseStats, VolumeBucket async def message_volume( pool: asyncpg.Pool, account_id: int, chat_id: int, *, days: int = 90 ) -> list[VolumeBucket]: self_id = await self_user_id(pool, account_id) rows = await pool.fetch( "SELECT date_trunc('day', date) AS bucket, count(*) AS total, " "count(*) FILTER (WHERE sender_id = $3) AS outgoing " "FROM messages " "WHERE account_id = $1 AND chat_id = $2 AND date >= $4 " "GROUP BY bucket ORDER BY bucket", account_id, chat_id, self_id, datetime.now(UTC) - timedelta(days=days), ) return [ VolumeBucket( bucket=row["bucket"], total=row["total"], outgoing=row["outgoing"], incoming=row["total"] - row["outgoing"], ) for row in rows ] async def response_stats( pool: asyncpg.Pool, account_id: int, chat_id: int, *, days: int = 365 ) -> ResponseStats: self_id = await self_user_id(pool, account_id) rows = await pool.fetch( "WITH ordered AS (" "SELECT sender_id, date, " "lag(sender_id) OVER w AS prev_sender, " "lag(date) OVER w AS prev_date " "FROM messages " "WHERE account_id = $1 AND chat_id = $2 AND sender_id IS NOT NULL " "AND date >= $4 " "WINDOW w AS (ORDER BY date, message_id)), " "resp AS (" "SELECT (sender_id = $3) AS is_mine, " "EXTRACT(EPOCH FROM (date - prev_date)) AS secs " "FROM ordered " "WHERE prev_sender IS NOT NULL AND prev_sender <> sender_id) " "SELECT is_mine, count(*) AS n, " "percentile_cont(0.5) WITHIN GROUP (ORDER BY secs) AS median_secs " "FROM resp GROUP BY is_mine", account_id, chat_id, self_id, datetime.now(UTC) - timedelta(days=days), ) stats = ResponseStats( mine_median_seconds=None, mine_count=0, their_median_seconds=None, their_count=0 ) for row in rows: if row["is_mine"]: stats.mine_median_seconds = row["median_secs"] stats.mine_count = row["n"] else: stats.their_median_seconds = row["median_secs"] stats.their_count = row["n"] return stats