feat(backend): Add Heady Jams to leaderboards and optimize queries
Some checks are pending
Deploy Elmeg / deploy (push) Waiting to run

This commit is contained in:
fullsizemalt 2025-12-21 01:55:38 -08:00
parent 5c53fbc497
commit 6407c19a29

View file

@ -2,7 +2,7 @@ from fastapi import APIRouter, Depends, Query
from sqlmodel import Session, select, func, desc from sqlmodel import Session, select, func, desc
from typing import List, Optional from typing import List, Optional
from database import get_session from database import get_session
from models import Review, Show, Venue, User, Profile from models import Review, Show, Venue, User, Profile, Performance, Song, Rating
router = APIRouter( router = APIRouter(
prefix="/leaderboards", prefix="/leaderboards",
@ -11,25 +11,21 @@ router = APIRouter(
@router.get("/shows/top") @router.get("/shows/top")
def get_top_shows(limit: int = 10, session: Session = Depends(get_session)): def get_top_shows(limit: int = 10, session: Session = Depends(get_session)):
"""Get top rated shows based on average review score""" """Get top rated shows based on average rating"""
# Group by show_id, calc avg score, count reviews # Using Rating table allows for more data points than just text reviews
# Filter for shows with at least 1 review (or maybe 2 to be significant?)
# SQLModel doesn't support complex group_by/having easily in pure pythonic way sometimes,
# but we can use session.exec with a direct select.
query = ( query = (
select( select(
Show, Show,
Venue, Venue,
func.avg(Review.score).label("avg_score"), func.avg(Rating.score).label("avg_score"),
func.count(Review.id).label("review_count") func.count(Rating.id).label("rating_count")
) )
.join(Review, Review.show_id == Show.id) .join(Rating, Rating.show_id == Show.id)
.join(Venue, Show.venue_id == Venue.id) .join(Venue, Show.venue_id == Venue.id)
.where(Rating.entity_type == "show")
.group_by(Show.id, Venue.id) .group_by(Show.id, Venue.id)
.having(func.count(Review.id) >= 1) .having(func.count(Rating.id) >= 1)
.order_by(desc("avg_score"), desc("review_count")) .order_by(desc("avg_score"), desc("rating_count"))
.limit(limit) .limit(limit)
) )
@ -39,33 +35,79 @@ def get_top_shows(limit: int = 10, session: Session = Depends(get_session)):
{ {
"show": show, "show": show,
"venue": venue, "venue": venue,
"avg_score": round(score, 2), "avg_score": round(score, 2) if score else 0,
"review_count": count "review_count": count
} }
for show, venue, score, count in results for show, venue, score, count in results
] ]
@router.get("/performances/top")
def get_top_performances(limit: int = 20, session: Session = Depends(get_session)):
"""Get top rated performances (Heady Jams)"""
query = (
select(
Performance,
Song,
Show,
Venue,
func.avg(Rating.score).label("avg_score"),
func.count(Rating.id).label("rating_count")
)
.join(Rating, Rating.performance_id == Performance.id)
.join(Song, Performance.song_id == Song.id)
.join(Show, Performance.show_id == Show.id)
.join(Venue, Show.venue_id == Venue.id)
.where(Rating.entity_type == "performance")
.group_by(Performance.id, Song.id, Show.id, Venue.id)
.having(func.count(Rating.id) >= 1)
.order_by(desc("avg_score"), desc("rating_count"))
.limit(limit)
)
results = session.exec(query).all()
return [
{
"performance": performance,
"song": song,
"show": show,
"venue": venue,
"avg_score": round(score, 2) if score else 0,
"rating_count": count
}
for performance, song, show, venue, score, count in results
]
@router.get("/venues/top") @router.get("/venues/top")
def get_top_venues(limit: int = 10, session: Session = Depends(get_session)): def get_top_venues(limit: int = 10, session: Session = Depends(get_session)):
"""Get top rated venues""" """Get top rated venues based on show ratings there?"""
# A venue's rating is often derivative of the shows there, or specific venue ratings.
# Let's assume venue rating directly for now if entity_type='venue' exists,
# otherwise we might avg show ratings. Let's start with direct venue ratings.
query = ( query = (
select( select(
Venue, Venue,
func.avg(Review.score).label("avg_score"), func.avg(Rating.score).label("avg_score"),
func.count(Review.id).label("review_count") func.count(Rating.id).label("rating_count")
) )
.join(Review, Review.venue_id == Venue.id) .join(Rating, Rating.venue_id == Venue.id)
.where(Rating.entity_type == "venue")
.group_by(Venue.id) .group_by(Venue.id)
.having(func.count(Rating.id) >= 1)
.order_by(desc("avg_score")) .order_by(desc("avg_score"))
.limit(limit) .limit(limit)
) )
results = session.exec(query).all() results = session.exec(query).all()
# Fallback: if no direct venue ratings, maybe average the shows played there?
# Keeping it simple for now.
return [ return [
{ {
"venue": venue, "venue": venue,
"avg_score": round(score, 2), "avg_score": round(score, 2) if score else 0,
"review_count": count "review_count": count
} }
for venue, score, count in results for venue, score, count in results
@ -73,7 +115,8 @@ def get_top_venues(limit: int = 10, session: Session = Depends(get_session)):
@router.get("/users/active") @router.get("/users/active")
def get_active_users(limit: int = 10, session: Session = Depends(get_session)): def get_active_users(limit: int = 10, session: Session = Depends(get_session)):
"""Get users with most reviews""" """Get users with most reviews/ratings"""
# Combining reviews count
query = ( query = (
select( select(
Profile, Profile,