add encar scraper

This commit is contained in:
qananasikq
2026-04-16 18:10:50 +03:00
parent bfb543c6eb
commit 2b6c6cab6e
27 changed files with 4507 additions and 0 deletions

View File

@@ -0,0 +1,121 @@
# Роуты запуска задач синхронизации 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
],
}