gendesign/tradein-mvp/deploy/import-rosreestr.sh
bot-backend de2d67c7f9
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI / changes (pull_request) Successful in 11s
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m15s
docs(mera/sales-vs-listings): якорь и докстринг по замечаниям ревью PR #3461
Ревью справедливо поймало два места, где текст после снятия предиката стал
неточным:

1. Якорь в deploy/import-rosreestr.sh обещал «потребителей, фильтрующих по
   d.rooms, больше нет» — это верно только про предикаты РАВЕНСТВА.
   app/tasks/asking_to_sold_ratio.py:148,152 по-прежнему КЛЮЧУЕТСЯ этим
   бакетом (GROUP BY LEAST(GREATEST(rooms,0),4)) и намеренно зеркалит ту же
   синтетику на листинговой стороне (#2620). Прежняя формулировка сказала бы
   будущему редактору, что проверять некого, — а в сценарии «поменяли CASE на
   реальную комнатность» вернулся бы именно #2620.

2. Докстринг GET /sales-vs-listings обещал listing «с такими же rooms». После
   снятия предиката это верно для пары запрос↔объявление, но не для пары
   сделка↔объявление: deal_rooms может не совпадать с запрошенным rooms.

Кода правка не касается. Полный сьют: 5946 passed, 35 skipped; ruff чист.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-12 13:55:40 +05:00

134 lines
9.1 KiB
Bash
Executable file
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env bash
# Импорт реальных сделок Росреестра из gendesign-БД в tradein deals.
#
# Источник: gendesign-postgres-1 / rosreestr_deals (6.8М строк, партиц.).
# Берём квартиры (realestate_type_code=002001003000) региона REGION_CODE — по умолчанию
# 66 = вся Свердловская область, все города (не только ЕКБ: city-фильтр снят давно,
# шапка про «ЕКБ квартиры» была неправдой).
# rooms выводим из площади (Росреестр не отдаёт кол-во комнат).
# Координаты — NULL, проставляются отдельно (geocode по street).
#
# DOC_TYPE по умолчанию ДКП (вторичка): ДДУ идут по ценам котлована и скёюят median,
# отделены сознательно (см. PR-A). С #3051 это параметр, а не литерал — для Москвы
# (REGION_CODE=77) типы разделяются колонкой deals.doc_type (миграция 288), которую
# скрипт теперь заполняет.
#
# #3051 п.3: REGION_CODE параметризован (default 66 — Свердловская обл., byte-for-byte
# прежнее поведение). Этот bash-путь — НЕ region-generic: для region_code=77 (Москва) он
# НЕ подставляет canonical_city вместо city источника (Росреестр по Москве отдаёт
# муниципальный округ/поселение, не сам город) и не пишет raw_payload с
# okato/quarter_cad_number/district — эту логику несёт только Python-путь
# (app/services/scheduler.py::import_rosreestr_dkp, боевой планировщик). Если этот
# скрипт когда-нибудь запустят вручную с REGION_CODE=77 — city/address будут
# "муниципальный округ Раменки, ..." как есть из источника, НЕ "Москва, ...". Держать
# паритет фильтров (area/price/doc_type) обязательно, паритет city-override — нет.
#
# Запуск на прод-хосте: ./import-rosreestr.sh
# REGION_CODE=77 DOC_TYPE='ДДУ' ./import-rosreestr.sh # Москва, первичка
# Повторяемо: дедуп по dedup_hash, новый запуск подтянет свежие кварталы.
set -euo pipefail
SRC_PG="${SRC_PG:-gendesign-postgres-1}"
DST_PG="${DST_PG:-tradein-postgres}"
SRC_DB="${SRC_DB:-gendesign}"
SRC_USER="${SRC_USER:-gendesign}"
SINCE="${SINCE:-2024-01-01}"
# #3051: регион и тип документа — параметры со старыми дефолтами (поведение не меняется).
REGION_CODE="${REGION_CODE:-66}"
DOC_TYPE="${DOC_TYPE:-ДКП}"
# Оба значения интерполируются в SQL текстом (не bind-параметром) — валидация здесь
# и есть единственная защита от SQL-инъекции. Паттерн — в переменной (не инлайн в
# [[ =~ ]]): голая кавычка в regex-операнде ломает bash-парсинг команды.
DOC_TYPE_RE="^[^;']+\$"
[[ "$REGION_CODE" =~ ^[0-9]+$ ]] || { echo "REGION_CODE должен быть целым числом, получено: '$REGION_CODE'" >&2; exit 1; }
[[ "$DOC_TYPE" =~ $DOC_TYPE_RE ]] || { echo "DOC_TYPE не должен содержать ' или ;, получено: '$DOC_TYPE'" >&2; exit 1; }
echo "[$(date -u +%H:%M:%S)] import-rosreestr: region=$REGION_CODE doc_type=$DOC_TYPE с $SINCE"
# 1. Staging-таблица в tradein.
docker exec "$DST_PG" psql -U tradein -d tradein -v ON_ERROR_STOP=on -c "
-- DROP перед CREATE: страхует от устаревшей формы staging-таблицы, если прошлый
-- запуск упал между CREATE и финальным DROP под старым кодом (без source_id).
DROP TABLE IF EXISTS deals_ros_staging;
CREATE TABLE deals_ros_staging (
dedup_hash text PRIMARY KEY, source_id text, address text, region_code int, city text,
rooms int, area_m2 numeric,
floor int, year_built int, price_rub bigint, price_per_m2 int, deal_date date,
doc_type text
);
"
# 2. Трансформируем на gendesign-стороне, пайпим CSV в staging.
docker exec "$SRC_PG" psql -U "$SRC_USER" -d "$SRC_DB" -v ON_ERROR_STOP=on -c "
COPY (
SELECT
'ros:dkp:' || id::text AS dedup_hash,
id::text AS source_id,
trim(city) || ', ' || trim(street) AS address,
region_code AS region_code,
trim(city) AS city,
-- ЯКОРЬ #3256: это НЕ комнатность, а бакет площади. Границы 30/44/62/85
-- обязаны совпадать с asking_to_sold_ratio.area_bucket() — совпадение
-- проверяет tests/test_3256_deals_rooms_key.py, который ПАРСИТ этот CASE.
-- ПОМЕНЯЕШЬ НА РЕАЛЬНУЮ КОМНАТНОСТЬ — вернись в #3256: потребители
-- (estimator._fetch_dkp_corridor, _fetch_deals, API /street-deals) сейчас
-- НЕ фильтруют по deals.rooms ИМЕННО потому, что здесь синтетика; с
-- настоящей комнатностью предикат нужно вернуть, иначе все сайты молча
-- продолжат ключеваться площадью.
-- Потребителей, фильтрующих РАВЕНСТВОМ по d.rooms, больше нет: последний —
-- TVF street_sales_vs_listings — расчищен миграцией 300 (#3451). Ключ там
-- асимметричный: d.rooms снят, l.rooms ОСТАВЛЕН (у объявлений комнатность
-- настоящая). Появится новый потребитель — сверься с этим якорем.
-- НО: app/tasks/asking_to_sold_ratio.py:148,152 по-прежнему КЛЮЧУЕТСЯ этим
-- бакетом (GROUP BY LEAST(GREATEST(rooms,0),4)) и намеренно зеркалит ту же
-- синтетику на листинговой стороне (#2620). Поменяешь CASE на реальную
-- комнатность — вернётся именно #2620 (миграция 23-55% объявлений между
-- бакетами, ratio>1 в «4+»), а не только вопрос предикатов.
CASE
WHEN area < 30 THEN 0 WHEN area < 44 THEN 1
WHEN area < 62 THEN 2 WHEN area < 85 THEN 3
ELSE 4 END AS rooms,
round(area, 2) AS area_m2,
LEAST(NULLIF(substring(floor from '[0-9]+'), '')::int, 100) AS floor,
year_build AS year_built,
round(deal_price)::bigint AS price_rub,
round(price_per_sqm)::int AS price_per_m2,
period_start_date AS deal_date,
doc_type AS doc_type
FROM rosreestr_deals
WHERE region_code = $REGION_CODE
AND city IS NOT NULL AND trim(city) <> ''
AND realestate_type_code = '002001003000'
AND area BETWEEN 18 AND 200
AND deal_price BETWEEN 1000000 AND 100000000
AND street IS NOT NULL AND trim(street) <> ''
AND doc_type = '$DOC_TYPE'
AND period_start_date >= '$SINCE'
) TO STDOUT WITH CSV
" | docker exec -i "$DST_PG" psql -U tradein -d tradein -v ON_ERROR_STOP=on -c "
COPY deals_ros_staging FROM STDIN WITH CSV
"
STAGED=$(docker exec "$DST_PG" psql -U tradein -d tradein -tAc "SELECT count(*) FROM deals_ros_staging")
echo "[$(date -u +%H:%M:%S)] staged: $STAGED строк"
# 3. Удаляем старые синтетические сделки, вставляем реальные (дедуп).
docker exec "$DST_PG" psql -U tradein -d tradein -v ON_ERROR_STOP=on -c "
DELETE FROM deals WHERE address = 'Екатеринбург, реальная сделка';
INSERT INTO deals (
source, dedup_hash, source_id, address, region_code, city, rooms, area_m2, floor,
year_built, price_rub, price_per_m2, deal_date, doc_type
)
SELECT 'rosreestr', dedup_hash, source_id, address, region_code, city, rooms, area_m2, floor,
year_built, price_rub, price_per_m2, deal_date, doc_type
FROM deals_ros_staging
ON CONFLICT (dedup_hash) DO NOTHING;
DROP TABLE deals_ros_staging;
"
TOTAL=$(docker exec "$DST_PG" psql -U tradein -d tradein -tAc "SELECT count(*) FROM deals")
echo "[$(date -u +%H:%M:%S)] deals всего: $TOTAL"
echo "Координаты проставит geocode-deals (отдельный шаг)."