diff --git a/app/api/v1/__init__.py b/app/api/v1/__init__.py index 95b4385..754158d 100644 --- a/app/api/v1/__init__.py +++ b/app/api/v1/__init__.py @@ -5,6 +5,7 @@ from fastapi import APIRouter from app.api.v1 import ( admin, auth, + dashboard, extractions, files, main, @@ -19,6 +20,7 @@ api_router = APIRouter(prefix="/v1") # Include all route modules api_router.include_router(auth.router, tags=["authentication"]) +api_router.include_router(dashboard.router, tags=["dashboard"]) api_router.include_router(extractions.router, tags=["extractions"]) api_router.include_router(files.router, tags=["files"]) api_router.include_router(main.router, tags=["main"]) diff --git a/app/api/v1/dashboard.py b/app/api/v1/dashboard.py new file mode 100644 index 0000000..a24c51b --- /dev/null +++ b/app/api/v1/dashboard.py @@ -0,0 +1,29 @@ +"""Dashboard API endpoints.""" + +from typing import Annotated, Any + +from fastapi import APIRouter, Depends + +from app.core.dependencies import get_current_user, get_dashboard_service +from app.models.user import User +from app.services.dashboard import DashboardService + +router = APIRouter(prefix="/dashboard", tags=["dashboard"]) + + +@router.get("/soundboard-statistics") +async def get_soundboard_statistics( + _current_user: Annotated[User, Depends(get_current_user)], + dashboard_service: Annotated[DashboardService, Depends(get_dashboard_service)], +) -> dict[str, Any]: + """Get soundboard statistics.""" + return await dashboard_service.get_soundboard_statistics() + + +@router.get("/track-statistics") +async def get_track_statistics( + _current_user: Annotated[User, Depends(get_current_user)], + dashboard_service: Annotated[DashboardService, Depends(get_dashboard_service)], +) -> dict[str, Any]: + """Get track statistics.""" + return await dashboard_service.get_track_statistics() diff --git a/app/core/dependencies.py b/app/core/dependencies.py index 5a7e56e..02d785a 100644 --- a/app/core/dependencies.py +++ b/app/core/dependencies.py @@ -9,7 +9,9 @@ from app.core.database import get_db from app.core.logging import get_logger from app.models.user import User from app.services.auth import AuthService +from app.services.dashboard import DashboardService from app.services.oauth import OAuthService +from app.repositories.sound import SoundRepository from app.utils.auth import JWTUtils, TokenUtils logger = get_logger(__name__) @@ -184,3 +186,11 @@ async def get_admin_user( detail="Not enough permissions", ) return current_user + + +async def get_dashboard_service( + session: Annotated[AsyncSession, Depends(get_db)], +) -> DashboardService: + """Get the dashboard service.""" + sound_repository = SoundRepository(session) + return DashboardService(sound_repository) diff --git a/app/repositories/sound.py b/app/repositories/sound.py index 5be2404..94ff96e 100644 --- a/app/repositories/sound.py +++ b/app/repositories/sound.py @@ -178,3 +178,49 @@ class SoundRepository(BaseRepository[Sound]): search_query, sound_types, sort_by, sort_order ) raise + + async def get_soundboard_statistics(self) -> dict[str, int | float]: + """Get statistics for SDB type sounds.""" + try: + statement = select( + func.count(Sound.id).label("count"), + func.sum(Sound.play_count).label("total_plays"), + func.sum(Sound.duration).label("total_duration"), + func.sum(Sound.size + func.coalesce(Sound.normalized_size, 0)).label("total_size") + ).where(Sound.type == "SDB") + + result = await self.session.exec(statement) + row = result.first() + + return { + "count": row.count if row.count is not None else 0, + "total_plays": row.total_plays if row.total_plays is not None else 0, + "total_duration": row.total_duration if row.total_duration is not None else 0, + "total_size": row.total_size if row.total_size is not None else 0 + } + except Exception: + logger.exception("Failed to get soundboard statistics") + raise + + async def get_track_statistics(self) -> dict[str, int | float]: + """Get statistics for EXT type sounds.""" + try: + statement = select( + func.count(Sound.id).label("count"), + func.sum(Sound.play_count).label("total_plays"), + func.sum(Sound.duration).label("total_duration"), + func.sum(Sound.size + func.coalesce(Sound.normalized_size, 0)).label("total_size") + ).where(Sound.type == "EXT") + + result = await self.session.exec(statement) + row = result.first() + + return { + "count": row.count if row.count is not None else 0, + "total_plays": row.total_plays if row.total_plays is not None else 0, + "total_duration": row.total_duration if row.total_duration is not None else 0, + "total_size": row.total_size if row.total_size is not None else 0 + } + except Exception: + logger.exception("Failed to get track statistics") + raise diff --git a/app/services/dashboard.py b/app/services/dashboard.py new file mode 100644 index 0000000..1540dbe --- /dev/null +++ b/app/services/dashboard.py @@ -0,0 +1,46 @@ +"""Dashboard service for statistics and analytics.""" + +from typing import Any + +from app.core.logging import get_logger +from app.repositories.sound import SoundRepository + +logger = get_logger(__name__) + + +class DashboardService: + """Service for dashboard statistics and analytics.""" + + def __init__(self, sound_repository: SoundRepository) -> None: + """Initialize the dashboard service.""" + self.sound_repository = sound_repository + + async def get_soundboard_statistics(self) -> dict[str, Any]: + """Get comprehensive soundboard statistics.""" + try: + stats = await self.sound_repository.get_soundboard_statistics() + + return { + "sound_count": stats["count"], + "total_play_count": stats["total_plays"], + "total_duration": stats["total_duration"], + "total_size": stats["total_size"], + } + except Exception: + logger.exception("Failed to get soundboard statistics") + raise + + async def get_track_statistics(self) -> dict[str, Any]: + """Get comprehensive track statistics.""" + try: + stats = await self.sound_repository.get_track_statistics() + + return { + "track_count": stats["count"], + "total_play_count": stats["total_plays"], + "total_duration": stats["total_duration"], + "total_size": stats["total_size"], + } + except Exception: + logger.exception("Failed to get track statistics") + raise \ No newline at end of file