from uuid import UUID from fastapi import APIRouter, Depends, HTTPException, status from sqlalchemy import select, func from sqlalchemy.ext.asyncio import AsyncSession from app.core.database import get_db from app.api.deps import get_current_active_user from app.models import User, Session, SessionRating from app.models.step_library import StepLibrary, StepRating from app.schemas.analytics import SessionRatingCreate, SessionRatingResponse, StepFeedbackCreate router = APIRouter(tags=["ratings"]) @router.post("/sessions/{session_id}/rate", response_model=SessionRatingResponse, status_code=status.HTTP_201_CREATED) async def rate_session( session_id: UUID, data: SessionRatingCreate, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_active_user), ): """Submit a CSAT rating (1-5) for a completed session.""" # Verify session exists and belongs to user result = await db.execute(select(Session).where(Session.id == session_id)) session = result.scalar_one_or_none() if not session: raise HTTPException(status_code=404, detail="Session not found") if session.user_id != current_user.id: raise HTTPException(status_code=403, detail="Not your session") if not session.completed_at: raise HTTPException(status_code=400, detail="Session not completed yet") # Check for duplicate existing = await db.execute( select(SessionRating).where(SessionRating.session_id == session_id) ) if existing.scalar_one_or_none(): raise HTTPException(status_code=409, detail="Session already rated") rating = SessionRating( session_id=session_id, user_id=current_user.id, tree_id=session.tree_id, account_id=current_user.account_id, rating=data.rating, comment=data.comment, ) db.add(rating) await db.commit() await db.refresh(rating) return SessionRatingResponse( id=str(rating.id), session_id=str(rating.session_id), rating=rating.rating, comment=rating.comment, created_at=rating.created_at, ) @router.post("/steps/{step_id}/feedback", status_code=status.HTTP_201_CREATED) async def submit_step_feedback( step_id: UUID, data: StepFeedbackCreate, db: AsyncSession = Depends(get_db), current_user: User = Depends(get_current_active_user), ): """Submit thumbs up/down feedback for a step used in a session.""" # Verify step exists result = await db.execute(select(StepLibrary).where(StepLibrary.id == step_id)) step = result.scalar_one_or_none() if not step: raise HTTPException(status_code=404, detail="Step not found") session_uuid = UUID(data.session_id) # Check for existing feedback for this step+user+session existing_result = await db.execute( select(StepRating).where( StepRating.step_id == step_id, StepRating.user_id == current_user.id, StepRating.session_id == session_uuid, ) ) existing = existing_result.scalar_one_or_none() if existing: existing.was_helpful = data.was_helpful resp_status = "updated" else: new_rating = StepRating( step_id=step_id, user_id=current_user.id, account_id=current_user.account_id, session_id=session_uuid, was_helpful=data.was_helpful, # rating is nullable now — thumbs-only mode ) db.add(new_rating) resp_status = "created" # Update aggregates on step_library await _update_step_helpful_counts(db, step_id) await db.commit() return {"step_id": str(step_id), "was_helpful": data.was_helpful, "status": resp_status} async def _update_step_helpful_counts(db: AsyncSession, step_id: UUID): """Recalculate helpful_yes and helpful_no on step_library.""" yes_q = await db.execute( select(func.count()).select_from(StepRating).where( StepRating.step_id == step_id, StepRating.was_helpful == True ) ) no_q = await db.execute( select(func.count()).select_from(StepRating).where( StepRating.step_id == step_id, StepRating.was_helpful == False ) ) await db.execute( StepLibrary.__table__.update() .where(StepLibrary.id == step_id) .values(helpful_yes=yes_q.scalar() or 0, helpful_no=no_q.scalar() or 0) )