diff --git a/backend/app/routers/flashcards.py b/backend/app/routers/flashcards.py index 6c01389..395e970 100644 --- a/backend/app/routers/flashcards.py +++ b/backend/app/routers/flashcards.py @@ -147,6 +147,67 @@ def list_trashed_decks( ).order_by(FlashcardDeck.deleted_at.desc()).all() +@router.get("/shared") +def list_shared_decks( + limit: int = Query(20, le=100), + offset: int = Query(0), + db: Session = Depends(get_db), + current_user: User = Depends(get_current_user), +): + """List shared decks sorted by average rating (desc), with pagination.""" + from sqlalchemy import func + + avg_rating = db.query( + FlashcardDeckRating.deck_id, + func.avg(FlashcardDeckRating.rating).label("avg_rating"), + func.count(FlashcardDeckRating.id).label("rating_count"), + ).group_by(FlashcardDeckRating.deck_id).subquery() + + my_ratings = {} + for r in db.query(FlashcardDeckRating).filter(FlashcardDeckRating.user_id == current_user.id).all(): + my_ratings[r.deck_id] = r.rating + + total = db.query(FlashcardDeck).filter( + FlashcardDeck.is_shared == 1, + FlashcardDeck.deleted_at.is_(None), + ).count() + + decks_with_rating = db.query( + FlashcardDeck, + func.coalesce(avg_rating.c.avg_rating, 0).label("avg_rating"), + func.coalesce(avg_rating.c.rating_count, 0).label("rating_count"), + ).outerjoin( + avg_rating, FlashcardDeck.id == avg_rating.c.deck_id + ).filter( + FlashcardDeck.is_shared == 1, + FlashcardDeck.deleted_at.is_(None), + ).order_by( + func.coalesce(avg_rating.c.avg_rating, 0).desc(), + FlashcardDeck.created_at.desc(), + ).offset(offset).limit(limit).all() + + user_cache: dict[int, str] = {} + result = [] + for deck, avg_r, r_count in decks_with_rating: + if deck.user_id not in user_cache: + owner = db.query(User).filter(User.id == deck.user_id).first() + user_cache[deck.user_id] = owner.name if owner else "Unknown" + result.append({ + "id": deck.id, + "title": deck.title, + "card_count": deck.card_count, + "user_id": deck.user_id, + "owner_name": user_cache[deck.user_id], + "is_shared": deck.is_shared, + "created_at": deck.created_at, + "avg_rating": round(float(avg_r), 1), + "rating_count": int(r_count), + "my_rating": my_ratings.get(deck.id), + }) + + return {"total": total, "decks": result} + + @router.get("/{deck_id}", response_model=FlashcardDeckDetail) def get_flashcard_deck( deck_id: int, @@ -224,73 +285,6 @@ def admin_unshare_deck( db.commit() -@router.get("/shared") -def list_shared_decks( - limit: int = Query(20, le=100), - offset: int = Query(0), - db: Session = Depends(get_db), - current_user: User = Depends(get_current_user), -): - """List shared decks sorted by average rating (desc), with pagination.""" - from sqlalchemy import func - - # Subquery for average rating - avg_rating = db.query( - FlashcardDeckRating.deck_id, - func.avg(FlashcardDeckRating.rating).label("avg_rating"), - func.count(FlashcardDeckRating.id).label("rating_count"), - ).group_by(FlashcardDeckRating.deck_id).subquery() - - # Current user's rating per deck - my_ratings = {} - if current_user: - for r in db.query(FlashcardDeckRating).filter(FlashcardDeckRating.user_id == current_user.id).all(): - my_ratings[r.deck_id] = r.rating - - query = db.query(FlashcardDeck).filter( - FlashcardDeck.is_shared == 1, - FlashcardDeck.deleted_at.is_(None), - ) - total = query.count() - - # Join with avg rating for sorting - decks_with_rating = db.query( - FlashcardDeck, - func.coalesce(avg_rating.c.avg_rating, 0).label("avg_rating"), - func.coalesce(avg_rating.c.rating_count, 0).label("rating_count"), - ).outerjoin( - avg_rating, FlashcardDeck.id == avg_rating.c.deck_id - ).filter( - FlashcardDeck.is_shared == 1, - FlashcardDeck.deleted_at.is_(None), - ).order_by( - func.coalesce(avg_rating.c.avg_rating, 0).desc(), - FlashcardDeck.created_at.desc(), - ).offset(offset).limit(limit).all() - - # Get owner names - user_cache: dict[int, str] = {} - result = [] - for deck, avg_r, r_count in decks_with_rating: - if deck.user_id not in user_cache: - owner = db.query(User).filter(User.id == deck.user_id).first() - user_cache[deck.user_id] = owner.name if owner else "Unknown" - result.append({ - "id": deck.id, - "title": deck.title, - "card_count": deck.card_count, - "user_id": deck.user_id, - "owner_name": user_cache[deck.user_id], - "is_shared": deck.is_shared, - "created_at": deck.created_at, - "avg_rating": round(float(avg_r), 1), - "rating_count": int(r_count), - "my_rating": my_ratings.get(deck.id), - }) - - return {"total": total, "decks": result} - - @router.post("/{deck_id}/rate") def rate_deck( deck_id: int, diff --git a/frontend/src/pages/FlashcardsPage.jsx b/frontend/src/pages/FlashcardsPage.jsx index b04e6e2..1316583 100644 --- a/frontend/src/pages/FlashcardsPage.jsx +++ b/frontend/src/pages/FlashcardsPage.jsx @@ -191,7 +191,7 @@ export default function FlashcardsPage() {
- {deck.card_count} cards �� {new Date(deck.created_at).toLocaleDateString()} + {deck.card_count} cards · {new Date(deck.created_at).toLocaleDateString()}
{deck.is_shared ? ( Shared