From 17edac532bd730a23ee6abc160a2cd6beecfea84 Mon Sep 17 00:00:00 2001 From: bot-backend Date: Sat, 20 Jun 2026 15:00:55 +0300 Subject: [PATCH] =?UTF-8?q?fix(estimator):=20Yandex=20history=20freshness?= =?UTF-8?q?=20filter=20=D0=B2=20price=5Ftrend=20(#audit-3)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit _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. --- tradein-mvp/backend/app/services/estimator.py | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/tradein-mvp/backend/app/services/estimator.py b/tradein-mvp/backend/app/services/estimator.py index 7549975c..34916dcd 100644 --- a/tradein-mvp/backend/app/services/estimator.py +++ b/tradein-mvp/backend/app/services/estimator.py @@ -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()