"""Per-tenant analytics event recording + aggregation.""" from __future__ import annotations from datetime import UTC, datetime, timedelta from typing import Any from sqlalchemy import func, select from sqlalchemy.ext.asyncio import AsyncSession from app.models import Event def record( db: AsyncSession, tenant_id: int | None, type_: str, *, session_id: str | None = None, meta: dict[str, Any] | None = None, ) -> None: """Queue an analytics event (persisted with the turn's commit).""" db.add(Event(tenant_id=tenant_id, type=type_, session_id=session_id, meta=meta or {})) async def summary(db: AsyncSession, tenant_id: int, days: int = 30) -> dict[str, Any]: since = datetime.now(UTC) - timedelta(days=days) rows = ( await db.execute( select(Event.type, func.count()) .where(Event.tenant_id == tenant_id, Event.created_at >= since) .group_by(Event.type) ) ).all() counts = {t: int(n) for t, n in rows} sessions = ( await db.execute( select(func.count(func.distinct(Event.session_id))).where( Event.tenant_id == tenant_id, Event.created_at >= since ) ) ).scalar() or 0 return { "days": days, "conversations": int(sessions), "messages": counts.get("turn", 0), "product_views": counts.get("products_shown", 0), "cart_links": counts.get("cart_link", 0), "order_lookups": counts.get("order_lookup", 0), "escalations": counts.get("escalation", 0), "stock_watches": counts.get("stock_watch", 0), "by_type": counts, }