Arag / app /services /analytics.py
AuthorBot
Restructure project for HF Spaces deployment
772f852
Raw
History Blame Contribute Delete
2.2 kB
"""Author RAG — Analytics Service.
Event tracking, session aggregation, and MaxMind geo-location.
Per implementation plan features #43-50.
"""
import hashlib
from datetime import datetime, timezone
from typing import Any
import structlog
from sqlalchemy.ext.asyncio import AsyncSession
logger = structlog.get_logger(__name__)
def fingerprint(ip: str, user_agent: str) -> str:
"""SHA-256 fingerprint from IP + User-Agent. No PII stored.
Args:
ip: Client IP address.
user_agent: HTTP User-Agent header.
Returns:
32-character hex fingerprint.
"""
combined = f"{ip}|{user_agent}"
return hashlib.sha256(combined.encode()).hexdigest()[:32]
async def track_event(
db: AsyncSession,
author_id: str,
event_type: str,
session_id: str,
book_id: str | None = None,
metadata: dict[str, Any] | None = None,
) -> None:
"""Persist an analytics event.
Event types: chat_start, message, buy_link_click, rating, session_end.
Args:
db: Database session.
author_id: Author UUID.
event_type: Event category string.
session_id: Reader session UUID.
book_id: Optional book UUID.
metadata: Additional event data.
"""
try:
from app.models.analytics import AnalyticsEvent
event = AnalyticsEvent(
author_id=author_id,
event_type=event_type,
session_id=session_id,
book_id=book_id,
meta=metadata or {},
created_at=datetime.now(timezone.utc),
)
db.add(event)
await db.commit()
except Exception as e:
logger.warning("Failed to track event", event_type=event_type, error=str(e))
async def get_geo_country(ip: str) -> str | None:
"""Look up country for an IP via MaxMind GeoLite2.
Returns None if GeoIP database is not available.
"""
try:
import geoip2.database
from app.config import get_settings
cfg = get_settings()
with geoip2.database.Reader(cfg.GEO_DB_PATH) as reader:
response = reader.country(ip)
return response.country.iso_code
except Exception:
return None