Московская область в реестре регионов, обход — по специфичности вместо кода #3501

Merged
lekss361 merged 1 commit from feat/region-50-registry into main 2026-09-12 13:59:44 +00:00
Owner

Зачем

Предусловие для импорта 411 056 сделок Росреестра по области, которые лежат в FDW нетронутыми. Импорт валидирует region_code по REGIONS и без записи в реестре не стартует.

Регион Сделок в источнике Загружено
Московская область 411 056 0
Москва 308 924 212 937
Свердловская область 272 000 108 623

Обход реестра больше не по коду региона

region_for_point шёл по sorted(REGIONS) — «первый по коду выигрывает». Докстринг называл это страховкой «на случай, если регионы когда-нибудь пересекутся». Случай наступил прямо здесь: прямоугольник области содержит прямоугольник Москвы целиком, а 50 < 77. Наивное добавление отправило бы все московские точки в регион 50, включая центр — и это доехало бы до оценки, estimator.py:4862 берёт отсюда скоуп коридора ДКП.

Ключ сортировки — площадь bbox_region по возрастанию: вложенный регион всегда проверяется раньше объемлющего, следующий новый регион встанет на место сам. Порядок стал (77, 50, 66).

region_by_city оставлен по коду осознанно: там точное сравнение строки, а не вложенность прямоугольников. Дублей имён между регионами нет, закреплено тестом.

Границы области — по данным, а не по карте

У области нет города-ядра, поэтому tight/wide/product_core здесь не «город плюс отступ», а эмпирический пояс по разбросу координат 45 294 подмосковных карточек Циана. Административный bbox стоит только в bbox_region, где он и нужен как fallback-accept. Обещать охват продукта там, где нет ни одного объявления, нельзя.

Тиров обогащения ноль, frozenset() пуст намеренно. canonical_city=None — источники по области несут настоящий город (Химки, Балашиха), в отличие от Москвы.

Известное ограничение, которое этот PR НЕ чинит

По координате Химки и Балашиха резолвятся в 77. Это свойство прямоугольника, а не выбранного порядка: любой прямоугольник, содержащий и Зеленоград, и центр Москвы, покоординатно содержит Химки. Замер по московским карточкам ДомКлика подтверждает — Москва дотягивается до долготы 37,95 (восточнее центра Балашихи) и до широты 56,01.

Подмосковных объявлений Циана 24 784
Из них внутри московского прямоугольника 18 288
Резолвятся в 50 корректно 6 496

Хуже не стало. До этого PR те же 18 288 резолвились туда же, просто без конкурента, а оставшиеся 6 496 не резолвились никуда и падали в дефолтный регион 66 — в Свердловскую область.

Следствие для следующего шага: импорт подмосковных объявлений обязан брать регион из города, а не из координаты. По имени Химки, Балашиха и Красногорск разрешаются верно.

Настоящее решение — граница полигоном вместо прямоугольника (#3052).

Test plan

  • Центр Москвы, Зеленоград, Новая Москва → 77
  • Серпухов, Дмитров, Электросталь → 50
  • Екатеринбург → 66, поведение 66 не изменилось
  • По имени: Химки, Балашиха, Красногорск → 50; Москва → 77; Екатеринбург → 66
  • Известное ограничение закреплено тестом, а не замолчано
  • Исключение в test_regions_do_not_overlap не голословно — перед ним проверка, что bbox_region(50) действительно содержит bbox_region(77) как множество
  • 81 passed по регионным файлам, ruff чистый
  • Ревью — APPROVE, критичных замечаний нет

🤖 Generated with Claude Code

https://claude.ai/code/session_01NQb6WeJtagZwZnUsSjDizs

## Зачем Предусловие для импорта **411 056 сделок Росреестра по области**, которые лежат в FDW нетронутыми. Импорт валидирует `region_code` по `REGIONS` и без записи в реестре не стартует. | Регион | Сделок в источнике | Загружено | |---|---|---| | Московская область | 411 056 | 0 | | Москва | 308 924 | 212 937 | | Свердловская область | 272 000 | 108 623 | ## Обход реестра больше не по коду региона `region_for_point` шёл по `sorted(REGIONS)` — «первый по коду выигрывает». Докстринг называл это страховкой «на случай, если регионы когда-нибудь пересекутся». Случай наступил прямо здесь: прямоугольник области содержит прямоугольник Москвы целиком, а 50 < 77. Наивное добавление отправило бы **все** московские точки в регион 50, включая центр — и это доехало бы до оценки, `estimator.py:4862` берёт отсюда скоуп коридора ДКП. Ключ сортировки — площадь `bbox_region` по возрастанию: вложенный регион всегда проверяется раньше объемлющего, следующий новый регион встанет на место сам. Порядок стал `(77, 50, 66)`. `region_by_city` оставлен по коду осознанно: там точное сравнение строки, а не вложенность прямоугольников. Дублей имён между регионами нет, закреплено тестом. ## Границы области — по данным, а не по карте У области нет города-ядра, поэтому `tight`/`wide`/`product_core` здесь не «город плюс отступ», а эмпирический пояс по разбросу координат 45 294 подмосковных карточек Циана. Административный bbox стоит только в `bbox_region`, где он и нужен как fallback-accept. Обещать охват продукта там, где нет ни одного объявления, нельзя. Тиров обогащения ноль, `frozenset()` пуст намеренно. `canonical_city=None` — источники по области несут настоящий город (Химки, Балашиха), в отличие от Москвы. ## Известное ограничение, которое этот PR НЕ чинит По координате Химки и Балашиха резолвятся в 77. Это свойство прямоугольника, а не выбранного порядка: любой прямоугольник, содержащий и Зеленоград, и центр Москвы, покоординатно содержит Химки. Замер по московским карточкам ДомКлика подтверждает — Москва дотягивается до долготы 37,95 (восточнее центра Балашихи) и до широты 56,01. | | | |---|---| | Подмосковных объявлений Циана | 24 784 | | Из них внутри московского прямоугольника | 18 288 | | Резолвятся в 50 корректно | 6 496 | **Хуже не стало.** До этого PR те же 18 288 резолвились туда же, просто без конкурента, а оставшиеся 6 496 не резолвились никуда и падали в дефолтный регион 66 — в Свердловскую область. **Следствие для следующего шага:** импорт подмосковных объявлений обязан брать регион из города, а не из координаты. По имени Химки, Балашиха и Красногорск разрешаются верно. Настоящее решение — граница полигоном вместо прямоугольника (#3052). ## Test plan - [x] Центр Москвы, Зеленоград, Новая Москва → 77 - [x] Серпухов, Дмитров, Электросталь → 50 - [x] Екатеринбург → 66, поведение 66 не изменилось - [x] По имени: Химки, Балашиха, Красногорск → 50; Москва → 77; Екатеринбург → 66 - [x] Известное ограничение закреплено тестом, а не замолчано - [x] Исключение в `test_regions_do_not_overlap` не голословно — перед ним проверка, что `bbox_region(50)` действительно содержит `bbox_region(77)` как множество - [x] 81 passed по регионным файлам, `ruff` чистый - [x] Ревью — APPROVE, критичных замечаний нет 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01NQb6WeJtagZwZnUsSjDizs
lekss361 added 1 commit 2026-09-12 13:53:37 +00:00
feat(regions): регион 50 в реестре + обход по специфичности вместо кода региона
All checks were successful
CI Trade-In / changes (pull_request) Successful in 9s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 12s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m36s
18add6d667
Московская область заводится в реестр покрытия. Это предусловие для импорта
411 056 сделок Росреестра, которые лежат в FDW нетронутыми: импорт валидирует
region_code по REGIONS, и без записи в реестре не стартует.

## Обход реестра больше не по коду региона

region_for_point шёл по sorted(REGIONS) — «первый по коду выигрывает».
Докстринг называл это страховкой «на случай, если регионы когда-нибудь
пересекутся». Случай наступил прямо здесь: прямоугольник области содержит
прямоугольник Москвы целиком, а 50 меньше 77. Наивное добавление отправило бы
ВСЕ московские точки в регион 50, включая центр — и это доехало бы до оценки,
estimator.py:4862 берёт отсюда скоуп коридора ДКП, а сделок по области ноль.

Ключ сортировки — площадь bbox_region по возрастанию: вложенный регион всегда
проверяется раньше объемлющего, и следующий новый регион встанет на место сам,
без правки этого места. Порядок стал (77, 50, 66).

region_by_city оставлен по коду осознанно: там точное сравнение строки, а не
вложенность прямоугольников, и тот же класс бага возможен только при дубле
имени города между регионами. Дублей нет, это закреплено тестом.

## Границы области — по данным, а не по карте

У области нет города-ядра, поэтому tight/wide/product_core здесь не «город плюс
отступ», а эмпирический пояс: разброс координат 45 294 подмосковных карточек
Циана. product_core взят по перцентилям 0.5..99.5, wide — полный наблюдённый
диапазон. Административный bbox стоит только в bbox_region, где он и нужен как
fallback-accept. Обещать охват продукта там, где нет ни одного объявления,
нельзя — поэтому product_code намеренно уже административной границы.

Тиров обогащения у области нет ни одного, frozenset() пуст намеренно.
canonical_city=None: источники по области несут настоящий город (Химки,
Балашиха), в отличие от Москвы, где Росреестр отдаёт округ или поселение.

## Известное ограничение, которое этот PR НЕ чинит

По координате Химки и Балашиха резолвятся в 77, а не в 50. Это свойство
прямоугольника, а не выбранного порядка: любой прямоугольник, содержащий и
Зеленоград, и центр Москвы, покоординатно содержит Химки. Замер по собранным
московским карточкам ДомКлика подтверждает — Москва реально дотягивается до
долготы 37.95 (восточнее центра Балашихи) и до широты 56.01.

Масштаб: из 24 784 подмосковных объявлений Циана 18 288 лежат внутри
московского прямоугольника. Хуже от этого PR не стало — до него они резолвились
туда же, просто без конкурента. Оставшиеся 6 496 раньше не резолвились никуда и
падали в дефолтный регион 66, то есть в Свердловскую область; теперь встают в 50.

Практическое следствие для следующего шага: импорт подмосковных объявлений
обязан брать регион из города, а не из координаты. По имени Химки, Балашиха и
Красногорск разрешаются верно.

Настоящее решение — граница полигоном вместо прямоугольника (#3052).

## Тесты

Регрессия на порядок: центр Москвы, Зеленоград и Новая Москва остаются в 77;
Серпухов, Дмитров, Электросталь встают в 50; Екатеринбург не тронут.
Известное ограничение закреплено отдельным тестом, а не замолчано.

Существующий test_regions_do_not_overlap получил исключение для пары (50,77) —
вложенность реальна. Исключение не голословно: перед ним стоит проверка, что
bbox_region(50) действительно содержит bbox_region(77) как множество. Выедет 77
за границы области — тест упадёт с понятной причиной, а не пропустит настоящее
пересечение через список исключений.

81 passed по регионным файлам, ruff чистый.

Claude-Session: https://claude.ai/code/session_01NQb6WeJtagZwZnUsSjDizs
lekss361 merged commit cef872ace1 into main 2026-09-12 13:59:44 +00:00
lekss361 deleted branch feat/region-50-registry 2026-09-12 13:59:44 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#3501
No description provided.