Initial commit: Tasteby - YouTube restaurant map service
Backend (FastAPI + Oracle ADB), Frontend (Next.js), daemon worker. Features: channel/video/restaurant management, semantic search, Google OAuth, user reviews. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
97
backend/api/routes/reviews.py
Normal file
97
backend/api/routes/reviews.py
Normal file
@@ -0,0 +1,97 @@
|
||||
"""Review API routes."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from datetime import date
|
||||
from typing import Optional
|
||||
|
||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
from core import review
|
||||
from api.deps import get_current_user
|
||||
|
||||
router = APIRouter()
|
||||
|
||||
|
||||
class ReviewCreate(BaseModel):
|
||||
rating: float = Field(..., ge=0.5, le=5.0)
|
||||
review_text: Optional[str] = None
|
||||
visited_at: Optional[date] = None
|
||||
|
||||
|
||||
class ReviewUpdate(BaseModel):
|
||||
rating: Optional[float] = Field(None, ge=0.5, le=5.0)
|
||||
review_text: Optional[str] = None
|
||||
visited_at: Optional[date] = None
|
||||
|
||||
|
||||
# --- Restaurant reviews ---
|
||||
|
||||
@router.get("/restaurants/{restaurant_id}/reviews")
|
||||
def list_restaurant_reviews(
|
||||
restaurant_id: str,
|
||||
limit: int = Query(20, le=100),
|
||||
offset: int = Query(0, ge=0),
|
||||
):
|
||||
"""List reviews for a restaurant (public)."""
|
||||
reviews = review.get_reviews_for_restaurant(restaurant_id, limit=limit, offset=offset)
|
||||
stats = review.get_restaurant_avg_rating(restaurant_id)
|
||||
return {"reviews": reviews, **stats}
|
||||
|
||||
|
||||
@router.post("/restaurants/{restaurant_id}/reviews", status_code=201)
|
||||
def create_restaurant_review(
|
||||
restaurant_id: str,
|
||||
body: ReviewCreate,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Create a review for a restaurant (requires auth)."""
|
||||
created = review.create_review(
|
||||
user_id=current_user["sub"],
|
||||
restaurant_id=restaurant_id,
|
||||
rating=body.rating,
|
||||
review_text=body.review_text,
|
||||
visited_at=body.visited_at,
|
||||
)
|
||||
return created
|
||||
|
||||
|
||||
@router.put("/reviews/{review_id}")
|
||||
def update_review_route(
|
||||
review_id: str,
|
||||
body: ReviewUpdate,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Update a review (own review only)."""
|
||||
updated = review.update_review(
|
||||
review_id=review_id,
|
||||
user_id=current_user["sub"],
|
||||
rating=body.rating,
|
||||
review_text=body.review_text,
|
||||
visited_at=body.visited_at,
|
||||
)
|
||||
if not updated:
|
||||
raise HTTPException(404, "Review not found or not yours")
|
||||
return updated
|
||||
|
||||
|
||||
@router.delete("/reviews/{review_id}", status_code=204)
|
||||
def delete_review_route(
|
||||
review_id: str,
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""Delete a review (own review only)."""
|
||||
deleted = review.delete_review(review_id, current_user["sub"])
|
||||
if not deleted:
|
||||
raise HTTPException(404, "Review not found or not yours")
|
||||
|
||||
|
||||
@router.get("/users/me/reviews")
|
||||
def list_my_reviews(
|
||||
limit: int = Query(20, le=100),
|
||||
offset: int = Query(0, ge=0),
|
||||
current_user: dict = Depends(get_current_user),
|
||||
):
|
||||
"""List current user's reviews."""
|
||||
return review.get_user_reviews(current_user["sub"], limit=limit, offset=offset)
|
||||
Reference in New Issue
Block a user