from datetime import datetime, timedelta, timezone from sqlalchemy import func from sqlalchemy.orm import Session from ..models import Consumer, Event, ParseJob from ..schemas import AnalyticsSummary, TimelinePoint, TopItem def get_analytics_summary(db: Session) -> AnalyticsSummary: now = datetime.now(timezone.utc) day_ago = now - timedelta(days=1) total_events = db.query(func.count(Event.id)).scalar() or 0 events_with_coords = ( db.query(func.count(Event.id)) .filter(Event.latitude.isnot(None), Event.longitude.isnot(None)) .scalar() or 0 ) events_last_24h = ( db.query(func.count(Event.id)) .filter(Event.ingested_at >= day_ago) .scalar() or 0 ) total_consumers = db.query(func.count(Consumer.id)).scalar() or 0 active_consumers = ( db.query(func.count(Consumer.id)) .filter(Consumer.is_active.is_(True)) .scalar() or 0 ) total_jobs = db.query(func.count(ParseJob.id)).scalar() or 0 pending_jobs = ( db.query(func.count(ParseJob.id)) .filter(ParseJob.status.in_(["queued", "running", "pending"])) .scalar() or 0 ) return AnalyticsSummary( total_events=total_events, events_with_coords=events_with_coords, events_last_24h=events_last_24h, total_consumers=total_consumers, active_consumers=active_consumers, total_jobs=total_jobs, pending_jobs=pending_jobs, ) def get_timeline(db: Session, days: int = 30) -> list[TimelinePoint]: since = datetime.now(timezone.utc) - timedelta(days=days) day_col = func.date_trunc("day", Event.ingested_at).label("day") rows = ( db.query(day_col, func.count(Event.id).label("count")) .filter(Event.ingested_at >= since) .group_by(day_col) .order_by(day_col) .all() ) return [ TimelinePoint(date=row.day.date().isoformat(), count=row.count) for row in rows ] def get_top_localities(db: Session, limit: int = 10) -> list[TopItem]: rows = ( db.query(Event.locality, func.count(Event.id).label("count")) .filter(Event.locality != "", Event.locality.isnot(None)) .group_by(Event.locality) .order_by(func.count(Event.id).desc()) .limit(limit) .all() ) return [TopItem(name=row.locality, count=row.count) for row in rows] def get_top_regions(db: Session, limit: int = 10) -> list[TopItem]: rows = ( db.query(Event.region, func.count(Event.id).label("count")) .filter(Event.region.isnot(None), Event.region != "") .group_by(Event.region) .order_by(func.count(Event.id).desc()) .limit(limit) .all() ) return [TopItem(name=row.region, count=row.count) for row in rows]