80 lines
2.7 KiB
Python
80 lines
2.7 KiB
Python
from datetime import UTC, datetime, timedelta
|
|
|
|
import asyncpg
|
|
|
|
from utils.read.accounts import self_user_id
|
|
from utils.read.models import ResponseStats, VolumeBucket
|
|
from utils.read.period import period_bounds
|
|
|
|
_VOLUME = """
|
|
WITH counts AS (
|
|
SELECT (date AT TIME ZONE $5)::date AS day, 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 1
|
|
)
|
|
SELECT local_day::date AS bucket, coalesce(total, 0) AS total,
|
|
coalesce(outgoing, 0) AS outgoing
|
|
FROM generate_series(($4 AT TIME ZONE $5)::date, (now() AT TIME ZONE $5)::date,
|
|
interval '1 day') AS local_day
|
|
LEFT JOIN counts ON counts.day = local_day::date
|
|
ORDER BY 1
|
|
"""
|
|
|
|
|
|
async def message_volume(
|
|
pool: asyncpg.Pool, account_id: int, chat_id: int, *, days: int, tz: str
|
|
) -> list[VolumeBucket]:
|
|
self_id = await self_user_id(pool, account_id)
|
|
since, _ = await period_bounds(pool, days, tz)
|
|
rows = await pool.fetch(_VOLUME, account_id, chat_id, self_id, since, tz)
|
|
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
|