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 typing import List, Optional
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(
prefix="/leaderboards",
@ -11,25 +11,21 @@ router = APIRouter(
@router.get("/shows/top")
def get_top_shows(limit: int = 10, session: Session = Depends(get_session)):
"""Get top rated shows based on average review score"""
# Group by show_id, calc avg score, count 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.
"""Get top rated shows based on average rating"""
# Using Rating table allows for more data points than just text reviews
query = (
select(
Show,
Venue,
func.avg(Review.score).label("avg_score"),
func.count(Review.id).label("review_count")
func.avg(Rating.score).label("avg_score"),
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)
.where(Rating.entity_type == "show")
.group_by(Show.id, Venue.id)
.having(func.count(Review.id) >= 1)
.order_by(desc("avg_score"), desc("review_count"))
.having(func.count(Rating.id) >= 1)
.order_by(desc("avg_score"), desc("rating_count"))
.limit(limit)
)
@ -39,24 +35,32 @@ def get_top_shows(limit: int = 10, session: Session = Depends(get_session)):
{
"show": show,
"venue": venue,
"avg_score": round(score, 2),
"avg_score": round(score, 2) if score else 0,
"review_count": count
}
for show, venue, score, count in results
]
@router.get("/venues/top")
def get_top_venues(limit: int = 10, session: Session = Depends(get_session)):
"""Get top rated venues"""
@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(Review.score).label("avg_score"),
func.count(Review.id).label("review_count")
func.avg(Rating.score).label("avg_score"),
func.count(Rating.id).label("rating_count")
)
.join(Review, Review.venue_id == Venue.id)
.group_by(Venue.id)
.order_by(desc("avg_score"))
.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)
)
@ -64,8 +68,46 @@ def get_top_venues(limit: int = 10, session: Session = Depends(get_session)):
return [
{
"performance": performance,
"song": song,
"show": show,
"venue": venue,
"avg_score": round(score, 2),
"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")
def get_top_venues(limit: int = 10, session: Session = Depends(get_session)):
"""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 = (
select(
Venue,
func.avg(Rating.score).label("avg_score"),
func.count(Rating.id).label("rating_count")
)
.join(Rating, Rating.venue_id == Venue.id)
.where(Rating.entity_type == "venue")
.group_by(Venue.id)
.having(func.count(Rating.id) >= 1)
.order_by(desc("avg_score"))
.limit(limit)
)
results = session.exec(query).all()
# Fallback: if no direct venue ratings, maybe average the shows played there?
# Keeping it simple for now.
return [
{
"venue": venue,
"avg_score": round(score, 2) if score else 0,
"review_count": count
}
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")
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 = (
select(
Profile,