from fastapi import APIRouter, Depends, HTTPException, Query from sqlalchemy import select from sqlalchemy.orm import Session, selectinload from ..database import get_db from ..models import PriceSnapshot, Product from ..schemas import PriceSnapshot as PriceSnapshotSchema, ProductWithLatestPrice router = APIRouter(prefix="/api/products", tags=["products"]) def _attach_latest_price(product: Product, db: Session) -> ProductWithLatestPrice: p = ProductWithLatestPrice.model_validate(product) latest = db.scalar( select(PriceSnapshot) .where(PriceSnapshot.product_id == product.id) .order_by(PriceSnapshot.timestamp.desc()) .limit(1) ) if latest: p.latest_price = latest.price p.latest_price_timestamp = latest.timestamp p.is_on_sale = latest.is_on_sale return p @router.get("", response_model=list[ProductWithLatestPrice]) def search_products( search: str = Query(default=""), limit: int = Query(default=20, le=100), db: Session = Depends(get_db), ): q = select(Product).options(selectinload(Product.store)) if search: q = q.where(Product.name.ilike(f"%{search}%")) q = q.order_by(Product.name).limit(limit) products = db.scalars(q).all() return [_attach_latest_price(p, db) for p in products] @router.get("/{product_id}", response_model=ProductWithLatestPrice) def get_product(product_id: int, db: Session = Depends(get_db)): product = db.scalar( select(Product) .where(Product.id == product_id) .options(selectinload(Product.store)) ) if not product: raise HTTPException(status_code=404, detail="Product not found") return _attach_latest_price(product, db) @router.get("/{product_id}/prices", response_model=list[PriceSnapshotSchema]) def get_product_prices( product_id: int, limit: int = Query(default=200, le=1000), db: Session = Depends(get_db), ): return db.scalars( select(PriceSnapshot) .where(PriceSnapshot.product_id == product_id) .order_by(PriceSnapshot.timestamp.asc()) .limit(limit) ).all()