fix(tradein): filter ДКП-only in rosreestr importer + re-enable deals #549
3 changed files with 20 additions and 11 deletions
|
|
@ -834,11 +834,17 @@ async def estimate_quality(payload: TradeInEstimateInput, db: Session) -> Aggreg
|
||||||
except Exception as exc:
|
except Exception as exc:
|
||||||
logger.warning("cian_valuation: lookup failed (graceful): %s", exc)
|
logger.warning("cian_valuation: lookup failed (graceful): %s", exc)
|
||||||
|
|
||||||
# 5. Deals — отключены 2026-05-24 (см. Decision_TradeIn_DataQuality_8PR_Roadmap).
|
# 5. Deals — ДКП-only sales (вторичка) из rosreestr_deals.
|
||||||
# rosreestr_deals содержит ДДУ первичного рынка (177-200 К/м²), что искажает
|
# Importer фильтрует doc_type='ДКП' (PR-A 2026-05-24), ДДУ застройщиков
|
||||||
# оценку вторички (~114 К/м²). Возвращаемся к этому когда отделим вторичные
|
# исключены — больше не скёюят median вторички ~110-120 К/м².
|
||||||
# сделки от ДДУ в источнике.
|
deals = _fetch_deals(
|
||||||
deals: list[dict[str, Any]] = []
|
db,
|
||||||
|
lat=geo.lat,
|
||||||
|
lon=geo.lon,
|
||||||
|
rooms=payload.rooms,
|
||||||
|
area=payload.area_m2,
|
||||||
|
radius_m=DEFAULT_RADIUS_M,
|
||||||
|
)
|
||||||
|
|
||||||
# 6. Сохраняем в trade_in_estimates
|
# 6. Сохраняем в trade_in_estimates
|
||||||
estimate_id = uuid4()
|
estimate_id = uuid4()
|
||||||
|
|
|
||||||
|
|
@ -102,7 +102,7 @@ def _common_patches(cian_mock):
|
||||||
return [
|
return [
|
||||||
patch("app.services.estimator.geocode", new=AsyncMock(return_value=_make_fake_geo())),
|
patch("app.services.estimator.geocode", new=AsyncMock(return_value=_make_fake_geo())),
|
||||||
patch("app.services.estimator.get_house_metadata", new=AsyncMock(return_value=None)),
|
patch("app.services.estimator.get_house_metadata", new=AsyncMock(return_value=None)),
|
||||||
patch("app.services.estimator._fetch_analogs", return_value=([], False)),
|
patch("app.services.estimator._fetch_analogs", return_value=([], False, "W")),
|
||||||
patch("app.services.estimator._fetch_deals", return_value=[]),
|
patch("app.services.estimator._fetch_deals", return_value=[]),
|
||||||
patch(
|
patch(
|
||||||
"app.services.estimator._get_or_fetch_imv_cached",
|
"app.services.estimator._get_or_fetch_imv_cached",
|
||||||
|
|
@ -134,7 +134,7 @@ def test_estimator_includes_cian_valuation_when_available() -> None:
|
||||||
new=AsyncMock(return_value=_make_fake_geo())),
|
new=AsyncMock(return_value=_make_fake_geo())),
|
||||||
patch("app.services.estimator.get_house_metadata",
|
patch("app.services.estimator.get_house_metadata",
|
||||||
new=AsyncMock(return_value=None)),
|
new=AsyncMock(return_value=None)),
|
||||||
patch("app.services.estimator._fetch_analogs", return_value=([], False)),
|
patch("app.services.estimator._fetch_analogs", return_value=([], False, "W")),
|
||||||
patch("app.services.estimator._fetch_deals", return_value=[]),
|
patch("app.services.estimator._fetch_deals", return_value=[]),
|
||||||
patch("app.services.estimator._get_or_fetch_imv_cached",
|
patch("app.services.estimator._get_or_fetch_imv_cached",
|
||||||
new=AsyncMock(return_value=None)),
|
new=AsyncMock(return_value=None)),
|
||||||
|
|
@ -164,7 +164,7 @@ def test_estimator_graceful_when_cian_returns_none() -> None:
|
||||||
new=AsyncMock(return_value=_make_fake_geo())),
|
new=AsyncMock(return_value=_make_fake_geo())),
|
||||||
patch("app.services.estimator.get_house_metadata",
|
patch("app.services.estimator.get_house_metadata",
|
||||||
new=AsyncMock(return_value=None)),
|
new=AsyncMock(return_value=None)),
|
||||||
patch("app.services.estimator._fetch_analogs", return_value=([], False)),
|
patch("app.services.estimator._fetch_analogs", return_value=([], False, "W")),
|
||||||
patch("app.services.estimator._fetch_deals", return_value=[]),
|
patch("app.services.estimator._fetch_deals", return_value=[]),
|
||||||
patch("app.services.estimator._get_or_fetch_imv_cached",
|
patch("app.services.estimator._get_or_fetch_imv_cached",
|
||||||
new=AsyncMock(return_value=None)),
|
new=AsyncMock(return_value=None)),
|
||||||
|
|
@ -195,7 +195,7 @@ def test_estimator_graceful_when_cian_raises() -> None:
|
||||||
new=AsyncMock(return_value=_make_fake_geo())),
|
new=AsyncMock(return_value=_make_fake_geo())),
|
||||||
patch("app.services.estimator.get_house_metadata",
|
patch("app.services.estimator.get_house_metadata",
|
||||||
new=AsyncMock(return_value=None)),
|
new=AsyncMock(return_value=None)),
|
||||||
patch("app.services.estimator._fetch_analogs", return_value=([], False)),
|
patch("app.services.estimator._fetch_analogs", return_value=([], False, "W")),
|
||||||
patch("app.services.estimator._fetch_deals", return_value=[]),
|
patch("app.services.estimator._fetch_deals", return_value=[]),
|
||||||
patch("app.services.estimator._get_or_fetch_imv_cached",
|
patch("app.services.estimator._get_or_fetch_imv_cached",
|
||||||
new=AsyncMock(return_value=None)),
|
new=AsyncMock(return_value=None)),
|
||||||
|
|
@ -228,7 +228,7 @@ def test_estimator_cian_result_no_sale_price_not_added() -> None:
|
||||||
new=AsyncMock(return_value=_make_fake_geo())),
|
new=AsyncMock(return_value=_make_fake_geo())),
|
||||||
patch("app.services.estimator.get_house_metadata",
|
patch("app.services.estimator.get_house_metadata",
|
||||||
new=AsyncMock(return_value=None)),
|
new=AsyncMock(return_value=None)),
|
||||||
patch("app.services.estimator._fetch_analogs", return_value=([], False)),
|
patch("app.services.estimator._fetch_analogs", return_value=([], False, "W")),
|
||||||
patch("app.services.estimator._fetch_deals", return_value=[]),
|
patch("app.services.estimator._fetch_deals", return_value=[]),
|
||||||
patch("app.services.estimator._get_or_fetch_imv_cached",
|
patch("app.services.estimator._get_or_fetch_imv_cached",
|
||||||
new=AsyncMock(return_value=None)),
|
new=AsyncMock(return_value=None)),
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,8 @@
|
||||||
# rooms выводим из площади (Росреестр не отдаёт кол-во комнат).
|
# rooms выводим из площади (Росреестр не отдаёт кол-во комнат).
|
||||||
# Координаты — NULL, проставляются отдельно (geocode по street).
|
# Координаты — NULL, проставляются отдельно (geocode по street).
|
||||||
#
|
#
|
||||||
|
# Только ДКП (вторичка) — ДДУ застройщиков скёюят median, отделены сознательно. См. PR-A.
|
||||||
|
#
|
||||||
# Запуск на прод-хосте: ./import-rosreestr.sh
|
# Запуск на прод-хосте: ./import-rosreestr.sh
|
||||||
# Повторяемо: дедуп по dedup_hash, новый запуск подтянет свежие кварталы.
|
# Повторяемо: дедуп по dedup_hash, новый запуск подтянет свежие кварталы.
|
||||||
|
|
||||||
|
|
@ -32,7 +34,7 @@ docker exec "$DST_PG" psql -U tradein -d tradein -v ON_ERROR_STOP=on -c "
|
||||||
docker exec "$SRC_PG" psql -U "$SRC_USER" -d "$SRC_DB" -v ON_ERROR_STOP=on -c "
|
docker exec "$SRC_PG" psql -U "$SRC_USER" -d "$SRC_DB" -v ON_ERROR_STOP=on -c "
|
||||||
COPY (
|
COPY (
|
||||||
SELECT
|
SELECT
|
||||||
md5('ros:' || id::text) AS dedup_hash,
|
md5('ros:dkp:' || id::text) AS dedup_hash,
|
||||||
'Екатеринбург, ' || trim(street) AS address,
|
'Екатеринбург, ' || trim(street) AS address,
|
||||||
CASE
|
CASE
|
||||||
WHEN area < 30 THEN 0 WHEN area < 44 THEN 1
|
WHEN area < 30 THEN 0 WHEN area < 44 THEN 1
|
||||||
|
|
@ -51,6 +53,7 @@ docker exec "$SRC_PG" psql -U "$SRC_USER" -d "$SRC_DB" -v ON_ERROR_STOP=on -c "
|
||||||
AND area BETWEEN 18 AND 200
|
AND area BETWEEN 18 AND 200
|
||||||
AND deal_price BETWEEN 1000000 AND 100000000
|
AND deal_price BETWEEN 1000000 AND 100000000
|
||||||
AND street IS NOT NULL AND trim(street) <> ''
|
AND street IS NOT NULL AND trim(street) <> ''
|
||||||
|
AND doc_type = 'ДКП'
|
||||||
AND period_start_date >= '$SINCE'
|
AND period_start_date >= '$SINCE'
|
||||||
) TO STDOUT WITH CSV
|
) TO STDOUT WITH CSV
|
||||||
" | docker exec -i "$DST_PG" psql -U tradein -d tradein -v ON_ERROR_STOP=on -c "
|
" | docker exec -i "$DST_PG" psql -U tradein -d tradein -v ON_ERROR_STOP=on -c "
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue