fix(ptica): достоверность видит зону ПЗЗ из НСПД, а не только из пустой таблицы (#2464) #2933

Merged
bot-backend merged 1 commit from fix/2464-confidence-zoning-source into main 2026-08-19 13:29:26 +00:00
Collaborator

Отчёт противоречил сам себе

_compute_confidence получал зону ПЗЗ только из legacy-словаря zoning, который питается таблицей pzz_zones_ekb. На проде она пуста — 0 строк (замер 19.08), поэтому data_available там всегда False.

Настоящая зона живёт в nspd_zoning: территориальные зоны дампа НСПД, а для участков в зазорах между зонами она синтезируется резолвером геопортала (см. комментарий PR-A #financial-zoning-decouple выше по файлу).

Прогон analyze в прод-контейнере, участок 66:41:0402029:25:

nspd_zoning.zone_code   = 'Ж-5'      <- зона известна
zoning.data_available   = False      <- пустая таблица
confidence              = 0.61 medium
подскор zoning          = 0.2   (из 7)
оговорка: «ПЗЗ zone_code не известен — нельзя оценить разрешённое использование»

Один и тот же ответ показывает зону Ж-5 и заявляет, что зона неизвестна.

Цена

Подскоров семь, значит в композите теряется (1.0 − 0.2) / 7 = 0.114: пользователь видит 0.61 там, где честно 0.72. Это не единичный случай: territorial_zones есть у 78.3% собранных кварталов (524 из 669), плюс геопортал добирает участки в зазорах — то есть занижение работает почти на каждом анализе ЕКБ.

Тесты переписаны после самопроверки

Первая версия падала на origin/main с TypeError: unexpected keyword argument 'nspd_zoning'. Это красное означает «возможности нет», а не «значение неверно» — и мои «контроли» падали ровно там же, то есть контролями не были: красное «нет параметра» неотличимо от красного «результат неправильный».

Переделал:

тест origin/main с правкой чем красный на main
test_analyze_does_not_claim_unknown_zone_when_nspd_resolved_it красный зелёный по поведению: «ответ показывает зону Ж-5 и одновременно заявляет, что зона неизвестна»
test_nspd_zone_counts_as_known красный зелёный TypeError (возможности нет)
test_regulation_zone_index_also_counts красный зелёный TypeError
test_composite_gain_matches_the_measured_cost красный зелёный TypeError
test_no_zone_anywhere_keeps_the_caveat зелёный зелёный контроль
test_legacy_source_still_honoured зелёный зелёный контроль

Контроли не передают новый аргумент вовсе — поэтому исполняются по обе стороны. Первый тест дополнительно сторожит проводку: параметр можно добавить в функцию и забыть передать на вызове, и все остальные проверки останутся зелёными.

test_composite_gain_matches_the_measured_cost пришпиливает и число подскоров (7), и прирост композита (0.11) — если кто-то добавит восьмой подскор, тест потребует пересчитать цену, а не промолчит.

pytest tests/api/v1: 354 passed, 1 skipped, rc=0 (код возврата снят без конвейера).

Что рядом и НЕ входит в этот PR

_build_zoning в full_report_html.py:460 при пустом nspd_zoning и заполненном legacy zoning проваливается мимо return и строит таблицу из пустого словаря. Дефект реальный, но недостижим: ветка требует непустого zoning.zone_code, а таблица pzz_zones_ekb пуста. Отмечу в эпике замером, чинить сейчас — менять код, который нельзя исполнить.

Refs #2464

## Отчёт противоречил сам себе `_compute_confidence` получал зону ПЗЗ **только** из legacy-словаря `zoning`, который питается таблицей `pzz_zones_ekb`. На проде она пуста — **0 строк** (замер 19.08), поэтому `data_available` там всегда `False`. Настоящая зона живёт в `nspd_zoning`: территориальные зоны дампа НСПД, а для участков в зазорах между зонами она синтезируется резолвером геопортала (см. комментарий PR-A `#financial-zoning-decouple` выше по файлу). Прогон `analyze` в **прод-контейнере**, участок `66:41:0402029:25`: ``` nspd_zoning.zone_code = 'Ж-5' <- зона известна zoning.data_available = False <- пустая таблица confidence = 0.61 medium подскор zoning = 0.2 (из 7) оговорка: «ПЗЗ zone_code не известен — нельзя оценить разрешённое использование» ``` Один и тот же ответ показывает зону Ж-5 и заявляет, что зона неизвестна. ## Цена Подскоров семь, значит в композите теряется `(1.0 − 0.2) / 7 = 0.114`: пользователь видит **0.61** там, где честно **0.72**. Это не единичный случай: `territorial_zones` есть у **78.3%** собранных кварталов (524 из 669), плюс геопортал добирает участки в зазорах — то есть занижение работает почти на каждом анализе ЕКБ. ## Тесты переписаны после самопроверки Первая версия падала на `origin/main` с `TypeError: unexpected keyword argument 'nspd_zoning'`. Это красное означает «возможности нет», а не «значение неверно» — и мои «контроли» падали ровно там же, то есть контролями не были: красное «нет параметра» неотличимо от красного «результат неправильный». Переделал: | тест | `origin/main` | с правкой | чем красный на main | |---|---|---|---| | `test_analyze_does_not_claim_unknown_zone_when_nspd_resolved_it` | **красный** | зелёный | **по поведению**: «ответ показывает зону Ж-5 и одновременно заявляет, что зона неизвестна» | | `test_nspd_zone_counts_as_known` | красный | зелёный | TypeError (возможности нет) | | `test_regulation_zone_index_also_counts` | красный | зелёный | TypeError | | `test_composite_gain_matches_the_measured_cost` | красный | зелёный | TypeError | | `test_no_zone_anywhere_keeps_the_caveat` | **зелёный** | зелёный | контроль | | `test_legacy_source_still_honoured` | **зелёный** | зелёный | контроль | Контроли не передают новый аргумент вовсе — поэтому исполняются по обе стороны. Первый тест дополнительно сторожит **проводку**: параметр можно добавить в функцию и забыть передать на вызове, и все остальные проверки останутся зелёными. `test_composite_gain_matches_the_measured_cost` пришпиливает и число подскоров (7), и прирост композита (0.11) — если кто-то добавит восьмой подскор, тест потребует пересчитать цену, а не промолчит. `pytest tests/api/v1`: **354 passed, 1 skipped, rc=0** (код возврата снят без конвейера). ## Что рядом и НЕ входит в этот PR `_build_zoning` в `full_report_html.py:460` при пустом `nspd_zoning` и заполненном legacy `zoning` проваливается мимо `return` и строит таблицу из пустого словаря. Дефект реальный, но **недостижим**: ветка требует непустого `zoning.zone_code`, а таблица `pzz_zones_ekb` пуста. Отмечу в эпике замером, чинить сейчас — менять код, который нельзя исполнить. Refs #2464
bot-backend added 1 commit 2026-08-19 13:09:48 +00:00
fix(ptica): достоверность видит зону ПЗЗ из НСПД, а не только из пустой таблицы (#2464)
All checks were successful
CI / changes (pull_request) Successful in 8s
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / backend-tests (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 / openapi-codegen-check (pull_request) Successful in 1m58s
CI / backend-tests (pull_request) Successful in 17m23s
956b4db010
_compute_confidence получал зону ТОЛЬКО из legacy-словаря `zoning`, который
питается таблицей pzz_zones_ekb. На проде она пуста — 0 строк (замер 19.08),
поэтому data_available там всегда False.

Настоящая зона живёт в nspd_zoning: территориальные зоны дампа НСПД, а для
участков в зазорах между зонами она синтезируется резолвером геопортала (см.
PR-A #financial-zoning-decouple). Из-за этого подскор «zoning» был 0.2 у КАЖДОГО
участка, а оговорка утверждала неправду.

Прогон analyze в прод-контейнере, участок 66:41:0402029:25:

    nspd_zoning.zone_code   = 'Ж-5'      <- зона известна
    zoning.data_available   = False
    confidence              = 0.61 medium
    подскор zoning          = 0.2
    оговорка: «ПЗЗ zone_code не известен — нельзя оценить разрешённое
               использование (G1 pending)»

Один и тот же ответ показывал зону Ж-5 и заявлял, что зона неизвестна. Подскоров
семь, цена в композите — (1.0 − 0.2)/7 = 0.114: 0.61 вместо 0.72.

Тесты переписаны после самопроверки: первая версия падала на origin/main с
TypeError, то есть красное означало «возможности нет», а не «значение неверно», и
контроли падали там же — контролями они не были. Теперь контроли не передают новый
аргумент и зелёные по обе стороны, а тест на уровне ручки красный ПО ПОВЕДЕНИЮ:
«ответ показывает зону Ж-5 и одновременно заявляет, что зона неизвестна». Он же
сторожит проводку — параметр можно добавить в функцию и забыть передать.

Переформатирование — от pre-commit ruff 0.7.4 (#2864), не моё.
bot-backend merged commit 37bf3d0e93 into main 2026-08-19 13:29:26 +00:00
bot-backend deleted branch fix/2464-confidence-zoning-source 2026-08-19 13:29:26 +00:00
Author
Collaborator

Прод-проверка: тот же зонд, тот же участок, другой ответ

Вызов analyze_parcel в работающем контейнере, участок 66:41:0402029:25 — ровно тот, на котором дефект был найден:

БЫЛО                                    СТАЛО
nspd_zoning.zone_code = 'Ж-5'           nspd_zoning.zone_code = 'Ж-5'
zoning.data_available = False           zoning.data_available = False
confidence            = 0.61 medium     confidence            = 0.72 medium
подскор zoning        = 0.2             подскор zoning        = 1.0
оговорка «ПЗЗ zone_code не известен»    оговорки нет

Прирост 0.11 — ровно посчитанные (1.0 − 0.2) / 7 = 0.114. Ложная оговорка исчезла, остальные две («POI старше 2 лет», «нет ДДУ в 3 км») на месте: правка не глушила предупреждения, а починила одно конкретное.

Проверял не факт мержа, а поведение механизма: те же входные данные дают другой выход.

### Прод-проверка: тот же зонд, тот же участок, другой ответ Вызов `analyze_parcel` в работающем контейнере, участок `66:41:0402029:25` — ровно тот, на котором дефект был найден: ``` БЫЛО СТАЛО nspd_zoning.zone_code = 'Ж-5' nspd_zoning.zone_code = 'Ж-5' zoning.data_available = False zoning.data_available = False confidence = 0.61 medium confidence = 0.72 medium подскор zoning = 0.2 подскор zoning = 1.0 оговорка «ПЗЗ zone_code не известен» оговорки нет ``` Прирост 0.11 — ровно посчитанные `(1.0 − 0.2) / 7 = 0.114`. Ложная оговорка исчезла, остальные две («POI старше 2 лет», «нет ДДУ в 3 км») на месте: правка не глушила предупреждения, а починила одно конкретное. Проверял не факт мержа, а поведение механизма: те же входные данные дают другой выход.
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#2933
No description provided.