smart-line-bot / app /api /v1 /endpoints /analytics.py
Smiel2's picture
Initial commit
2eae977 verified
Raw
History Blame Contribute Delete
5.09 kB
"""
Usage analytics endpoints for monitoring and insights.
"""
import logging
from typing import Optional
from datetime import datetime, timedelta
from fastapi import APIRouter, Depends, Query
from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy import select, func
from app.core.database import get_db
logger = logging.getLogger(__name__)
router = APIRouter(prefix="/analytics", tags=["analytics"])
@router.get("/overview")
async def get_overview(
db: AsyncSession = Depends(get_db),
):
"""Get system overview analytics."""
from app.Models.user import User
from app.Models.line_user import LINEUser
from app.Models.ai_conversation import AIConversation
from app.Models.scraping_job import ScrapingJob
from app.Models.api_key import APIKey
# Total users
user_result = await db.execute(select(func.count(User.id)))
total_users = user_result.scalar()
# LINE users
line_result = await db.execute(select(func.count(LINEUser.id)))
line_users = line_result.scalar()
# Active conversations
conv_result = await db.execute(
select(func.count(AIConversation.id)).where(AIConversation.is_active == True)
)
active_conversations = conv_result.scalar()
# Scraping jobs
job_result = await db.execute(select(func.count(ScrapingJob.id)))
total_jobs = job_result.scalar()
# Active API keys
key_result = await db.execute(
select(func.count(APIKey.id)).where(APIKey.is_active == True)
)
active_keys = key_result.scalar()
return {
"total_users": total_users or 0,
"line_users": line_users or 0,
"active_conversations": active_conversations or 0,
"total_scraping_jobs": total_jobs or 0,
"active_api_keys": active_keys or 0,
}
@router.get("/messages")
async def get_message_stats(
days: int = Query(7, ge=1, le=90),
db: AsyncSession = Depends(get_db),
):
"""Get message statistics for the last N days."""
from app.Models.ai_conversation import AIMessage
start_date = datetime.utcnow() - timedelta(days=days)
# Count messages per day
result = await db.execute(
select(
func.date(AIMessage.created_at).label("date"),
func.count(AIMessage.id).label("count")
)
.where(AIMessage.created_at >= start_date)
.group_by(func.date(AIMessage.created_at))
.order_by(func.date(AIMessage.created_at))
)
rows = result.all()
return {
"period_days": days,
"total_messages": sum(r.count for r in rows),
"daily_breakdown": [
{"date": str(r.date), "count": r.count}
for r in rows
]
}
@router.get("/platforms")
async def get_platform_stats(
db: AsyncSession = Depends(get_db),
):
"""Get user count by platform."""
from app.Models.line_user import LINEUser
result = await db.execute(
select(LINEUser.platform, func.count(LINEUser.id))
.group_by(LINEUser.platform)
)
rows = result.all()
platforms = {}
for platform, count in rows:
platforms[platform or "unknown"] = count
return {"platforms": platforms}
@router.get("/api-usage")
async def get_api_usage(
days: int = Query(30, ge=1, le=90),
db: AsyncSession = Depends(get_db),
):
"""Get API key usage statistics."""
from app.Models.api_key import APIKey
start_date = datetime.utcnow() - timedelta(days=days)
result = await db.execute(
select(
APIKey.name,
APIKey.can_send_message,
APIKey.can_read_conversation,
APIKey.can_read_stats,
APIKey.last_used_at,
)
.where(APIKey.last_used_at >= start_date)
.order_by(APIKey.last_used_at.desc())
)
rows = result.all()
return {
"period_days": days,
"active_keys": len(rows),
"usage": [
{
"key_name": r.name,
"last_used": r.last_used_at.isoformat() if r.last_used_at else None,
}
for r in rows
]
}
@router.get("/scraping")
async def get_scraping_stats(
days: int = Query(7, ge=1, le=90),
db: AsyncSession = Depends(get_db),
):
"""Get scraping job statistics."""
from app.Models.scraping_job import ScrapingJob
start_date = datetime.utcnow() - timedelta(days=days)
result = await db.execute(
select(ScrapingJob.status, func.count(ScrapingJob.id))
.where(ScrapingJob.created_at >= start_date)
.group_by(ScrapingJob.status)
)
rows = result.all()
status_counts = {}
for status, count in rows:
status_counts[status or "unknown"] = count
return {
"period_days": days,
"total_jobs": sum(status_counts.values()),
"by_status": status_counts,
}
@router.get("/health")
async def analytics_health():
"""Analytics service health check."""
return {"status": "healthy", "service": "analytics"}