Files
ai_agent/backend/app/api/routes/v1/admin_ratings.py
T
furyhawk 8351e73d39 feat: add Zustand stores for conversation, file preview, sidebar, theme, and knowledge base selection
- Implemented `conversation-store` for managing conversations and messages.
- Created `file-preview-store` to handle file preview state.
- Added `sidebar-store` for sidebar visibility management.
- Developed `theme-store` for theme persistence and management.
- Introduced `kb-selection-store` for managing active knowledge base selections with persistence.

chore: define API and chat types

- Added types for API responses, authentication, chat messages, conversations, and projects.
- Defined interfaces for various entities including users, sessions, and message ratings.

build: configure TypeScript and testing setup

- Set up `tsconfig.json` for TypeScript configuration.
- Created `vitest.config.ts` for testing configuration with Vitest.
- Added `vitest.setup.ts` for global test setup including mocks for Next.js router and media queries.
- Configured Vercel deployment settings in `vercel.json`.
2026-06-11 16:54:43 +08:00

96 lines
3.2 KiB
Python

"""Admin endpoints for message ratings.
Provides endpoints for administrators to view and analyze user ratings
on AI assistant messages.
The endpoints are:
- GET /admin/ratings - List all ratings with filtering
- GET /admin/ratings/summary - Get aggregated rating statistics
- GET /admin/ratings/export - Export ratings as JSON or CSV
"""
from typing import Any
from fastapi import APIRouter, Query
from fastapi.responses import JSONResponse, StreamingResponse
from app.api.deps import CurrentAdmin, MessageRatingSvc
from app.schemas.message_rating import MessageRatingList, RatingSummary
router = APIRouter()
@router.get("", response_model=MessageRatingList)
async def list_ratings_admin(
rating_service: MessageRatingSvc,
_: CurrentAdmin,
skip: int = Query(0, ge=0),
limit: int = Query(50, ge=1, le=100),
rating_filter: int | None = Query(None, ge=-1, le=1, description="Filter by rating value"),
with_comments_only: bool = Query(False, description="Only show ratings with comments"),
) -> Any:
"""List all ratings with filtering (admin only).
Returns paginated list of ratings with optional filters:
- rating_filter: Filter by rating value (1 for likes, -1 for dislikes)
- with_comments_only: Only return ratings that have comments
Results are ordered by creation date (newest first).
"""
items, total = await rating_service.list_ratings(
skip=skip,
limit=limit,
rating_filter=rating_filter,
with_comments_only=with_comments_only,
)
return MessageRatingList(items=items, total=total)
@router.get("/summary", response_model=RatingSummary)
async def get_rating_summary(
rating_service: MessageRatingSvc,
_: CurrentAdmin,
days: int = Query(30, ge=1, le=365, description="Number of days to include"),
) -> Any:
"""Get aggregated rating statistics (admin only).
Returns summary statistics including:
- Total ratings count
- Like/dislike counts
- Average rating (-1.0 to 1.0)
- Count of ratings with comments
- Daily breakdown of ratings
The `days` parameter controls the time window (default: 30 days).
"""
return await rating_service.get_summary(days=days)
@router.get("/export")
async def export_ratings(
rating_service: MessageRatingSvc,
_: CurrentAdmin,
export_format: str = Query("json", description="Export format: 'json' or 'csv'"),
rating_filter: int | None = Query(None, ge=-1, le=1, description="Filter by rating value"),
with_comments_only: bool = Query(False, description="Only show ratings with comments"),
) -> Any:
"""Export all ratings as JSON or CSV (admin only).
CSV is streamed row-by-row; JSON collects into a single document.
"""
result = await rating_service.export_ratings(
export_format=export_format,
rating_filter=rating_filter,
with_comments_only=with_comments_only,
)
if result.media_type == "text/csv":
return StreamingResponse(
result.payload,
media_type="text/csv",
headers={"Content-Disposition": result.content_disposition},
)
return JSONResponse(
content=result.payload,
headers={"Content-Disposition": result.content_disposition},
)