gendesign/backend/app/services/site_finder
Light1YT 41804ed70e fix(forecast): посегментный+дедуплицированный индекс дефицита (#1959)
Корень «−1.00 везде» (эпик #1953): compute_demand_supply_forecast брал
district-wide unit_velocity (847.5/мес, ВСЕ классы/комнаты) как спрос и
весь district-сток (~63k доступных) как предложение для КАЖДОЙ ячейки
what_to_build → один и тот же ratio во всех ячейках → все deficit_index
прижаты к −1.0. Плюс objective_lots — append-per-snapshot (~2.9× инфляция
строк), что симметрично раздувало обе базы → даже сегментация без дедупа
осталась бы вырожденной.

Фикс (blast radius — ТОЛЬКО forecast/deficit calc; platform-wide dedup = #1964):
- market_metrics.compute_market_metrics: +obj_class/+room_bucket (+cache key).
  _STOCK_SQL и _SALES_WINDOW_SQL дедуплят до ПОСЛЕДНЕГО снапшота на физлот
  (DISTINCT ON project_name,corpus_name,section,floor,lot_number ORDER BY …
  snapshot_date DESC,id DESC), затем агрегируют. Class-фильтр (LOWER=LOWER,
  class lowercase) + room-bucket (Source-B room_area-вокабуляр, зеркало
  sales_series.room_area_bucket_of → what_to_build фильтрует без перевода).
  ROLLUP/GROUPING сохранён; confidence считается на дедуплицированных counts.
- demand_supply_forecast: base_pace и open-сток теперь ПОСЕГМЕНТНЫЕ
  (market_metrics(obj_class,room_bucket)). При заданном сегменте L2/L3
  (hidden/future) ИСКЛЮЧЕНЫ из баланса — они класс/формат-агностичны, иначе
  двоились бы по всем ячейкам. +_market_room_bucket VOCAB-мост (валидирующий
  pass-through Source-B меток; неизвестное → None = без фильтра, не тихий 0-rows).
- what_to_build/_DEFAULT_CLASSES и recommendation Economy-маппинг: «эконом»→
  «стандарт» (в objective_lots эконома НЕТ, стандарт=483k → раньше ячейка
  матчила 0 строк и молча выпадала).
- report_assembler honesty-guard: если ВСЯ сетка прижата к ±1.0
  (degenerate-fallback) — не эмитим «строить»/«избегать», показываем
  «недостаточно гранулярных данных для посегментного вывода».
- data/sql/173_objective_lots_physflat_idx.sql: partial index под DISTINCT ON
  (Index Only Scan + Unique, без Sort на 1.75M строк; idempotent, BEGIN/COMMIT).

Prod-verify (parcel 66:41:0205010:287, Железнодорожный, h=24): ячейки
ДИФФЕРЕНЦИРУЮТ (12 measured, 7 distinct) вместо all −1.0; MOI комфорт/студия
38.5 vs стандарт/студия 244.3 (точное совпадение с ожидаемым).

Тесты: регрессия «ячейки различаются (не all −1.0)» + vocab-translation +
honesty-guard + посегментное предложение. ruff clean; no :name::type.
2026-06-27 22:50:58 +05:00
..
__init__.py init 2026-04-25 13:45:19 +03:00
best_layouts.py fix(site_finder): correct best-layouts supply fan-out + objects-first perf 2026-06-27 10:45:19 +05:00
cadastre_fetch.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
competitors.py fix(site-finder): align competitor flats_sold to objective_lots.contract_date (#1926) (#1927) 2026-06-26 18:33:51 +00:00
custom_pois.py feat(#254): user_custom_pois — backend schema + CRUD + scoring integration 2026-05-17 09:34:33 +03:00
developer_attribution.py feat(site-finder): детерминированная атрибуция застройщика в analyze (#1088) 2026-06-13 16:13:45 +05:00
district_resolver.py feat(forecast): resolve admin district -> micro set in §9.x market/supply/sales filters 2026-06-05 07:03:37 +05:00
filters.py init 2026-04-25 13:45:19 +03:00
functional_zone_lookup.py feat(sf): ingest функц.зон генплана ЕКБ-2045 из ГИСОГД-СО WFS → ekb_genplan_functional_zone (#1137) 2026-06-07 12:03:39 +00:00
future_supply.py perf(forecast): per-request memoization cache for §22 cold build (#1129) 2026-06-08 05:26:27 +00:00
gate_verdict.py fix(gate): area-gate ЗОУИТ utility-easement blocker (sub-17) — снять over-block 2026-06-24 14:45:59 +05:00
granddoc_lookup.py fix(week-review): backend-аудит v2 — 82 фиксов (#1660) 2026-06-17 17:13:38 +00:00
ird_analyze.py fix(site-finder): optimize zone-regulation resolve in /analyze (#1850) 2026-06-25 09:12:41 +00:00
ird_overlay_lookup.py fix(site-finder): расширить except в parcel_ird_overlaps до DataError (#1095) 2026-06-13 15:02:50 +05:00
krt_lookup.py fix(site-finder): склеить разорванный regex в _PLANNING_NO_MATCH_SQL (#1230) 2026-06-13 12:43:28 +05:00
layout_signature.py fix(sf-08): add euro-1/euro-2 room_bucket for small-format rooms=2 flats 2026-05-17 14:38:48 +03:00
layout_velocity_refresh.py feat(analytics): mv_layout_velocity MV + refresh helper (#113 PR B) (#194) 2026-05-16 08:26:59 +00:00
locations.py fix(site_finder): make Location demand_index city-relative (#948) 2026-06-08 13:58:12 +05:00
macro.py feat(macro): ЕМИСС среднедушевые доходы (id=57039) -> macro_indicator (#946 part2) 2026-06-07 18:44:38 +05:00
market_metrics.py fix(forecast): посегментный+дедуплицированный индекс дефицита (#1959) 2026-06-27 22:50:58 +05:00
network_obremenenie.py feat(site-finder): сетевые обременения из ЗОУИТ (#1070, Refs #103) (#1328) 2026-06-14 14:49:32 +00:00
noise_loader.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
okn_lookup.py fix(okn): expand registry coverage + load detail-card name/address (#1159) 2026-06-13 15:02:50 +05:00
ors_client.py feat(site_finder): routing POI decay (#41) + OSM utility distances (#44) + ЗОУИТ geom (#255bk) 2026-06-13 22:31:56 +05:00
osrm_client_local.py feat(analyze): per-category OSRM routing (foot vs driving) (#39 A3) 2026-06-27 04:30:25 +05:00
own_portfolio.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
parcel_financial.py feat(financial): ground-floor нежилое (office/commercial) tranche 2026-06-25 11:40:01 +00:00
parser.py init 2026-04-25 13:45:19 +03:00
pat_lookup.py feat(sf): подзоны ПАТ Кольцово → pat_subzones (#1150) 2026-06-07 14:43:07 +00:00
planning_lookup.py feat(sf): wire ППТ/ПМТ planning_projects в analyze ird-блок (#1105) 2026-06-06 20:50:34 +00:00
poi_loader.py fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1») 2026-06-15 20:21:11 +05:00
poi_score.py fix(site-finder): normalize POI weighted score to 0..100 on backend (#1486) 2026-06-17 20:26:35 +03:00
ppt_tep_lookup.py fix(week-review): backend-аудит v2 — 82 фиксов (#1660) 2026-06-17 17:13:38 +00:00
premises_lookup.py fix(week-review): backend-аудит v2 — 82 фиксов (#1660) 2026-06-17 17:13:38 +00:00
pzz_loader.py fix(pzz_loader): replace bare db.rollback() with SAVEPOINT per-row (#124) 2026-05-14 23:29:23 +03:00
quarter_dump_lookup.py docs(zouit): уточнить DISTINCT ON-комментарий + убрать stale cad_zouit из FE-типа (#1957) 2026-06-27 14:21:01 +05:00
quarter_price_index_refresh.py fix(week-review): backend-аудит v2 — 82 фиксов (#1660) 2026-06-17 17:13:38 +00:00
quarter_price_refresh.py feat(analytics): mv_quarter_price_per_m2 MV + refresh helper (#33 D1 PR A) (#209) 2026-05-16 11:12:01 +00:00
reservation_lookup.py feat(sf): ПАГЕ-парсер изъятия/резервирования → land_reservation (#1118) 2026-06-07 10:15:03 +00:00
riasurt_lookup.py feat(site-finder): РИАСУРТ Свердл 14-layer harvest client+schema+task for ЕКБ agglomeration (#108) 2026-06-17 21:49:04 +03:00
sales_tracker_mv_refresh.py chore(sql): renumber sales-tracker MV migration 161→164 (avoid collision) 2026-06-17 22:36:39 +03:00
saturation.py feat(site-finder): POI saturation per capita по районам ЕКБ (#42) (#1329) 2026-06-14 15:05:22 +00:00
scorer.py init 2026-04-25 13:45:19 +03:00
supply_layers.py fix(site-finder): non-overlapping L2/L3 ready_dt windows, stop double-count of 13-24mo supply (#1355) 2026-06-17 20:29:09 +03:00
utility_infrastructure_loader.py fix(site-finder): Overpass retry-backoff + mirror fallback for utility loader (#1746) (#1931) 2026-06-26 21:13:06 +00:00
velocity.py feat(etl): housing-class normalization fallback via yandex_realty trigram match (#38) (#1911) 2026-06-26 07:44:25 +00:00
weight_profiles.py feat(#114): seed 3 default weight presets + include_system API param 2026-05-16 22:49:56 +03:00
zone_regulation.py fix(site-finder): isolate zone-regulation cache write off the request session (#1850 item 4) 2026-06-25 11:16:26 +00:00