diff --git a/backend/app/api/v1/admin_scrape.py b/backend/app/api/v1/admin_scrape.py index d9606667..55b1d14a 100644 --- a/backend/app/api/v1/admin_scrape.py +++ b/backend/app/api/v1/admin_scrape.py @@ -1249,3 +1249,38 @@ def trigger_ekburg_permits( scope = f"year_{payload.year}" return {"task_id": result.id, "scope": scope, "queued_at": "now"} + + +class TriggerKnCatalogObjectsRequest(BaseModel): + region_code: int = Field(default=66, ge=1, le=99) + max_objects: int | None = Field(default=None, ge=1, le=2000) + + +@router.post("/kn-catalog-objects") +def trigger_kn_catalog_objects( + payload: TriggerKnCatalogObjectsRequest, + _: AdminTokenAuth, +) -> dict[str, Any]: + """Manual trigger для catalog-OBJECT scraper (заполняет wall_type, energy_eff, + ceiling_height_m, parking_*, playground_*, scores из SSR __NEXT_DATA__). + + Beat schedule: Tuesday 04:00 UTC, batch 300/run. Этот endpoint — для ad-hoc + запуска (smoke-тест после деплоя или повторный pass для свежесозданных + объектов до next beat fire). + + - max_objects=None → берёт из job_settings (default 300 для beat). + - max_objects=3 → smoke-тест. + """ + from app.workers.tasks.scrape_kn_catalog_objects import scrape_kn_catalog_objects + + kwargs: dict[str, Any] = {"region_code": payload.region_code} + if payload.max_objects is not None: + kwargs["max_objects"] = payload.max_objects + result = scrape_kn_catalog_objects.apply_async(kwargs=kwargs) + + return { + "task_id": result.id, + "region_code": payload.region_code, + "max_objects": payload.max_objects, + "queued_at": "now", + } diff --git a/backend/app/workers/celery_app.py b/backend/app/workers/celery_app.py index 9a3afa0c..112c2fa1 100644 --- a/backend/app/workers/celery_app.py +++ b/backend/app/workers/celery_app.py @@ -49,6 +49,7 @@ celery_app = Celery( backend=settings.redis_url, include=[ "app.workers.tasks.scrape_kn", + "app.workers.tasks.scrape_kn_catalog_objects", "app.workers.tasks.refresh_analytics", "app.workers.tasks.scrape_objective", "app.workers.tasks.objective_etl",