feat(site-finder): X2 confidence indicator + caveats (#48) #88
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#88
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/site-finder-confidence"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Closes #48 (priority/high, UX X2 — confidence score). Stub-версия — использует currently-available сигналы, расширится после G1/G2/D1/D2.
Backend — composite confidence 0..1 в
/parcels/{cad}/analyze:confidence: float(0..1) — средневзвешенная по 7 подскорамconfidence_label:high(>0.75) /medium(0.4-0.75) /low(<0.4)confidence_breakdown: per-subscore 0..1 (poi_freshness, geom_source, district, market_trend, competitors, environment, zoning)confidence_caveats: string[]— конкретные проблемы для UIПодскоры (stub):
last_osm_edit_date > NOW() - 2y, coverage penalty при <10 POIFrontend —
ConfidenceBadge.tsx:low— caveats показываются сразу; дляmedium/high— под toggle «Подробнее»Acceptance (per issue #48)
Test plan
npx tsc --noEmit+npm run lint --max-warnings 0— clean (✅ локально)ruff check + format— clean (✅ локально)66:41:0204016:10→ badge в начале Overview⚠️ Auto-review: minor (non-blocking) — 0 блокеров, 6 рекомендаций.
Backend (parcels.py):
import datetime as _dtвнутри функции (~line 357_compute_confidence) — anti-pattern + overhead на каждый вызов. Перенести в module-level imports (top of file).Magic numbers без констант: 730 days, 10, 5 (POI thresholds), 0.75/0.4 (confidence thresholds) inline в
_compute_confidence. Вынести:_CONF_POI_DAYS_CUTOFF = 730,_CONF_HIGH = 0.75,_CONF_MEDIUM = 0.4. При тюнинге не придётся искать по тексту.env_ok = sum([bool(noise_sources_count > 0), bool(air_q), bool(weather)])— двойнойbool(...), плюсbool({})для пустого dict дастFalse(что норм, но неявно). Чище:sum([noise_sources_count > 0, air_q is not None, weather is not None]).Risk of ruff line ≤100 violation — caveat-строки длинноваты, например
"Геометрия quartal-level (нет parcel shape) — окружение усреднено по кварталу". Проверьruff check backend/app/api/v1/parcels.pyлокально — pre-commit может упасть.Frontend (types):
source: \"cad_quarter\" | \"cad_building\"— если SQL UNION умеет вернуть иcad_parcel(есть вcad_parcels_geom), нужно добавить literal. Иначе TS narrowing будет неверным для parcel-level результатов.<li key={i}>— array index как key для caveats. Лист статичный, без reorder — норм, но если будущая версия будет фильтровать caveats динамически, ключи могут переиспользоваться. Лучшеkey={cv}(caveat-строка как ключ, они уникальны).Что хорошо (✅):
_compute_confidenceчисто работает на already-fetched data.composite = max(0, min(1, ...))— defensive clamp.Record<Props[\"label\"], ...>дляLABEL_RU/COLOR— exhaustiveness check.aria-expandedна toggle button — accessibility.Все 6 рекомендаций применены в
7e570fe:Backend:
import datetime as _dtперенесён в top-of-file imports._CONF_HIGH,_CONF_MEDIUM,_CONF_POI_FRESH_DAYS,_CONF_POI_COVERAGE_MIN,_CONF_POI_LOW_THRESHOLD,_CONF_MARKET_DEALS_TARGET,_CONF_MARKET_DEALS_LOW,_CONF_COMPETITOR_TARGET.env_okбез двойного bool —[noise_sources_count > 0, air_q is not None, weather is not None].ruff check + format— clean (длина строк ≤100).Frontend:
5. ✅
source: "cad_quarter" | "cad_building" | "cad_parcel"— добавлен литерал.6. ✅ Caveats key —
key={cv}вместоkey={i}(string-as-key стабильно при фильтрации).tsc --noEmit+lint— clean. Ждём re-review.