Add more result types
This commit is contained in:
+111
-2
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user