Some checks failed
Deploy Trade-In / changes (push) Successful in 5s
Deploy Trade-In / build-frontend (push) Has been skipped
Deploy Trade-In / test (push) Successful in 30s
Deploy Trade-In / build-backend (push) Successful in 40s
Deploy Trade-In / deploy (push) Has been cancelled
Co-authored-by: bot-backend <bot-backend@gendsgn.local> Co-committed-by: bot-backend <bot-backend@gendsgn.local>
70 lines
2.9 KiB
Python
70 lines
2.9 KiB
Python
"""Deactivate stale avito listings that have not been seen in >TTL days (#759).
|
||
|
||
Нечасто обновляемые объявления на Avito (last_seen_at < NOW() - TTL) помечаются
|
||
is_active=false, чтобы coverage/estimator denominators не раздувались мёртвыми
|
||
объявлениями. Строки НЕ удаляются — история нужна для бэктеста (#667).
|
||
|
||
Только source='avito'. Cian/Yandex не затрагиваются.
|
||
|
||
Задача синхронная (DB-only, никаких внешних HTTP-вызовов) — запускается in-app
|
||
scheduler'ом через trigger_deactivate_stale_avito_run() (scheduler.py),
|
||
по образцу snapshot_listing_sources / recompute_asking_to_sold_ratios
|
||
(sync task в run_in_executor).
|
||
|
||
TTL берётся из settings.avito_stale_ttl_days (env AVITO_STALE_TTL_DAYS, default 10).
|
||
"""
|
||
|
||
from __future__ import annotations
|
||
|
||
import logging
|
||
|
||
from sqlalchemy import text
|
||
from sqlalchemy.orm import Session
|
||
|
||
from app.core.config import settings
|
||
from app.services import scrape_runs as runs_mod
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# UPDATE: помечаем is_active=false для avito-объявлений, не виденных >TTL дней.
|
||
# CAST(:ttl_days || ' days' AS interval) — psycopg v3 safe (никаких :param::type).
|
||
# Только source='avito' и is_active=true — чтобы не трогать уже неактивные строки
|
||
# и не затрагивать cian/yandex.
|
||
_DEACTIVATE_SQL = text(
|
||
"""
|
||
UPDATE listings
|
||
SET is_active = false
|
||
WHERE source = 'avito'
|
||
AND is_active = true
|
||
AND last_seen_at < NOW() - CAST(:ttl_days || ' days' AS interval)
|
||
"""
|
||
)
|
||
|
||
|
||
def deactivate_stale_avito_listings(db: Session, run_id: int) -> dict[str, int]:
|
||
"""Пометить is_active=false все avito-объявления с last_seen_at > TTL дней назад.
|
||
|
||
Sync (вызывается scheduler-триггером в executor, как snapshot_listing_sources).
|
||
Один UPDATE в транзакции. Финализирует scrape_runs (mark_done / mark_failed).
|
||
|
||
Returns {"deactivated": N} — количество обновлённых строк.
|
||
"""
|
||
counters: dict[str, int] = {"deactivated": 0}
|
||
try:
|
||
result = db.execute(_DEACTIVATE_SQL, {"ttl_days": settings.avito_stale_ttl_days})
|
||
counters["deactivated"] = result.rowcount or 0
|
||
|
||
db.commit()
|
||
runs_mod.mark_done(db, run_id, counters)
|
||
logger.info(
|
||
"deactivate_stale_avito run_id=%d done: deactivated=%d (ttl_days=%d)",
|
||
run_id,
|
||
counters["deactivated"],
|
||
settings.avito_stale_ttl_days,
|
||
)
|
||
return counters
|
||
except Exception as exc:
|
||
logger.exception("deactivate_stale_avito run_id=%d failed", run_id)
|
||
db.rollback()
|
||
runs_mod.mark_failed(db, run_id, str(exc)[:1000], counters)
|
||
raise
|