feat(sf-b6): GET /parcels/{cad}/poi-score — weighted top-7 #333
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#333
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/sf-b6-poi-weighted"
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
SF Wave 1 / Group B / sub-task B6. NEW POI weighted score endpoint.
Files
backend/app/services/site_finder/poi_score.py— NEW service modulebackend/app/api/v1/parcels.py— добавленGET /{cad_num}/poi-scoreendpointbackend/tests/test_poi_score.py— 12 unit tests (pure function, mock DB)Algorithm
CATEGORY_WEIGHTS:
metro_stop= 6.0 (highest)школа5.0,детский_сад4.5,вуз3.0продукты3.5,торговый_центр4.0медицина4.0,поликлиника4.5транспорт4.5,парк3.5,спорт2.5,развлечения2.0Top-7 POI sorted DESC, в радиусе 2km (default).
Response
Tests
12/12 pass. Pure function (no DB), ratio test для weight formula, edge cases (empty list, distance=0).
Possible conflict
Этот PR + B5 #332 оба меняют
parcels.py— добавляют импорты и endpoints в разных секциях. После merge одного второй может потребовать rebase.Part of plan vault
code/patterns/SiteFinder_Backend_Migration_Plan_May17.md§B6.Deep Code Review — verdict: APPROVE
Files reviewed: 3 (P1:
services/site_finder/poi_score.py,api/v1/parcels.py; P3:tests/test_poi_score.py)Lines: +378 / -0
Scoring math — correct
weight = (1/(distance_m + 100)) * category_weightis strictly positive, monotone decreasing in distance, monotone increasing in category weight. AllCATEGORY_WEIGHTSare > 0 (asserted in tests).top_n*10=70nearest by distance ASC, then re-ranks by weight DESC and slices. Candidate widening is sufficient — a default-category POI close enough to beat a high-weight POI at the radius edge would already be in the 70-candidate window.test_metro_beats_school_at_equal_distance. Ratio test covers same-category distance ordering.Spatial SQL — correct
osm_poi_ekbhas GIST indexosm_poi_ekb_geom_gistongeom (Point, 4326)(data/sql/82_osm_poi_ekb.sql).ST_SetSRID(ST_MakePoint(:lon, :lat), 4326)— correct lon/lat order, matching POI geom SRID. Both sides cast to::geography→ST_Distance/ST_DWithinreturn/operate in metres.:lat,:lon,:radius_m,:limit,:c); no f-strings, no SQL injection vector.CAST(... AS double precision)used per project rule (backend.mdCAST trap).::geographycasts on non-parameter expressions are literal Postgres casts and not subject to the v3 placeholder bug.cad_quarters_geom(cad_number) ∪cad_buildings(cad_num) ∪cad_parcels_geom(cad_num) — column names match each schema. 404 if all three miss.Conventions
logger.debug(noprint),text()+ named params, Pydantic v2 BaseModel,from __future__ import annotations, type hints on public API. Pre-commit clean.Minor (non-blocking, future PR)
CATEGORY_WEIGHTS(scale 1.0–6.0, English category keys) deliberately diverges from_SYSTEM_POI_WEIGHTSinweight_profiles.py(scale 0.x–1.5, same keys). Two parallel weighting tables — intentional per PR body ("2GIS-style ranking"), but worth a vault note indecisions/to avoid future confusion.geom::geographycast: planner usually still hits GIST, but if you ever see a Seq Scan in EXPLAIN consider an expression index on(geom::geography). Volumes are low (<2km radius in EKB), so not a concern now.школа,детский_сад) that don't match the actual code keys (school,kindergarten). Code is correct, description sloppy.Merging.