"""Analytics API endpoints for accessing system analytics and reports. Provides REST API endpoints for querying analytics data including download statistics, series popularity, storage analysis, and performance reports. """ from typing import Optional from fastapi import APIRouter, Depends, HTTPException from pydantic import BaseModel from sqlalchemy.ext.asyncio import AsyncSession from src.server.database.connection import get_db_session from src.server.services.analytics_service import get_analytics_service router = APIRouter(prefix="/api/analytics", tags=["analytics"]) class DownloadStatsResponse(BaseModel): """Download statistics response model.""" total_downloads: int successful_downloads: int failed_downloads: int total_bytes_downloaded: int average_speed_mbps: float success_rate: float average_duration_seconds: float class SeriesPopularityResponse(BaseModel): """Series popularity response model.""" series_name: str download_count: int total_size_bytes: int last_download: Optional[str] success_rate: float class StorageAnalysisResponse(BaseModel): """Storage analysis response model.""" total_storage_bytes: int used_storage_bytes: int free_storage_bytes: int storage_percent_used: float downloads_directory_size_bytes: int cache_directory_size_bytes: int logs_directory_size_bytes: int class PerformanceReportResponse(BaseModel): """Performance report response model.""" period_start: str period_end: str downloads_per_hour: float average_queue_size: float peak_memory_usage_mb: float average_cpu_percent: float uptime_seconds: float error_rate: float class SummaryReportResponse(BaseModel): """Comprehensive analytics summary response.""" timestamp: str download_stats: DownloadStatsResponse series_popularity: list[SeriesPopularityResponse] storage_analysis: StorageAnalysisResponse performance_report: PerformanceReportResponse @router.get("/downloads", response_model=DownloadStatsResponse) async def get_download_statistics( days: int = 30, db: AsyncSession = Depends(get_db_session), ) -> DownloadStatsResponse: """Get download statistics for specified period. Args: days: Number of days to analyze (default: 30) db: Database session Returns: Download statistics including success rates and speeds """ try: service = get_analytics_service() stats = await service.get_download_stats(db, days=days) return DownloadStatsResponse( total_downloads=stats.total_downloads, successful_downloads=stats.successful_downloads, failed_downloads=stats.failed_downloads, total_bytes_downloaded=stats.total_bytes_downloaded, average_speed_mbps=stats.average_speed_mbps, success_rate=stats.success_rate, average_duration_seconds=stats.average_duration_seconds, ) except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get download statistics: {str(e)}", ) @router.get( "/series-popularity", response_model=list[SeriesPopularityResponse] ) async def get_series_popularity( limit: int = 10, db: AsyncSession = Depends(get_db_session), ) -> list[SeriesPopularityResponse]: """Get most popular series by download count. Args: limit: Maximum number of series (default: 10) db: Database session Returns: List of series sorted by popularity """ try: service = get_analytics_service() popularity = await service.get_series_popularity(db, limit=limit) return [ SeriesPopularityResponse( series_name=p.series_name, download_count=p.download_count, total_size_bytes=p.total_size_bytes, last_download=p.last_download, success_rate=p.success_rate, ) for p in popularity ] except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get series popularity: {str(e)}", ) @router.get( "/storage", response_model=StorageAnalysisResponse ) async def get_storage_analysis() -> StorageAnalysisResponse: """Get current storage usage analysis. Returns: Storage breakdown including disk and directory usage """ try: service = get_analytics_service() analysis = service.get_storage_analysis() return StorageAnalysisResponse( total_storage_bytes=analysis.total_storage_bytes, used_storage_bytes=analysis.used_storage_bytes, free_storage_bytes=analysis.free_storage_bytes, storage_percent_used=analysis.storage_percent_used, downloads_directory_size_bytes=( analysis.downloads_directory_size_bytes ), cache_directory_size_bytes=( analysis.cache_directory_size_bytes ), logs_directory_size_bytes=( analysis.logs_directory_size_bytes ), ) except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get storage analysis: {str(e)}", ) @router.get( "/performance", response_model=PerformanceReportResponse ) async def get_performance_report( hours: int = 24, db: AsyncSession = Depends(get_db_session), ) -> PerformanceReportResponse: """Get performance metrics for specified period. Args: hours: Number of hours to analyze (default: 24) db: Database session Returns: Performance metrics including speeds and system usage """ try: service = get_analytics_service() report = await service.get_performance_report(db, hours=hours) return PerformanceReportResponse( period_start=report.period_start, period_end=report.period_end, downloads_per_hour=report.downloads_per_hour, average_queue_size=report.average_queue_size, peak_memory_usage_mb=report.peak_memory_usage_mb, average_cpu_percent=report.average_cpu_percent, uptime_seconds=report.uptime_seconds, error_rate=report.error_rate, ) except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to get performance report: {str(e)}", ) @router.get("/summary", response_model=SummaryReportResponse) async def get_summary_report( db: AsyncSession = Depends(get_db_session), ) -> SummaryReportResponse: """Get comprehensive analytics summary. Args: db: Database session Returns: Complete analytics report with all metrics """ try: service = get_analytics_service() summary = await service.generate_summary_report(db) return SummaryReportResponse( timestamp=summary["timestamp"], download_stats=DownloadStatsResponse( **summary["download_stats"] ), series_popularity=[ SeriesPopularityResponse(**p) for p in summary["series_popularity"] ], storage_analysis=StorageAnalysisResponse( **summary["storage_analysis"] ), performance_report=PerformanceReportResponse( **summary["performance_report"] ), ) except Exception as e: raise HTTPException( status_code=500, detail=f"Failed to generate summary report: {str(e)}", )