Add more result types

This commit is contained in:
Esa Kataja
2025-05-17 21:12:14 +03:00
parent 08624052aa
commit b4fa3aa04d
2 changed files with 209 additions and 5 deletions
+111 -2
View File
@@ -1,13 +1,110 @@
from fastapi import APIRouter
from enum import Enum
import duckdb # Added for specific exception handling
from models.result import Result, TeamResult
from models.msg import Message
from lib.db import get_connection
from lib.logger import logger
router = APIRouter(prefix="/results", tags=["results"])
@router.get("/", response_model=list[Result])
async def list_results():
# It's good practice for FastAPI to have Enum derive from str as well
class ResultType(str, Enum):
DEVIATION = "deviation"
COSTUME = "costume"
SHOW = "show"
SONG = "song"
@router.get("/{result_type}", response_model=list[Result] | Message)
async def list_results(result_type: ResultType):
query = ""
try:
with get_connection() as conn:
if result_type == ResultType.DEVIATION:
# Selects the song with the highest standard deviation in its total review scores
query = """
WITH SongReviewDeviations AS (
SELECT
song_id,
COALESCE(STDDEV_SAMP((score_song + score_show + score_costume) / 3.0), 0) AS deviation_value
FROM Review
GROUP BY song_id
)
SELECT
ras.song_id,
ras.country_fi,
ras.country_sv,
ras.artist,
ras.title,
ras.avg_score_song,
ras.avg_score_show,
ras.avg_score_costume,
ras.avg_total_score
FROM ReviewAllSongs ras
JOIN SongReviewDeviations srd ON ras.song_id = srd.song_id
ORDER BY srd.deviation_value DESC
LIMIT 1;
"""
df = conn.execute(query).fetchdf()
logger.debug(f"Query for DEVIATION: {query}")
logger.debug(f"Result DataFrame for DEVIATION: {df}")
elif result_type == ResultType.COSTUME:
# Selects the song with the highest average costume score
query = """SELECT * FROM ReviewAllSongs ORDER BY avg_score_costume DESC LIMIT 1 """
df = conn.execute(query).fetchdf()
elif result_type == ResultType.SHOW:
# Selects the song with the highest average show score
query = """SELECT * FROM ReviewAllSongs ORDER BY avg_score_show DESC LIMIT 1 """
df = conn.execute(query).fetchdf()
elif result_type == ResultType.SONG:
# Selects the song with the highest average song score
query = """SELECT * FROM ReviewAllSongs ORDER BY avg_score_song DESC LIMIT 1 """
df = conn.execute(query).fetchdf()
else:
# This case should ideally not be reached if using Enums properly with FastAPI
return Message(type="error", message="Invalid result type")
if df.empty:
# If the dataframe is empty, return an empty list.
# This applies if a view exists but has no data, or LIMIT 1 returns no row.
return []
# The existing code implies Result model can handle various structures.
# For DEVIATION, row will be {'deviation_value': X}
# For COSTUME/SHOW/SONG (LIMIT 1), it will be a single row from ReviewAllSongs.
# For others, it will be multiple rows from their respective views.
# All these are converted to a list of Result objects.
results = [Result(**row) for row in df.to_dict(orient="records")]
return results
except duckdb.CatalogException as e:
# Handles errors like "View not found" (e.g., ReviewAllSongs doesn't exist)
# You might want to log the error e
print(f"Database Catalog Error: {e}")
return Message(
type="error",
message=f"Data source for '{result_type.value}' not found or query error. Details: {str(e)}",
)
except duckdb.Error as e: # Catch other DuckDB specific errors
print(f"DuckDB Error: {e}")
return Message(
type="error",
message=f"Database query error for '{result_type.value}'. Details: {str(e)}",
)
except Exception as e:
# Catch any other unexpected errors
print(f"Unexpected Error: {e}")
return Message(
type="error",
message=f"An unexpected error occurred while fetching results for '{result_type.value}'.",
)
@router.get("/global", response_model=list[Result])
async def list_global_results():
with get_connection() as conn:
df = conn.execute("SELECT * FROM ReviewSummaryGlobal").fetchdf()
if df.empty:
@@ -16,6 +113,18 @@ async def list_results():
return results
@router.get("/user/{user_id}", response_model=list[Result])
async def list_user_results(user_id: int):
with get_connection() as conn:
df = conn.execute(
"SELECT * FROM ReviewSummaryByUser WHERE user_id = ?", (user_id,)
).fetchdf()
if df.empty:
return []
results = [Result(**row) for row in df.to_dict(orient="records")]
return results
@router.get("/team/{team_id}", response_model=list[TeamResult])
async def list_team_results(team_id: int):
with get_connection() as conn: