Initial commit.
This commit is contained in:
commit
2fa935016e
11 changed files with 1410 additions and 0 deletions
234
app/routes/reviews.py
Normal file
234
app/routes/reviews.py
Normal file
|
|
@ -0,0 +1,234 @@
|
|||
import sqlite3
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
|
||||
from app.database import get_db
|
||||
from app.models import Review, ReviewListResponse
|
||||
|
||||
router = APIRouter(prefix="/reviews", tags=["Reviews"])
|
||||
|
||||
@router.get(
|
||||
"",
|
||||
response_model=ReviewListResponse,
|
||||
summary="List reviews",
|
||||
description="Retrieve a paginated list of reviews with optional filters. All filters are combined with AND logic, where only reviews matching every specified filter are returned.",
|
||||
)
|
||||
def list_reviews(
|
||||
category: str | None = Query(
|
||||
None, description="Filter by media category (exact match).", examples=["Movies"]
|
||||
),
|
||||
creator: str | None = Query(
|
||||
None, description="Filter by creator name (exact match).", examples=["Christopher Nolan"]
|
||||
),
|
||||
genre: str | None = Query(
|
||||
None, description="Filter by genre (exact match).", examples=["Science Fiction"]
|
||||
),
|
||||
min_rating: float | None = Query(
|
||||
None, description="Minimum rating (inclusive).", ge=-3.0, le=3.0, examples=[2.0]
|
||||
),
|
||||
max_rating: float | None = Query(
|
||||
None, description="Maximum rating (inclusive).", ge=-3.0, le=3.0, examples=[3.0]
|
||||
),
|
||||
date_from: str | None = Query(
|
||||
None, description="Earliest review date (inclusive, YYYY-MM-DD).", examples=["2020-01-01"]
|
||||
),
|
||||
date_to: str | None = Query(
|
||||
None, description="Latest review date (inclusive, YYYY-MM-DD).", examples=["2025-12-31"]
|
||||
),
|
||||
year_from: int | None = Query(
|
||||
None, description="Earliest release year (inclusive).", examples=[1990]
|
||||
),
|
||||
year_to: int | None = Query(
|
||||
None, description="Latest release year (inclusive).", examples=[2000]
|
||||
),
|
||||
limit: int = Query(
|
||||
20, description="Number of results per page.", ge=1, le=100
|
||||
),
|
||||
offset: int = Query(
|
||||
0, description="Number of results to skip.", ge=0
|
||||
),
|
||||
db: sqlite3.Connection = Depends(get_db),
|
||||
):
|
||||
conditions = []
|
||||
params = []
|
||||
|
||||
if category:
|
||||
conditions.append("category = ?")
|
||||
params.append(category)
|
||||
if creator:
|
||||
conditions.append("creator = ?")
|
||||
params.append(creator)
|
||||
if genre:
|
||||
conditions.append("genre = ?")
|
||||
params.append(genre)
|
||||
if min_rating is not None:
|
||||
conditions.append("rating >= ?")
|
||||
params.append(min_rating)
|
||||
if max_rating is not None:
|
||||
conditions.append("rating <= ?")
|
||||
params.append(max_rating)
|
||||
if date_from:
|
||||
conditions.append("date >= ?")
|
||||
params.append(date_from)
|
||||
if date_to:
|
||||
conditions.append("date <= ?")
|
||||
params.append(date_to)
|
||||
if year_from is not None:
|
||||
conditions.append("year >= ?")
|
||||
params.append(year_from)
|
||||
if year_to is not None:
|
||||
conditions.append("year <= ?")
|
||||
params.append(year_to)
|
||||
|
||||
where = ""
|
||||
if conditions:
|
||||
where = "WHERE " + " AND ".join(conditions)
|
||||
|
||||
total = db.execute(
|
||||
f"SELECT COUNT(*) FROM reviews {where}", params
|
||||
).fetchone()[0]
|
||||
|
||||
rows = db.execute(
|
||||
f"SELECT * FROM reviews {where} ORDER BY date DESC LIMIT ? OFFSET ?",
|
||||
params + [limit, offset],
|
||||
).fetchall()
|
||||
|
||||
return ReviewListResponse(
|
||||
total=total,
|
||||
limit=limit,
|
||||
offset=offset,
|
||||
results=[dict(row) for row in rows],
|
||||
)
|
||||
|
||||
@router.get(
|
||||
"/random",
|
||||
response_model=Review,
|
||||
summary="Get a random review",
|
||||
description="Returns a single randomly selected review. Optionally filter by category to get a random review from a specific media type.",
|
||||
)
|
||||
def random_review(
|
||||
category: str | None = Query(
|
||||
None, description="Limit random selection to this category.", examples=["Books"]
|
||||
),
|
||||
db: sqlite3.Connection = Depends(get_db),
|
||||
):
|
||||
conditions = []
|
||||
params = []
|
||||
|
||||
if category:
|
||||
conditions.append("category = ?")
|
||||
params.append(category)
|
||||
|
||||
where = ""
|
||||
if conditions:
|
||||
where = "WHERE " + " AND ".join(conditions)
|
||||
|
||||
row = db.execute(
|
||||
f"SELECT * FROM reviews {where} ORDER BY RANDOM() LIMIT 1", params
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
raise HTTPException(status_code=404, detail="No reviews found.")
|
||||
|
||||
return dict(row)
|
||||
|
||||
@router.get(
|
||||
"/top",
|
||||
response_model=list[Review],
|
||||
summary="Get top-rated reviews",
|
||||
description="Returns the highest-rated reviews, sorted by rating descending.",
|
||||
)
|
||||
def top_reviews(
|
||||
category: str | None = Query(None, description="Limit to this category.", examples=["Movies"]),
|
||||
year_from: int | None = Query(None, description="Earliest release year (inclusive).", examples=[1990]),
|
||||
year_to: int | None = Query(None, description="Latest release year (inclusive).", examples=[2000]),
|
||||
limit: int = Query(10, description="Number of results to return.", ge=1, le=50),
|
||||
db: sqlite3.Connection = Depends(get_db),
|
||||
):
|
||||
conditions = []
|
||||
params = []
|
||||
|
||||
if category:
|
||||
conditions.append("category = ?")
|
||||
params.append(category)
|
||||
if year_from is not None:
|
||||
conditions.append("year >= ?")
|
||||
params.append(year_from)
|
||||
if year_to is not None:
|
||||
conditions.append("year <= ?")
|
||||
params.append(year_to)
|
||||
|
||||
where = ""
|
||||
if conditions:
|
||||
where = "WHERE " + " AND ".join(conditions)
|
||||
|
||||
rows = db.execute(
|
||||
f"SELECT * FROM reviews {where} ORDER BY rating DESC LIMIT ?",
|
||||
params + [limit],
|
||||
).fetchall()
|
||||
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
@router.get(
|
||||
"/bottom",
|
||||
response_model=list[Review],
|
||||
summary="Get lowest-rated reviews",
|
||||
description="Returns the lowest-rated reviews, sorted by rating ascending. "
|
||||
"These are the harshest takes in the collection.",
|
||||
)
|
||||
def bottom_reviews(
|
||||
category: str | None = Query(None, description="Limit to this category.", examples=["Movies"]),
|
||||
year_from: int | None = Query(None, description="Earliest release year (inclusive).", examples=[1990]),
|
||||
year_to: int | None = Query(None, description="Latest release year (inclusive).", examples=[2000]),
|
||||
limit: int = Query(10, description="Number of results to return.", ge=1, le=50),
|
||||
db: sqlite3.Connection = Depends(get_db),
|
||||
):
|
||||
conditions = []
|
||||
params = []
|
||||
|
||||
if category:
|
||||
conditions.append("category = ?")
|
||||
params.append(category)
|
||||
if year_from is not None:
|
||||
conditions.append("year >= ?")
|
||||
params.append(year_from)
|
||||
if year_to is not None:
|
||||
conditions.append("year <= ?")
|
||||
params.append(year_to)
|
||||
|
||||
where = ""
|
||||
if conditions:
|
||||
where = "WHERE " + " AND ".join(conditions)
|
||||
|
||||
rows = db.execute(
|
||||
f"SELECT * FROM reviews {where} ORDER BY rating ASC LIMIT ?",
|
||||
params + [limit],
|
||||
).fetchall()
|
||||
|
||||
return [dict(row) for row in rows]
|
||||
|
||||
@router.get(
|
||||
"/{category}/{title}/{creator}",
|
||||
response_model=Review,
|
||||
summary="Get a specific review",
|
||||
description="Retrieve a single review by its unique combination of "
|
||||
"category, title, and creator.",
|
||||
)
|
||||
def get_review(
|
||||
category: str,
|
||||
title: str,
|
||||
creator: str,
|
||||
db: sqlite3.Connection = Depends(get_db),
|
||||
):
|
||||
row = db.execute(
|
||||
"SELECT * FROM reviews WHERE category = ? AND title = ? AND creator = ?",
|
||||
[category, title, creator],
|
||||
).fetchone()
|
||||
|
||||
if not row:
|
||||
raise HTTPException(
|
||||
status_code=404,
|
||||
detail=f"No review found for '{title}' by {creator} in {category}.",
|
||||
)
|
||||
|
||||
return dict(row)
|
||||
Loading…
Add table
Add a link
Reference in a new issue