fix(estimator): Yandex history freshness filter в price_trend (#audit-3)

_fetch_price_trend(freshness_months) — новый параметр, дефолт из
settings.estimate_price_trend_max_age_months (6 мес).
WHERE scraped_at > now() - N months исключает устаревшие items
(yandex_valuation 2024 и т.д.) из house_placement_history fallback.
houses_price_dynamics (Source 1) не затронут. psycopg v3 CAST syntax.
This commit is contained in:
bot-backend 2026-06-20 15:00:55 +03:00
parent 6b99f7d238
commit 17edac532b

View file

@ -2978,7 +2978,12 @@ async def estimate_quality(
freshness_min = _compute_freshness_minutes(metadata_lots)
last_scraped_at = _compute_last_scraped_at(metadata_lots)
# Месячный ₽/м² тренд целевого дома (web TREND chart) — best-effort, None если нет данных.
price_trend_raw = _fetch_price_trend(db, target_house_id=target_house_id)
# #audit-3: передаём freshness_months из настроек — исключаем устаревшие items.
price_trend_raw = _fetch_price_trend(
db,
target_house_id=target_house_id,
freshness_months=settings.estimate_price_trend_max_age_months,
)
price_trend = (
[PriceTrendPoint(month=p["month"], ppm2=p["ppm2"]) for p in price_trend_raw]
if price_trend_raw
@ -3143,6 +3148,7 @@ def _fetch_price_trend(
target_house_id: int | None,
months: int = 24,
min_points: int = 3,
freshness_months: int | None = None,
) -> list[dict[str, Any]] | None:
"""Месячный ₽/м² тренд для целевого дома (web TREND chart) — best-effort.
@ -3200,6 +3206,10 @@ def _fetch_price_trend(
return trend
# ── Source 2 (fallback): aggregate house_placement_history by month ──────
# #audit-3: freshness_months — фильтр по scraped_at чтобы исключить стale items
# (yandex_valuation из 2024 и т.д.). Применяется ко ВСЕМ source в этом запросе
# (avito_imv + yandex_valuation), не только к yandex_valuation. Дефолт из config.
_fresh_months = freshness_months if freshness_months is not None else months
try:
rows = (
db.execute(
@ -3223,11 +3233,14 @@ def _fetch_price_trend(
AND COALESCE(last_price_date, start_price_date) IS NOT NULL
AND COALESCE(last_price_date, start_price_date) > (CURRENT_DATE
- (CAST(:months AS integer) || ' months')::interval)
AND scraped_at > (now()
- CAST(:fresh_months AS integer)
* CAST('1 month' AS interval))
GROUP BY 1
ORDER BY 1 ASC
"""
),
{"hid": target_house_id, "months": months},
{"hid": target_house_id, "months": months, "fresh_months": _fresh_months},
)
.mappings()
.all()