# Роуты запуска задач синхронизации Encar и просмотра истории sync-runs. from fastapi import APIRouter, Depends, Query from pydantic import BaseModel from sqlalchemy import select, func from ..deps import get_persistence from ...storage.db import PersistenceService from ...storage.models import SyncRun from ...worker.celery_app import celery_app from ...worker.tasks import encar_sync_listing_task, encar_sync_vehicle_task router = APIRouter() class SyncVehicleRequest(BaseModel): vehicle_url: str lane: str = "encar" class SyncListingRequest(BaseModel): car_type: str = "all" # all, domestic, import manufacturer: str | None = None year_from: int | None = None year_to: int | None = None price_max: int | None = None lane: str = "encar" limit: int | None = None @router.post("/tasks/sync-vehicle") def start_sync_vehicle(body: SyncVehicleRequest): """Синхронизация одного авто Encar.""" result = encar_sync_vehicle_task.apply_async( kwargs={"vehicle_url": body.vehicle_url, "lane": body.lane}, queue="encar", ) return { "task_id": result.id, "status": "queued", "vehicle_url": body.vehicle_url, } @router.post("/tasks/sync-listing") def start_sync_listing(body: SyncListingRequest): """Синхронизация листинга Encar через публичный API.""" result = encar_sync_listing_task.apply_async( kwargs={ "car_type": body.car_type, "manufacturer": body.manufacturer, "year_from": body.year_from, "year_to": body.year_to, "price_max": body.price_max, "lane": body.lane, "limit": body.limit, }, queue="encar", ) return { "task_id": result.id, "status": "queued", "car_type": body.car_type, "manufacturer": body.manufacturer, } @router.get("/tasks/{task_id}") def get_task_status(task_id: str): """Статус задачи Celery.""" result = celery_app.AsyncResult(task_id) payload: dict = { "task_id": task_id, "state": result.state, } if result.successful(): payload["result"] = result.result elif result.failed(): payload["error"] = str(result.result) elif result.info is not None: payload["meta"] = result.info return payload @router.get("/sync-runs") def list_sync_runs( page: int = Query(1, ge=1), per_page: int = Query(20, ge=1, le=100), persistence: PersistenceService = Depends(get_persistence), ): """История запусков синхронизации.""" with persistence.session_scope() as session: total = session.execute(select(func.count(SyncRun.id))).scalar() or 0 offset = (page - 1) * per_page runs = session.execute( select(SyncRun).order_by(SyncRun.started_at.desc()).offset(offset).limit(per_page) ).scalars().all() return { "total": total, "page": page, "per_page": per_page, "items": [ { "id": run.id, "started_at": run.started_at.isoformat() if run.started_at else None, "finished_at": run.finished_at.isoformat() if run.finished_at else None, "status": run.status, "lane": run.lane, "ids_fetched": run.ids_fetched, "cars_upserted": run.cars_upserted, "cars_failed": run.cars_failed, "images_upserted": run.images_upserted, "error_summary": run.error_summary, } for run in runs ], }