feat: Add dashboard API endpoints and service for sound statistics
Some checks failed
Backend CI / lint (push) Failing after 4m52s
Backend CI / test (push) Failing after 3m42s

This commit is contained in:
JSC
2025-08-11 11:16:45 +02:00
parent bb1f036caa
commit 49ad6c8581
5 changed files with 133 additions and 0 deletions

View File

@@ -5,6 +5,7 @@ from fastapi import APIRouter
from app.api.v1 import ( from app.api.v1 import (
admin, admin,
auth, auth,
dashboard,
extractions, extractions,
files, files,
main, main,
@@ -19,6 +20,7 @@ api_router = APIRouter(prefix="/v1")
# Include all route modules # Include all route modules
api_router.include_router(auth.router, tags=["authentication"]) 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(extractions.router, tags=["extractions"])
api_router.include_router(files.router, tags=["files"]) api_router.include_router(files.router, tags=["files"])
api_router.include_router(main.router, tags=["main"]) api_router.include_router(main.router, tags=["main"])

29
app/api/v1/dashboard.py Normal file
View File

@@ -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()

View File

@@ -9,7 +9,9 @@ from app.core.database import get_db
from app.core.logging import get_logger from app.core.logging import get_logger
from app.models.user import User from app.models.user import User
from app.services.auth import AuthService from app.services.auth import AuthService
from app.services.dashboard import DashboardService
from app.services.oauth import OAuthService from app.services.oauth import OAuthService
from app.repositories.sound import SoundRepository
from app.utils.auth import JWTUtils, TokenUtils from app.utils.auth import JWTUtils, TokenUtils
logger = get_logger(__name__) logger = get_logger(__name__)
@@ -184,3 +186,11 @@ async def get_admin_user(
detail="Not enough permissions", detail="Not enough permissions",
) )
return current_user 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)

View File

@@ -178,3 +178,49 @@ class SoundRepository(BaseRepository[Sound]):
search_query, sound_types, sort_by, sort_order search_query, sound_types, sort_by, sort_order
) )
raise 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

46
app/services/dashboard.py Normal file
View File

@@ -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