107 lines
3.6 KiB
Python
107 lines
3.6 KiB
Python
# Роуты для авто и статистики.
|
|
|
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
|
from sqlalchemy import func, select
|
|
from sqlalchemy.orm import selectinload
|
|
|
|
from ..deps import get_persistence
|
|
from ...storage.db import PersistenceService
|
|
from ...storage.models import Car, Image
|
|
from ...storage.schemas import CarRead
|
|
|
|
router = APIRouter()
|
|
|
|
|
|
@router.get("/cars")
|
|
def list_cars(
|
|
page: int = Query(1, ge=1),
|
|
per_page: int = Query(20, ge=1, le=100),
|
|
brand: str | None = None,
|
|
model: str | None = None,
|
|
year_min: int | None = None,
|
|
year_max: int | None = None,
|
|
is_sold: bool | None = None,
|
|
persistence: PersistenceService = Depends(get_persistence),
|
|
):
|
|
# Список авто с фильтрами.
|
|
with persistence.session_scope() as session:
|
|
query = select(Car).options(selectinload(Car.images))
|
|
|
|
if brand:
|
|
query = query.where(Car.brand.ilike(f"%{brand}%"))
|
|
if model:
|
|
query = query.where(Car.model.ilike(f"%{model}%"))
|
|
if year_min is not None:
|
|
query = query.where(Car.year >= year_min)
|
|
if year_max is not None:
|
|
query = query.where(Car.year <= year_max)
|
|
if is_sold is not None:
|
|
query = query.where(Car.is_sold == is_sold)
|
|
|
|
count_query = select(func.count()).select_from(query.subquery())
|
|
total = session.execute(count_query).scalar() or 0
|
|
|
|
offset = (page - 1) * per_page
|
|
cars = session.execute(
|
|
query.order_by(Car.last_seen_at.desc()).offset(offset).limit(per_page)
|
|
).scalars().all()
|
|
|
|
return {
|
|
"total": total,
|
|
"page": page,
|
|
"per_page": per_page,
|
|
"pages": (total + per_page - 1) // per_page if per_page else 0,
|
|
"items": [CarRead.model_validate(car).model_dump(mode="json") for car in cars],
|
|
}
|
|
|
|
|
|
@router.get("/cars/{car_id}")
|
|
def get_car(
|
|
car_id: int,
|
|
persistence: PersistenceService = Depends(get_persistence),
|
|
):
|
|
# Карточка авто с фото.
|
|
with persistence.session_scope() as session:
|
|
car = session.execute(
|
|
select(Car).options(selectinload(Car.images)).where(Car.id == car_id)
|
|
).scalar_one_or_none()
|
|
if car is None:
|
|
raise HTTPException(status_code=404, detail="Car not found")
|
|
return CarRead.model_validate(car).model_dump(mode="json")
|
|
|
|
|
|
@router.get("/cars/by-origin/{origin_id}")
|
|
def get_car_by_origin(
|
|
origin_id: str,
|
|
persistence: PersistenceService = Depends(get_persistence),
|
|
):
|
|
# Поиск по origin_id.
|
|
with persistence.session_scope() as session:
|
|
car = session.execute(
|
|
select(Car).options(selectinload(Car.images)).where(Car.origin_id == origin_id)
|
|
).scalars().first()
|
|
if car is None:
|
|
raise HTTPException(status_code=404, detail="Car not found")
|
|
return CarRead.model_validate(car).model_dump(mode="json")
|
|
|
|
|
|
@router.get("/stats")
|
|
def get_stats(persistence: PersistenceService = Depends(get_persistence)):
|
|
# Общая статистика.
|
|
with persistence.session_scope() as session:
|
|
total_cars = session.execute(select(func.count(Car.id))).scalar() or 0
|
|
total_images = session.execute(select(func.count(Image.id))).scalar() or 0
|
|
brands = session.execute(
|
|
select(Car.brand, func.count(Car.id))
|
|
.group_by(Car.brand)
|
|
.order_by(func.count(Car.id).desc())
|
|
.limit(20)
|
|
).all()
|
|
|
|
return {
|
|
"total_cars": total_cars,
|
|
"total_images": total_images,
|
|
"top_brands": [{"brand": b, "count": c} for b, c in brands],
|
|
}
|
|
|