Commit graph

216 commits

Author SHA1 Message Date
4205ecb483 Merge pull request 'feat(ptica): rich dark map (POI/connections/competitors/satellite) + ПТИЦА as default analysis route' (#1851) from feat/ptica-default-and-rich-map into main
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Successful in 3m17s
Deploy / deploy (push) Successful in 1m10s
2026-06-20 19:06:08 +00:00
7866479653 Merge pull request 'feat(ptica): wire real КСИТ/height/zone/density into cockpit (frontend of #1843)' (#1849) from feat/ptica-real-ksit-frontend into main
Some checks failed
Deploy / deploy (push) Blocked by required conditions
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Has been cancelled
2026-06-20 19:05:39 +00:00
ba05a4cb62 feat(ptica): rich dark cockpit map + ПТИЦА as the default analysis route
All checks were successful
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 49s
CI / openapi-codegen-check (pull_request) Successful in 1m48s
Fix two regressions vs the prototype:
1. PticaMapInner: render real data layers over the dark base by REUSING the
   existing analysis-map components — POI by category, competitors/pipeline
   (MarketLayers), connection points + colored polylines (ConnectionPointsLayer),
   ЗОУИТ (ZouitLayer), custom POI add/edit/delete (CustomPoiLayer + mutation hooks).
   Add Спутник/Схема base toggle (Esri default + --map-filter-sat); legend rows
   toggle layers; «Точки подключения» driven by real CP data. Isochrones left
   «скоро» (ORS is on-demand, not in /analyze).
2. /site-finder/analysis/[cad] now renders the ПТИЦА cockpit — every parcel entry
   opens ПТИЦА, not the old design. [cad]/ptica redirects to the parent (single
   cockpit copy). Old AnalysisPageContent kept in repo; legacy UI at /legacy/site-finder.
3. Surface 3D massing: new «Инсоляция · 3D-масса» scan card + 3D legend row + 3D
   map tool all open the insolation/future3d drawers.

SSR-safe (Leaflet only behind dynamic ssr:false); real POI from score_breakdown;
no redirect loop. tsc/lint/prettier/build green. code-reviewer APPROVE.
2026-06-21 00:04:00 +05:00
167fcbace2 feat(ptica): wire real ПЗЗ regulation (КСИТ/height/zone/density) into cockpit
All checks were successful
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 51s
CI / openapi-codegen-check (pull_request) Successful in 1m46s
Read nspd_zoning.{max_far,max_height_m,max_floors,max_building_pct,
regulation_zone_index,regulation_source} (backend PR #1847) into the urban
scan-card, urban/buildability/potential drawers and the 3D massing КСИТ-target.
Defensive: every read is optional-chained + != null guarded → identical "—"
placeholders when fields absent, so the cockpit is unchanged until #1847 deploys,
then auto-lights-up. КСИТ density = area × max_far; пятно = area × max_building_pct
(percent 0..100, verified vs backend parser); sellable labeled estimate. 3D maxFar
= nspd_zoning.max_far ?? 3.5 (finite/>0 guarded). NspdZoning type +7 optional fields.
TS strict, no any.
2026-06-20 23:33:41 +05:00
88865bf6a7 fix(ptica): drawer modal a11y (focus-trap + restore) + polish
All checks were successful
CI / changes (pull_request) Successful in 5s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 57s
CI / openapi-codegen-check (pull_request) Successful in 1m52s
PticaDrawer: proper modal focus management —
- focus-trap: Tab/Shift+Tab wrap inside the dialog (focusables re-queried per
  Tab so it survives lazy panels/sub-tabs), focus-already-outside pulled back in;
- focus-restore: the trigger element is captured on open and re-focused on close
  (guards null + detached node);
- onClose held in a ref so the effect depends on [open] only — fixes focus
  thrashing when the host's URL/searchParams change mid-open.
Esc/scrim close, body-scroll-lock, role=dialog/aria-modal preserved.

Cleanups (code-review follow-ups):
- MassingScene: drop duplicate initial mass build (floors/sections effect owns
  it); corrected the effect-ordering comment.
- Static inline styles → CSS-module classes (scan/gauge/score/spaced rows).
- DrawerPrimitives: remove !important on .dtabActive via specificity
  (.dtabs button.dtabActive).
- Remove unused SoonCard component + its CSS.

tsc/lint/prettier clean; next build green (route bundled). ptica-only, no any.
2026-06-20 20:13:26 +05:00
5779e512be feat(ptica): interactive 3D building-massing module (Three.js) [PR#5]
Port the prototype massing.js to a client-only React component (MassingScene)
behind a dynamic(ssr:false) wrapper (MassingViewer), wired into the future3d +
insolation drawers, replacing the СКОРО SoonCards.

Generative mass from parcel area (real geometry_suitability.area_ha) × КСИТ-цель
3.5 (labelled regulation-default · НСПД — honest, real max_far not in /analyze),
OrbitControls (orbit/zoom/auto-rotate), time-of-day sun with shadows (insolation
preview), live GFA/КСИТ-факт metrics (green ≤3.5 / amber over). Full renderer/
scene/RAF dispose on drawer close; graceful WebGL-unavailable fallback.

Adds three@0.184.0 + @types/three (package.json + package-lock.json in sync).
SSR-isolated (three imported only in MassingScene). TS strict, no any.
2026-06-20 16:27:36 +05:00
95f93da83b feat(ptica): right-slider detail drawer system + 17 drawers wired (PR#4)
PticaDrawer: controlled right slider (480px / full-screen <768), scrim+Esc
close, focus-on-open, body-scroll-lock (restored on close/unmount), dialog
a11y. Deep-link ?drawer= via useSearchParams + shallow router.replace (no
render loop; unknown keys ignored). Enables the «Подробнее» buttons on scan
+ hero cards.

17 drawer contents (passport/buildability/urban/restrictions/engineering/
market[4 sub-tabs]/economy/risks/georisks/oks/potential/product/finance/legal/
environment/insolation/future3d) built from ParcelAnalysis + forecast product_tz.
Many are REAL: environment (noise/air_quality/wind/weather), georisks (geology/
hydrology/geotech_risk), market (competitors/market_trend/velocity/pipeline),
restrictions (zouit/red_lines/zoning), engineering, passport. Absent fields
(КСИТ/ОКС/финмодель/insolation) show honest «—»/«скоро» via ptica-adapt.

All field paths verified against types. Dark theme scoped. TS strict, no any.
Follow-up: drawer focus-trap + focus-restore for full modal a11y.
2026-06-20 16:16:39 +05:00
e18bb06539 feat(ptica): build Отчёты + Сравнение cockpit tabs (PR#3)
Reports: dark export cards reusing real infra — PDF snapshot (/snapshot.pdf
via shared API_BASE_URL+triggerDownload), client-side CSV (formula-injection
guard + UTF-8 BOM preserved), §22 forecast DOCX/PPTX gated on forecast-ready
with honest 404; full-report DOCX/PPTX disabled «скоро» (no endpoint). Section
checklist mirrors the snapshot TOC.

Compare: reuses compare-shortlist (localStorage, COMPARE_MAX) + ParcelCompareLoader
for real per-cad analyze metrics (shared cache, rules-of-hooks safe via keyed
loader). Current cad auto-pinned + non-removable, validated кадастр input,
removable chips. Dark comparison table with winner highlight + colour-coded
verdict (Можно/С огранич./Нельзя).

Dark styles scoped under .pticaRoot[data-theme=dark]; reuse-only (ExportButtons,
compare page, loaders untouched); no new deps. TS strict, no any.
2026-06-20 15:58:50 +05:00
b0736e3a45 feat(ptica): build Сценарии tab (§22 forecast) + real hero invest-score
Dark cockpit Scenarios tab wired to useParcelForecastQuery (ForecastEnvelope):
scenario cards (base/aggr/conserv), demand-vs-supply chart + horizons table,
scenario compare table, confidence panel, recommended product (квартирография
+ target price + success-score), scoring transparency. Calm polling skeleton
while the async forecast is pending — never an error.

Hero invest-score/buy-signal now surface real values once the forecast resolves
(scoring.overall×10; buy-signal from exec_summary.key_numbers.deficit_index,
derived independent of the overall score), keeping the "после прогноза"
placeholders while pending. advisory · §22 caption shown in both states.

All forecast field paths verified against types/forecast.ts. Dark theme stays
scoped under .pticaRoot[data-theme=dark]. TS strict, no any.
2026-06-20 15:48:54 +05:00
05eb70eb62 feat(site-finder): ПТИЦА dark cockpit — increment 1 (shell + hero + Development Scan)
All checks were successful
CI / changes (pull_request) Successful in 5s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 55s
CI / openapi-codegen-check (pull_request) Successful in 1m57s
New non-destructive route /site-finder/analysis/[cad]/ptica rendering the existing
/analyze data in the «ПТИЦА» operator-terminal cockpit. The light analysis page,
Section1-6, globals.css and package.json are untouched.

PR#1 scope: app-shell (topbar + 4 tabs + left rail), hero (dark Leaflet map +
parcel passport KV-grid + Buildability radial gauge + invest-score block), and the
Development Scan card grid — wired to useParcelAnalyzeQuery / ParcelAnalysis.

Data honesty: real fields render live (passport egrn.*, district, utilities.summary,
median_price, pipeline_24mo); absent fields show muted placeholders with source
captions (buildability/risk = derived proxy «предв.»; invest/buy-signal «после
прогноза»; КСИТ/height/ОКС/economy «—») via typed ptica-adapt.ts — no fake numbers
presented as live.

Dark theme is a CSS-module scoped under .pticaRoot[data-theme=dark] (cannot leak to
the light app); IBM Plex Mono added via next/font (no dep/lockfile change). Leaflet
via dynamic(ssr:false). TS strict, no any. Deferred to follow-up PRs: Scenarios
(forecast), Reports+Compare tabs, 16 detail drawers, Three.js 3D massing.
2026-06-20 15:29:53 +05:00
5408466905 chore(frontend): regen api-types for snapshot exclude_dev param
All checks were successful
CI / changes (pull_request) Successful in 5s
CI / frontend-tests (pull_request) Successful in 1m1s
CI / openapi-codegen-check (pull_request) Successful in 1m50s
CI / backend-tests (pull_request) Successful in 8m50s
2026-06-18 15:51:56 +03:00
2b4f80e39f Merge pull request 'feat(site-finder): карта точек подключения (НСПД) в секции «Сети» (#1746 part)' (#1751) from fix/1746-networks-section-map into main
All checks were successful
Deploy / changes (push) Successful in 9s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Successful in 3m45s
Deploy / deploy (push) Successful in 1m23s
Reviewed-on: #1751
2026-06-18 08:44:16 +00:00
6fe72d2374 Merge pull request 'feat(site-finder): продуктовые решения по фидбеку analyze (#1741-1745)' (#1749) from fix/sf-product-decisions into main
Some checks failed
Deploy / deploy (push) Blocked by required conditions
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Has been cancelled
Deploy / build-worker (push) Has been cancelled
Deploy / build-frontend (push) Has been cancelled
Reviewed-on: #1749
2026-06-18 08:43:43 +00:00
e89fd66639 feat(site-finder): карта точек подключения (НСПД) в секции «Сети» (#1746, доступная часть)
All checks were successful
CI / changes (pull_request) Successful in 7s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 48s
CI / openapi-codegen-check (pull_request) Successful in 1m40s
Section2 «Сети и точки подключения» был только таблицей расстояний без карты — пользователь не видел ГДЕ точка. Добавлена карта (dynamic SiteMap ssr:false + useConnectionPoints + ConnectionPointsLayer/CpLayerControlPanel), label «Точки подключения и охранные зоны сетей (НСПД)», empty-state при отсутствии дампа квартала. Таблица расстояний не тронута. Frontend-only.

Полное обогащение 191-ФЗ (владелец/диаметр/мощность, единый источник истины) — остаётся за датасетом Минстроя (#1746 не закрыт целиком).
2026-06-18 12:48:23 +05:00
d93553c489 fix(site-finder): перенумеровать под-подписи Section3 4.1/4.2/4.3 под новый порядок + doc-хедеры (#1741 хвост)
All checks were successful
CI / changes (push) Successful in 8s
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (push) Successful in 1m17s
CI / frontend-tests (pull_request) Successful in 1m20s
CI / openapi-codegen-check (push) Successful in 2m13s
CI / openapi-codegen-check (pull_request) Successful in 1m40s
CI / backend-tests (pull_request) Successful in 9m0s
CI / backend-tests (push) Successful in 9m4s
2026-06-18 12:38:54 +05:00
8cd0620543 feat(site-finder): продуктовые решения по фидбеку analyze (#1741 #1742 #1743 #1744 #1745)
All checks were successful
CI / changes (pull_request) Successful in 8s
CI / changes (push) Successful in 9s
CI / frontend-tests (push) Successful in 1m43s
CI / frontend-tests (pull_request) Successful in 1m59s
CI / openapi-codegen-check (push) Successful in 2m43s
CI / openapi-codegen-check (pull_request) Successful in 1m55s
CI / backend-tests (push) Successful in 9m28s
CI / backend-tests (pull_request) Successful in 9m28s
После ролевой дискуссии (аналитик×комдир×собственник):
- #1741 секции в 2 группы «Участок»→«Стройка и рынок» (Section1→4→2→3→5→6), gate-баннер вынесен НАД группой (убран дубль из Section4Estimate), nav-группы
- #1742 объяснение выбора класса: фраза-причина + мини-таблица 3 классов по deficit_index («+1 острый дефицит, −1 затоварка»). POI «обеспеченность»/market-overstock relabel — N/A (в UI не выводятся)
- #1743 «Остатки и скорость»→«Как продаётся рынок рядом» + headline-вердикт + «Темп продаж»/«Будущее предложение конкурентов (24 мес)» (убран англицизм Velocity-score/Pipeline) + интерпретированные единицы
- #1744 горизонт: caption+tooltip «пересчёт ~10-30с» + статус «Пересчитываем…»; колонки прогноза «…, квартир» + caveat advisory
- #1745 фаза1: таблица «Прогноз по всем форматам» (студия/1-5к × индекс дефицита/прогноз спроса/сигнал); backend пробросил per-format projected_demand_units + signal. Слайдеры/рубли — фаза2 после бэктеста #951

Verify: tsc 0 ошибок; py_compile; what_to_build/recommendation/report/exporter тесты pass.

Closes #1741
Closes #1742
Closes #1743
Closes #1744
Closes #1745
2026-06-18 12:18:04 +05:00
3280859170 Merge remote fix/sf-feedback-batch (#1736 minimap test) into local
All checks were successful
CI / changes (pull_request) Successful in 8s
CI / frontend-tests (pull_request) Successful in 1m39s
CI / openapi-codegen-check (pull_request) Successful in 3m30s
CI / backend-tests (pull_request) Successful in 9m35s
2026-06-18 10:05:56 +03:00
0f9babb7ff test(site-finder): wrap MiniMap test render in QueryClientProvider (#1736)
All checks were successful
CI / changes (pull_request) Successful in 11s
CI / changes (push) Successful in 15s
CI / frontend-tests (push) Successful in 1m31s
CI / frontend-tests (pull_request) Successful in 1m17s
CI / openapi-codegen-check (push) Successful in 2m28s
CI / openapi-codegen-check (pull_request) Successful in 2m37s
CI / backend-tests (push) Successful in 10m32s
CI / backend-tests (pull_request) Successful in 10m34s
#1736 added useConnectionPoints (useQuery) to MiniMap, but MiniMap.test.tsx
rendered without a QueryClient -> 'No QueryClient set' (2 failing tests in CI
frontend-tests). Wrap renders in a QueryClientProvider (retry:false).
2026-06-18 10:03:48 +03:00
bcd24923b8 Merge remote-tracking branch 'forgejo-backend/main' into fix/sf-feedback-batch 2026-06-18 10:01:43 +03:00
e7342d9f98 chore(frontend): regenerate api-types.ts for forecast pdf export (#1739)
Some checks failed
CI / changes (pull_request) Successful in 8s
CI / changes (push) Successful in 10s
CI / frontend-tests (pull_request) Failing after 1m47s
CI / frontend-tests (push) Failing after 1m55s
CI / openapi-codegen-check (pull_request) Successful in 3m11s
CI / openapi-codegen-check (push) Successful in 3m1s
CI / backend-tests (pull_request) Successful in 10m29s
CI / backend-tests (push) Successful in 10m41s
#1748 added format=pdf to the forecast export endpoint but didn't regenerate
the frontend OpenAPI types, failing CI openapi-codegen-check. Regenerated via
the CI recipe (app.openapi() dump -> openapi-typescript -> prettier).
2026-06-18 10:00:26 +03:00
b1c0ea1268 fix(site-finder): 5 дефектов из боевого фидбека analyze (#1736 #1737 #1738 #1739 #1740)
Some checks failed
CI / changes (push) Successful in 9s
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (push) Failing after 1m50s
CI / frontend-tests (pull_request) Failing after 1m48s
CI / openapi-codegen-check (pull_request) Failing after 2m39s
CI / openapi-codegen-check (push) Failing after 2m46s
CI / backend-tests (pull_request) Successful in 9m25s
CI / backend-tests (push) Successful in 9m30s
- #1736 MiniMap: проброс useConnectionPoints → точки подключения на карте analyze (были только в /legacy)
- #1737 confidence: пронесено имя сервиса → RU-ярлык (Рынок/Будущее предложение/…) вместо «Компонент вкладывающий сервис»
- #1738 pipeline: self-exclusion субъекта (ST_DWithin 80м) — проект не считает сам себя будущим конкурентом
- #1739 PDF: snapshot_pdf обёрнут в try/except+logger.exception (причина 500 видна) + format=pdf в forecast export + font_url fallback
- #1740 gate↔recommendation: при can_build_mkd=False — gate_caveat на обоих рекомендаторах (противоречие явное, не молчит)

Verify: py_compile 5/5, tsc 0, ruff clean, pytest confidence/forecast 95 passed.

Closes #1736
Closes #1737
Closes #1738
Closes #1739
Closes #1740
2026-06-18 11:47:21 +05:00
d44f78c33e fix(frontend): regen api-types.ts — add POI-score fields (#1709)
All checks were successful
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 47s
CI / openapi-codegen-check (pull_request) Successful in 3m7s
openapi-codegen-check падал СИСТЕМНО на каждом backend-PR: committed
frontend/src/lib/api-types.ts отставал от backend OpenAPI на 2 поля,
добавленных в POI-score схемы (PR ~#1486 normalize POI weighted score),
но не перегенерённых:
  - PoiScoreItem.score_contribution
  - PoiScoreResponse.poi_weighted_score

Контент взят 1:1 из authoritative CI-диффа (openapi-typescript + prettier).
Возвращает gate openapi-codegen-check в зелёное для всех PR.

Refs #1709
2026-06-17 22:22:02 +03:00
7d5962e7b4 Merge pull request 'fix(site-finder): normalize POI weighted score to 0..100 on backend (#1486)' (#1674) from fix/poi-score-scale-1486 into main
Some checks are pending
Deploy / changes (push) Waiting to run
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
2026-06-17 17:54:52 +00:00
b534579e1b fix(site-finder): correct POI-score mock shape+values, routing empty assert (#1486 review)
Some checks failed
CI / changes (pull_request) Successful in 8s
CI / changes (push) Successful in 9s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been skipped
CI / openapi-codegen-check (push) Has been skipped
CI / frontend-tests (pull_request) Successful in 49s
CI / openapi-codegen-check (pull_request) Failing after 1m35s
CI / backend-tests (pull_request) Failing after 8m47s
- Rename top_poi → items in poi-score.json to match PoiScoreResponse TS type
  (mock was cast as PoiScoreResponse but had wrong field name → items undefined
  at runtime in MOCK_POI_SCORE mode → PoiList2Gis crashed at [...items].sort)
- Recompute all score_contribution values using backend formula
  (weight / _MAX_STRAIGHT_SCORE * 100, _MAX_STRAIGHT_SCORE=0.315) and
  poi_weighted_score=19.9 (was 72, which was inconsistent with the new normalization)
- Add assert result.poi_weighted_score == 0.0 to test_routing_decay_empty_db
  to match the straight-line empty-db assertion
- Remove stale comment in PoiList2Gis.tsx saying normalization needs fixing in
  site-finder-api.ts (already done backend-side in this PR)
2026-06-17 20:47:01 +03:00
3ffba9f8a8 Merge pull request 'fix(analytics): align portfolio series to area buckets in quartirography (#1406)' (#1675) from fix/quartirography-area-bucket-1406 into main
Some checks are pending
Deploy / changes (push) Waiting to run
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
2026-06-17 17:44:58 +00:00
79c5068e96 Merge pull request 'fix(analytics): drop false market verdict from per-object Sold % KpiCard (#1407)' (#1672) from fix/sold-pct-market-verdict-1407 into main
Some checks are pending
Deploy / changes (push) Waiting to run
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
2026-06-17 17:44:47 +00:00
68eb6ded89 fix(site-finder): normalize POI weighted score to 0..100 on backend (#1486)
Some checks failed
CI / changes (push) Successful in 10s
CI / changes (pull_request) Successful in 12s
CI / frontend-tests (push) Successful in 49s
CI / openapi-codegen-check (push) Failing after 1m45s
CI / backend-tests (push) Failing after 8m57s
CI / frontend-tests (pull_request) Successful in 45s
CI / openapi-codegen-check (pull_request) Failing after 1m37s
CI / backend-tests (pull_request) Failing after 8m53s
Backend now computes poi_weighted_score and per-POI score_contribution in
0..100 range. Normalization denominator = sum of top-7 category weights / 100
(straight-line mode) or sum of top-7 category weights (routing-decay mode).
Frontend stops reconstructing score from raw weight × 100.
2026-06-17 20:26:35 +03:00
26ae507c11 fix(analytics): align portfolio series to area buckets in quartirography (#1406)
Some checks failed
CI / changes (push) Successful in 8s
CI / changes (pull_request) Successful in 9s
CI / frontend-tests (push) Successful in 1m1s
CI / openapi-codegen-check (push) Successful in 1m50s
CI / backend-tests (push) Failing after 9m8s
CI / frontend-tests (pull_request) Successful in 54s
CI / openapi-codegen-check (pull_request) Successful in 1m59s
CI / backend-tests (pull_request) Failing after 8m56s
Switch portfolio source from domrf_region_aggregates (room_count_type ONE/TWO/THREE/FOUR)
to domrf_flat_area_distribution (area_bucket FROM_0_TO_25…FROM_100), aggregated into the
same 5 area buckets as the deals series. Both series now share the same axis and are
directly comparable. Frontend portfolioMap simplified to direct bucket key lookup.
2026-06-17 20:25:17 +03:00
66afac8bb3 fix(analytics): drop false market verdict from per-object Sold % KpiCard (#1407)
All checks were successful
CI / changes (push) Successful in 8s
CI / openapi-codegen-check (push) Successful in 1m47s
CI / changes (pull_request) Successful in 6s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Successful in 50s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 1m4s
CI / openapi-codegen-check (pull_request) Successful in 2m19s
2026-06-17 20:22:29 +03:00
249182b678 fix(week-review): дожим ревью — #1569, #1590, #1642 + feat #801 MSW-preview
All checks were successful
CI / frontend-tests (pull_request) Successful in 1m2s
CI / openapi-codegen-check (push) Successful in 2m13s
CI / openapi-codegen-check (pull_request) Successful in 1m45s
CI / backend-tests (push) Successful in 9m29s
CI / backend-tests (pull_request) Successful in 9m29s
CI / changes (push) Successful in 8s
CI / changes (pull_request) Successful in 8s
CI / frontend-tests (push) Successful in 1m7s
Финиш-волна по cross-file/partial остаткам аудита + a11y-харнесс.

Полностью (4): #1569 alembic регистрирует все ORM-модели (+убран фантомный Parcel) ·
#1590 «Индекс дефицита» больше не лжёт на fallback-горизонте (report_pdf+report_md) ·
#1642 thumbs mtime-freshness (нет устаревшей миниатюры) ·
#801 /__preview/estimate mock-render для axe/lighthouse (env-gated bypass).

Частично (остаток cross-file/домен, открыты): #1593 #1635 #1640.
needs-leha (открыт): #1650 (нужна новая миграция вьюхи supply-слоёв).

Verify: tsc --noEmit 0; py_compile OK.

Closes #1569
Closes #1590
Closes #1642
Closes #801
2026-06-17 11:24:34 +05:00
edfdd04923 fix(frontend): regen api-types.ts — rate_ms в BulkGeoEnqueueRequest
Some checks failed
CI / changes (push) Successful in 7s
CI / changes (pull_request) Successful in 8s
CI / frontend-tests (push) Successful in 59s
CI / frontend-tests (pull_request) Successful in 55s
CI / openapi-codegen-check (push) Successful in 2m5s
CI / openapi-codegen-check (pull_request) Successful in 1m32s
CI / backend-tests (push) Failing after 8m53s
CI / backend-tests (pull_request) Failing after 8m50s
Бэкенд-схема BulkGeoEnqueueRequest получила поле rate_ms (int, default 600),
но api-types.ts не был перегенерён → CI openapi-codegen-check (drift-gate) RED.
Добавлено сгенерированное поле (проверено: openapi-typescript 7.13.0 + prettier
defaults даёт байт-идентичный результат).
2026-06-15 22:32:21 +03:00
479f01ae1a Merge remote-tracking branch 'forgejo/main' into HEAD
Some checks failed
CI / changes (push) Successful in 7s
CI / changes (pull_request) Successful in 8s
CI / frontend-tests (push) Successful in 1m7s
CI / openapi-codegen-check (push) Failing after 2m9s
CI / frontend-tests (pull_request) Successful in 1m4s
CI / openapi-codegen-check (pull_request) Failing after 1m37s
CI / backend-tests (pull_request) Failing after 9m7s
CI / backend-tests (push) Failing after 9m11s
# Conflicts:
#	backend/app/services/site_finder/gate_verdict.py
2026-06-15 21:07:05 +03:00
7fd69e90cd fix(site-finder/compare): thread velocityDataAvailable into compare columns (#1422)
Some checks failed
CI / changes (push) Successful in 7s
CI / frontend-tests (push) Successful in 1m4s
CI / changes (pull_request) Successful in 15s
CI / openapi-codegen-check (push) Failing after 2m9s
CI / frontend-tests (pull_request) Successful in 1m12s
CI / openapi-codegen-check (pull_request) Failing after 1m46s
CI / backend-tests (push) Failing after 9m1s
CI / backend-tests (pull_request) Failing after 9m2s
2026-06-15 21:33:10 +05:00
8279b0ee1f fix(week-review): cross-file добивка код-ревью (#1338, #1363, #1419, #1421, #1424, #1480)
- #1338 concepts.py: geometry.generate вынесен в run_in_threadpool (не блокирует event loop)
- #1363 house_imv_backfill.py + scrape_pipeline.py: периодический heartbeat в долгих IMV-фазах (reap_zombies не помечает живой sweep zombie)
- #1419 site-finder-api.ts: placeholderData keepPreviousData для bbox-запроса (KPI не мигает «0/0» при пане/зуме)
- #1421 site-finder-api.ts: area_ha nullable + адаптер не подставляет 0; null-гейт потребителей EntryMap.tsx, ParcelDrawer.tsx
- #1424/#1480 EstimateResult.tsx: блок «Параметры объекта» скрыт при восстановлении по shared-ссылке (нет мусора «0 м²/Студия/Этаж 0 из 0»)

Верификация: tsc --noEmit 0 ошибок; py_compile OK.
2026-06-15 20:29:00 +05:00
86e9ea2937 fix(week-review): автофиксы код-ревью — 169 issue (label «week ревью 1»)
Многоагентный аудит + имплементация: один воркер на файл, точечные правки.
Верификация: py_compile (47/47 .py) + tsc --noEmit (0 ошибок). Unit-тесты
не прогонялись (окружение не поднято: rollup native dep / нет pytest-venv).

Полностью исправлено (169): #1336, #1337, #1339, #1340, #1341, #1342, #1343, #1345, #1346, #1348, #1349, #1350, #1351, #1354, #1356, #1358, #1359, #1360, #1362, #1364, #1365, #1366, #1367, #1368, #1369, #1370, #1371, #1372, #1373, #1374, #1375, #1376, #1377, #1378, #1379, #1380, #1381, #1382, #1384, #1385, #1386, #1387, #1388, #1389, #1390, #1391, #1392, #1394, #1395, #1396, #1397, #1399, #1400, #1401, #1402, #1403, #1404, #1408, #1409, #1410, #1411, #1412, #1413, #1414, #1415, #1416, #1417, #1418, #1420, #1423, #1425, #1426, #1427, #1428, #1429, #1430, #1431, #1432, #1433, #1434, #1435, #1437, #1438, #1439, #1440, #1441, #1442, #1443, #1444, #1445, #1446, #1447, #1448, #1449, #1450, #1451, #1452, #1453, #1454, #1455, #1456, #1457, #1458, #1459, #1460, #1461, #1462, #1463, #1464, #1465, #1466, #1467, #1468, #1469, #1471, #1472, #1473, #1474, #1476, #1478, #1479, #1481, #1482, #1483, #1484, #1485, #1487, #1488, #1489, #1490, #1491, #1492, #1493, #1494, #1495, #1496, #1497, #1499, #1500, #1501, #1502, #1504, #1505, #1506, #1507, #1510, #1514, #1515, #1516, #1517, #1518, #1519, #1521, #1522, #1523, #1524, #1525, #1526, #1527, #1528, #1529, #1531, #1532, #1533, #1534, #1535, #1536, #1537, #1538

Частично (9, in-file часть, остаток cross-file): #1361, #1419, #1422, #1424, #1470, #1475, #1477, #1480, #1498
Требуют cross-file (3, не тронуты): #1338, #1363, #1421
Пропущено (1): #1539

Не входило в партию: 22 needs-Leha issue (нужны решения владельца).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-15 20:21:11 +05:00
b405ae291d feat(site-finder): additive objective_lots price fallback for competitors (#307 OBJ-3) (#1332)
All checks were successful
Deploy / changes (push) Successful in 7s
Deploy / build-backend (push) Successful in 1m57s
Deploy / build-worker (push) Successful in 2m51s
Deploy / build-frontend (push) Successful in 3m6s
Deploy / deploy (push) Successful in 1m12s
2026-06-14 16:10:02 +00:00
5507bbc1a4 feat(site-finder): domrf→cad geom-match + parking_ratio конкурентов (#96) (#1327)
Some checks failed
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / build-backend (push) Blocked by required conditions
Deploy / build-worker (push) Blocked by required conditions
Deploy / changes (push) Has been cancelled
2026-06-14 14:49:28 +00:00
eb251ba7e7 ci(infra): coverage-gate (#68) + OpenAPI codegen-assert (#69) + uptime monitoring (#75)
All checks were successful
CI / changes (push) Successful in 13s
CI / changes (pull_request) Successful in 12s
CI / frontend-tests (push) Successful in 53s
CI / openapi-codegen-check (push) Successful in 1m58s
CI / frontend-tests (pull_request) Successful in 47s
CI / openapi-codegen-check (pull_request) Successful in 1m50s
CI / backend-tests (push) Successful in 8m54s
CI / backend-tests (pull_request) Successful in 8m45s
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Successful in 2m50s
Deploy / build-frontend (push) Successful in 3m11s
Deploy / build-worker (push) Successful in 4m38s
Deploy / deploy (push) Successful in 2m1s
#68: pytest-cov + [tool.coverage] fail_under=65 (baseline 71%), backend-tests --cov + xml.
#69: openapi-codegen-check job (dump app.openapi() → openapi-typescript → git diff).
  Job использует node_modules-pinned openapi-typescript/prettier (НЕ npx --yes latest —
  иначе version-mismatch ложный diff). + regenerated api-types.ts из СВЕЖЕГО main openapi
  (8288 строк, включая #73 /freshness — синхронен).
#75: Uptime Kuma isolated stack + status.gendsgn.ru Caddy + external Telegram watchdog.
#77: no change — build cache достаточен.

Closes #68
Closes #69
Closes #75
Closes #77
2026-06-13 23:28:06 +05:00
8107c23f18 feat(frontend): mobile responsiveness (#66) + data-sources страница (#80)
Some checks failed
CI / changes (push) Successful in 8s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Successful in 1m3s
CI / changes (pull_request) Successful in 6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Successful in 50s
Deploy / changes (push) Has been cancelled
Deploy / build-frontend (push) Has been cancelled
Deploy / deploy (push) Has been cancelled
Deploy / build-backend (push) Has been cancelled
Deploy / build-worker (push) Has been cancelled
#66: responsive CSS-layer в globals.css (3-контекста <768/768-1280/>1280, не Tailwind
sm/md/lg) + ResizeObserver в ChartShell (единственный echarts импортёр → все charts
рефлоу) + class-применение (grid-collapse, nav horizontal-scroll, map-stack) на
site-finder/analytics. overflow-x guard.
#80: /data-sources attribution-страница (OSM ODbL/ДОМ.РФ/Объектив/Open-Meteo/Росреестр/
НСПД, server component, tokens) + footer-link.

Closes #66
Closes #80
2026-06-13 23:12:31 +05:00
0b20d98b45 feat(site-finder): ЗОУИТ visual layer на Leaflet карте (#255)
All checks were successful
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Successful in 2m49s
Deploy / deploy (push) Successful in 1m6s
ZouitLayer (GeoJSON-полигоны из geom_geojson, severity blocker/warning/info по keyword,
popup тип+имя) + ZouitLayerControlPanel (collapsible toggle per-severity, счётчик mappable)
+ SiteMap integration (default blocker+warning ON, info OFF, фон под POI). Graceful: нет
geom_geojson → слой/панель скрыты, текстовый NspdZouitOverlapsBlock остаётся. 10 vitest.

Closes #255
2026-06-13 17:41:08 +00:00
adc6dde015 feat(site-finder): multi-cad сравнение (#50) + analytics поиск/cross-links (#65)
All checks were successful
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Successful in 3m59s
Deploy / deploy (push) Successful in 1m7s
#50: /site-finder/compare — shortlist (localStorage, SSR-guard, cap 2-5) + CompareTable
(per-row winner-highlight, reuse useParcelAnalyzeQuery per cad, hook-order stable в .map,
cache-shared). Cross-link с site-finder.

#65: debounced search (300ms, client-side filter top-15) в DeveloperLeaderboard +
cross-link объект→Site Finder analyze. Existing developers compare не дублирован.

Closes #50
Closes #65
2026-06-13 17:14:15 +00:00
3bbbf25412 feat(concept): concept-компоненты (DrawMap/ParamsForm/VariantsResult/ResultMap/ExportButtons) (#57)
All checks were successful
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Successful in 2m45s
Deploy / deploy (push) Successful in 1m3s
Дополняет UI: 5 компонентов которые импортит page.tsx (Leaflet draw, форма ConceptInput,
табы вариантов, placement-карта, экспорт). Не вошли в предыдущий commit (untracked-dir).
ConceptResultMap: Leaflet stroke #D1D5DB (CSS var не резолвится в SVG).
2026-06-13 16:42:19 +00:00
750d34d5cb feat(concept): Concept UI Stage 1d — ввод участка + варианты (#57)
Заменяет concept/page.tsx TODO-stubs полным generative-флоу: draw-полигон или
кадастр→geom ввод участка, форма параметров ConceptInput, POST /concepts через
useMutation, табы вариантов с placement-картой + ТЭП KPI + финмодель + экспорт
(GeoJSON/CSV client-side, DXF/PDF на /concepts/export graceful 404). Типы зеркалят
concept.py; переиспользует site-finder Leaflet + analytics Section/KpiCard паттерны.
tsc + lint + build clean.

Closes #57
2026-06-13 16:42:19 +00:00
d737307022 feat(site-finder): НСПД verify deep-links + landing GenDesign vs НСПД таблица (#97 #100)
All checks were successful
Deploy / changes (push) Successful in 5s
Deploy / build-frontend (push) Successful in 4m29s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / deploy (push) Successful in 1m5s
#97: nspdLinks helper (nspdMapUrl/pkkSearchUrl, encodeURIComponent) + NspdVerifyLink
компонент (var(--accent), rel=noopener) → одноклик-сверка с НСПД/ПКК в Section1
(кадастр/ЕГРН) + Section2 (инж.сети) карточках analyze.

#100: landing — сравнительная секция «GenDesign и НСПД» (7-row таблица value-prop:
цены ДДУ/остатки/скорость/инвест-балл/прогноз/квартирография/экспорт сверх бесплатной
гос-карты) + footer deep-links.

Closes #97
Closes #100
2026-06-13 15:53:02 +00:00
84d08940c6 fix(analytics): свежая ипотечная ставка + recommend last_updated + drop dead route (#236 #237 #251)
All checks were successful
Deploy / changes (push) Successful in 6s
Deploy / build-frontend (push) Successful in 3m9s
Deploy / build-backend (push) Successful in 4m12s
Deploy / build-worker (push) Successful in 5m31s
Deploy / deploy (push) Successful in 1m19s
#236: _current_mortgage_rate сортировал cbr_mortgage_series по TEXT-полю period
лексикографически ('Январь'>'Февраль') → возвращал stale-точку. Сортировка по
parsed (year, month) + regex-guard на malformed period-строки. Остаётся на
subsidized weighted series (НЕ domrf market — иная метрика). Verified prod:
Февраль 2026 (было Январь 2026).

#237: scope.last_updated = MAX(snapshot_date) domrf_kn_objects в recommend_mix +
подпись «данные на <дата>» в recommend page.

#251: удалён never-implemented GET /parcels/{parcel_id} (501) + ParcelDetail схема
(frontend bare 1-сегментный route не зовёт).

Closes #236
Closes #237
Closes #251
2026-06-13 15:22:22 +00:00
414601c1d2 test(analytics): vitest для ObjectSaleChart union-months alignment (#1246)
All checks were successful
CI / changes (push) Successful in 7s
CI / backend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
Deploy / build-worker (push) Has been skipped
Deploy / deploy (push) Successful in 1m6s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (push) Successful in 56s
CI / frontend-tests (pull_request) Successful in 58s
Deploy / changes (push) Successful in 8s
Deploy / build-backend (push) Has been skipped
Deploy / build-frontend (push) Successful in 2m37s
union axis sorted, per-month alignment с nulls, регрессия-guard (хвост позднего
типа не клипается), happy-path unchanged, empty-state. 5 кейсов.
2026-06-13 18:20:41 +05:00
e2c8c0e97b fix(analytics): выровнять ObjectSaleChart серии по union месяцев всех типов (#1246)
Some checks failed
CI / changes (push) Successful in 7s
CI / backend-tests (push) Has been skipped
CI / frontend-tests (push) Has been cancelled
ObjectSaleChart строил ось X из месяцев ТОЛЬКО первого типа и привязывал серии
позиционно. domrf_kn_sale_graph фетчится per-type независимо от DOM.РФ → типы
могут нести разные наборы месяцев — позиционная привязка тогда смещает бары/
линию цены и молча клипает лишние точки. Текущие prod-данные выровнены (457/457
объектов, идентичные месяцы), поэтому live-поломки нет, но код полагался на
coincidence, не invariant. Ось из union всех месяцев + маппинг значений каждого
типа по месяцу через Map (null где отсутствует), как PrinzipVelocityChart. +vitest.

Closes #1246
2026-06-13 18:20:25 +05:00
16e505326d perf(site-finder): honour AbortSignal в poll-цикле useParcelAnalyzeQuery (#1248)
Some checks failed
Deploy / deploy (push) Blocked by required conditions
Deploy / changes (push) Successful in 6s
Deploy / build-backend (push) Has been skipped
Deploy / build-worker (push) Has been skipped
Deploy / build-frontend (push) Has been cancelled
queryFn игнорировал TanStack v5 AbortSignal: first POST /analyze, poll GET
/fetch-status и re-POST не передавали signal, цикл спал на non-abortable
setTimeout без проверки signal.aborted. При unmount / смене cad|horizon цикл
жил до 2-мин кэпа, дёргая GET /fetch-status каждые 2с + POST /analyze на каждый
ready — зомби-поллинг. Проброс signal во все 3 fetch, sleep через shared
abortableSleep (extracted из useSiteAnalysis #1242), throw AbortError перед
итерацией. +3 vitest (abort + happy-path).

Closes #1248
2026-06-13 13:19:50 +00:00
99c9a130bc feat(forecast): расширить горизонт прогноза до 24 мес (ТЗ §12.1)
All checks were successful
CI / changes (push) Successful in 6s
CI / frontend-tests (push) Successful in 46s
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (pull_request) Successful in 46s
CI / backend-tests (push) Successful in 6m35s
CI / backend-tests (pull_request) Successful in 6m30s
Deploy / changes (push) Successful in 5s
Deploy / build-backend (push) Successful in 2m14s
Deploy / build-frontend (push) Successful in 2m55s
Deploy / build-worker (push) Successful in 4m6s
Deploy / deploy (push) Successful in 1m40s
API отвергал ?horizon=24 (422), хотя ТЗ §12.1 называет 6/12/18/24, а движок
УЖЕ считает 24 на каждом ране: _DEFAULT_HORIZONS=(6,12,18,24) во всех 6 точках
стека (orchestrator/forecast-task/demand_supply_forecast/scenarios/
special_indices/report_assembler), PIPELINE_HORIZON_MONTHS=24.
_hidden_release_fraction клампит h/18→1.0 на 24 (без переполнения),
future_supply._horizon_weight расширяет окно чисто — скрытых ≤18 потолков нет.
Чистое расширение валидатора-enum, не новая математика.

Backend: _ALLOWED_FORECAST_HORIZONS → {6,12,18,24}, Query/docstring/error-msg.
Frontend: HorizonSelector HORIZONS=[6,12,18,24] (тип horizon=number, union не нужен;
прочие потребители data-driven через meta.horizons/forecasts_by_horizon).
Тесты: API принимает 24/отвергает 30; движок-тесты доказывают h=24 осмыслен
(поля посчитаны, demand(24)>demand(18), hidden созрел, индексы в диапазонах).

Closes #944 (Q1 горизонт 24)
2026-06-13 17:27:35 +05:00
d8a2ad7854 fix(auth): отобразить роль analyst в UserMenu, расширить Role union в useMe (#1237) 2026-06-13 15:03:15 +05:00