Московская область считается по своему ряду Сбериндекса, а не по общероссийскому
All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m30s
All checks were successful
CI / changes (pull_request) Successful in 10s
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / backend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 4m30s
Загрузчик научился тянуть REF_AREA=50 раньше, и 12.09.2026 ряд приехал на прод: real_estate_deals, сегмент «Вторичный», 116 месячных точек с 2017-01 по 2026-08 — та же глубина, что у Москвы и Свердловской области. До этой правки код 50 в карте _SBER_REGION_SERIES отсутствовал, поэтому поправка по времени для области считалась по ряду «Россия», то есть ошибалась на всё расхождение области со страной. Теперь ошибка остаётся только внутрирегиональной. Область автоматически попадает в SBER_REQUIRED_REGIONS, то есть пропажа её ряда станет видимой монитору свежести — это и есть желаемое, данные под наблюдением. Фикстуры монитора, которые перечисляли ряды литералами, перевязаны на карту эстиматора: они краснели от самого факта появления третьего обязательного региона, хотя проверяют не его. Тест загрузчика, утверждавший, что ряд загружен, но эстиматором НЕ используется, заменён на сцепку имён: имя у загрузчика и у эстиматора обязано совпадать буква в букву, иначе один читает пустоту, а второй требует ряд, которого никто не пишет. Комментарий в реестре регионов о том, что кода 50 в карте ещё нет, поправлен. Набор тиров обогащения у области остаётся пустым: у Москвы он тоже пуст, а свой ряд она читает — поправка по времени идёт мимо enrichment_tiers.
This commit is contained in:
parent
c265e1f769
commit
6106ee1be8
4 changed files with 59 additions and 20 deletions
|
|
@ -328,6 +328,13 @@ SBER_TIME_ADJUST_REGION = "Свердловская область" # ряд р
|
|||
_SBER_REGION_SERIES: dict[int, str] = {
|
||||
66: SBER_TIME_ADJUST_REGION,
|
||||
77: "Москва",
|
||||
# Московская область — свой ряд, а не фолбэк на «Россию». Загрузчик научился
|
||||
# тянуть REF_AREA=50 (services/sber_index), и 12.09.2026 ряд приехал на прод:
|
||||
# real_estate_deals/«Вторичный», 116 месяцев 2017-01..2026-08 — та же глубина,
|
||||
# что у Москвы и обл.66. До этой строки область считалась по общероссийскому
|
||||
# ряду, то есть коридор ошибался на расхождение области со страной; теперь
|
||||
# ошибка только внутрирегиональная.
|
||||
50: "Московская область",
|
||||
}
|
||||
|
||||
# Ряд для региона ВНЕ карты. Выбран общероссийский, а не отказ от поправки (1.0):
|
||||
|
|
|
|||
|
|
@ -233,9 +233,11 @@ REGIONS: dict[int, Region] = {
|
|||
),
|
||||
# Тиров обогащения у области пока НЕТ ни одного: IMV/квартальный
|
||||
# индекс/кадастр/POI не заведены (проверено — frozenset() пуст
|
||||
# намеренно, не заглушка). Ряд Сбериндекса по области загружен в
|
||||
# источник (#3498), но в карте _SBER_REGION_SERIES эстиматора кода 50
|
||||
# ЕЩЁ НЕТ — тир пока недоступен, добавляется отдельным решением.
|
||||
# намеренно, не заглушка). Ряд Сбериндекса по области с 12.09.2026 в
|
||||
# карте _SBER_REGION_SERIES эстиматора есть, но тиром он от этого не
|
||||
# становится: у Москвы набор тиров тоже пуст, а свой ряд она читает —
|
||||
# поправка по времени идёт мимо enrichment_tiers (TIER_SBER_INDEX нигде
|
||||
# за пределами этого реестра не спрашивают).
|
||||
enrichment_tiers=frozenset(),
|
||||
# Источники по области несут настоящий city (Химки, Балашиха — не
|
||||
# муниципальный округ/поселение, в отличие от Москвы) — перезаписывать
|
||||
|
|
|
|||
|
|
@ -539,16 +539,24 @@ async def test_pull_sber_indices_asking_benchmark_logged(caplog: pytest.LogCaptu
|
|||
)
|
||||
|
||||
|
||||
def test_moscow_oblast_ref_area_is_loaded_but_not_yet_used_by_estimator() -> None:
|
||||
"""Область грузится заранее: ряд наполняется месяцами, задним числом не берётся.
|
||||
def test_moscow_oblast_ref_area_feeds_the_estimator_under_the_same_name() -> None:
|
||||
"""Имя ряда у загрузчика и у эстиматора обязано совпадать буква в букву.
|
||||
|
||||
Одновременно проверяем, что заведение ряда НЕ включает область в оценку и НЕ
|
||||
поднимает тревогу монитора свежести: обе величины считаются от карты
|
||||
эстиматора (_SBER_REGION_SERIES), а не от карты загрузчика.
|
||||
Загрузчик завёл REF_AREA=50 раньше (#3498), когда карта эстиматора кода 50
|
||||
ещё не знала и область считалась по «России». 12.09.2026 ряд приехал на прод
|
||||
(real_estate_deals/«Вторичный», 116 месяцев 2017-01..2026-08), и код 50 попал
|
||||
в `_SBER_REGION_SERIES`. С этого момента расхождение имён перестаёт быть
|
||||
безобидным: эстиматор молча читал бы пустой ряд, а монитор свежести требовал
|
||||
бы ряд, которого загрузчик не пишет. Тест сцепляет обе стороны по имени.
|
||||
"""
|
||||
from app.services.estimator import SBER_MONITORED_REGIONS, SBER_REQUIRED_REGIONS
|
||||
from app.services.estimator import (
|
||||
SBER_MONITORED_REGIONS,
|
||||
SBER_REQUIRED_REGIONS,
|
||||
sber_region_series_name,
|
||||
)
|
||||
from app.services.sber_index import SBER_REF_AREAS
|
||||
|
||||
assert SBER_REF_AREAS["50"] == "Московская область"
|
||||
assert "Московская область" not in SBER_REQUIRED_REGIONS
|
||||
assert "Московская область" not in SBER_MONITORED_REGIONS
|
||||
assert sber_region_series_name(50) == SBER_REF_AREAS["50"]
|
||||
assert "Московская область" in SBER_REQUIRED_REGIONS
|
||||
assert "Московская область" in SBER_MONITORED_REGIONS
|
||||
|
|
|
|||
|
|
@ -88,6 +88,21 @@ def test_series_names_agree_with_region_registry() -> None:
|
|||
assert regions_mod.REGIONS[code].name == name
|
||||
|
||||
|
||||
def test_resolver_maps_50_to_moscow_oblast_not_russia() -> None:
|
||||
"""Область получает СВОЙ ряд, а не общероссийский фолбэк.
|
||||
|
||||
Загрузчик научился тянуть REF_AREA=50 (#3498), и 12.09.2026 ряд приехал на
|
||||
прод: real_estate_deals/«Вторичный», 116 месяцев 2017-01..2026-08 — та же
|
||||
глубина, что у Москвы и обл.66. Пока кода 50 в карте не было, коридор
|
||||
области считался по «России», то есть ошибался на всё расхождение области
|
||||
со страной. Тест красный на прежней карте ПО ЗНАЧЕНИЮ: там вернулась бы
|
||||
«Россия».
|
||||
"""
|
||||
assert est.sber_region_series_name(50) == "Московская область"
|
||||
assert est.sber_region_series_name(50) != est.SBER_TIME_ADJUST_FALLBACK_REGION
|
||||
assert "Московская область" in est.SBER_REQUIRED_REGIONS
|
||||
|
||||
|
||||
def test_monitored_regions_cover_every_series_the_estimator_can_read() -> None:
|
||||
assert set(est.SBER_MONITORED_REGIONS) == set(est._SBER_REGION_SERIES.values()) | {
|
||||
est.SBER_TIME_ADJUST_FALLBACK_REGION
|
||||
|
|
@ -275,8 +290,15 @@ def test_monitor_verdict_for_sverdlovsk_unchanged(monkeypatch: pytest.MonkeyPatc
|
|||
|
||||
|
||||
def _without_moscow() -> _MonDB:
|
||||
"""Источник переименовал «Москва» → ряда с прежним именем больше нет."""
|
||||
return _MonDB({"Свердловская область": _JUN_2026, "Россия": _JUN_2026})
|
||||
"""Источник переименовал «Москва» → ряда с прежним именем больше нет.
|
||||
|
||||
Остальные обязательные ряды на месте — иначе тесты ниже считали бы пропажу
|
||||
Москвы вместе с чужими пропажами. Строятся от карты эстиматора, чтобы новый
|
||||
регион в ней не ронял эти тесты по счётчику (ровно так они и покраснели,
|
||||
когда в карту приехала область).
|
||||
"""
|
||||
present = {r: _JUN_2026 for r in est.SBER_MONITORED_REGIONS if r != "Москва"}
|
||||
return _MonDB(present)
|
||||
|
||||
|
||||
def test_missing_moscow_does_not_suppress_the_sverdlovsk_verdict(
|
||||
|
|
@ -352,7 +374,7 @@ def test_missing_fallback_series_does_not_fail_the_monitor(
|
|||
) -> None:
|
||||
"""«Россия» — фолбэк: сегодня по нему не считается ни одна сделка → не сбой."""
|
||||
calls = _patch_runs(monkeypatch)
|
||||
db = _MonDB({"Свердловская область": _JUN_2026, "Москва": _JUN_2026})
|
||||
db = _MonDB({r: _JUN_2026 for r in est.SBER_REQUIRED_REGIONS})
|
||||
out = mon.check_sber_freshness(db, run_id=4, params={}, now=_NOW) # type: ignore[arg-type]
|
||||
assert calls["failed"] is None
|
||||
assert out["regions_missing"] == 1
|
||||
|
|
@ -457,15 +479,17 @@ def test_duplicate_series_name_does_not_pin_regions_missing(
|
|||
def test_early_exit_fills_the_new_counters(monkeypatch: pytest.MonkeyPatch) -> None:
|
||||
"""КРАСНЫЙ ДО ПРАВКИ: в раннем выходе новые ключи отдавали нули.
|
||||
|
||||
Пропажа ДВУХ обязательных рядов была неотличима от пропажи одного свердловского,
|
||||
а найденный ряд не попадал в age_days_max вовсе.
|
||||
Пропажа ВСЕХ обязательных рядов была неотличима от пропажи одного
|
||||
свердловского, а найденный ряд не попадал в age_days_max вовсе. Счётчик
|
||||
сверяем с длиной карты, а не с литералом: новый регион в ней — не повод
|
||||
этому тесту краснеть.
|
||||
"""
|
||||
calls = _patch_runs(monkeypatch)
|
||||
db = _MonDB({est.SBER_TIME_ADJUST_FALLBACK_REGION: _JUN_2026})
|
||||
out = mon.check_sber_freshness(db, run_id=11, params={}, now=_NOW) # type: ignore[arg-type]
|
||||
assert calls["done"] is None
|
||||
assert calls["failed"] is not None
|
||||
assert out["regions_missing"] == 2
|
||||
assert out["regions_missing"] == len(est.SBER_REQUIRED_REGIONS)
|
||||
assert out["alert_regions_missing"] == 1 # ДО ПРАВКИ: 0
|
||||
assert out["age_days_max"] == 72 # ДО ПРАВКИ: 0 — найденный ряд был не виден
|
||||
# прежняя семантика раннего выхода не тронута
|
||||
|
|
@ -492,9 +516,7 @@ class _MonDBDriverFault(_MonDB):
|
|||
|
||||
def execute(self, stmt: Any, params: dict[str, Any] | None = None) -> _Result:
|
||||
if self._deactivated:
|
||||
raise PendingRollbackError(
|
||||
"Can't reconnect until invalid transaction is rolled back"
|
||||
)
|
||||
raise PendingRollbackError("Can't reconnect until invalid transaction is rolled back")
|
||||
params = params or {}
|
||||
if params.get("city") == self._broken:
|
||||
self._deactivated = True
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue