feat(site-finder): OSRM road-distance in /analyze behind flag (#39 A2) #1932

Merged
bot-backend merged 1 commit from feat/39-osrm-analyze-distances into main 2026-06-26 21:25:51 +00:00
Collaborator

Summary

  • Заменяет straight-line ST_Distance(geom::geography) в POI-скоринге /analyze реальным дорожным расстоянием центроид→POI из локального OSRM, за флагом.
  • OSRM table API (правильный): GET http://osrm:5000/table/v1/driving/{lon0},{lat0};{lon1},{lat1};...?sources=0&annotations=distance{"code":"Ok","distances":[[0, d1, d2, ...]]} (метры). Читаем distances[0][1:] (пропуская self origin→origin = 0). НЕ durations×скорость, НЕ POST-body (это ORS).
  • Флаг use_osrm_distances default OFF → деплой поведенчески-нейтрален. При OFF OSRM не дёргается вообще, /analyze байт-в-байт как сегодня.
  • Score-shift note: включение сдвинет все POI-score на ~20-30% (дорога длиннее прямой) — осознанное позднее решение после валидации, НЕ этот PR.
  • Graceful fallback: любой сбой OSRM (HTTP / timeout / code!=Ok / битая форма / рассинхрон длины) → OsrmLocalUnavailableError → straight-line по всем POI. /analyze не падает в 500. Недостижимый POI (null от OSRM) сохраняет straight-line.
  • Покрыто и для OSM POI (osm_poi_ekb), и для custom POI (user_custom_pois).
  • Нет изменения response-схемы (distance_m уже в score_breakdown, AnalyzeResponse = extra="allow") → codegen НЕ нужен.

Files

  • backend/app/core/config.pyuse_osrm_distances: bool = False, osrm_local_url, osrm_distance_timeout_s.
  • backend/app/services/site_finder/osrm_client_local.py — новый клиент (get_road_distances_m, OsrmLocalUnavailableError).
  • backend/app/api/v1/parcels.py — flag-gated применение road-distance в POI-блоках + helper _apply_osrm_road_distances.
  • backend/tests/test_osrm_client_local.py, backend/tests/api/v1/test_analyze_osrm_distances.py — юнит + интеграция.

Test plan

  • uv run pytest -k "osrm or analyze or poi" -q → 195 passed, 3 skipped, 0 failed
  • FULL suite uv run pytest -q → 3567 passed, 51 skipped, 0 failed
  • uv run ruff check → clean
  • flag ON + mocked OSRM → distance заменён дорожным; flag ON + OSRM raises → fallback straight-line, analyze=200; flag OFF → OSRM не вызван ни разу

Refs #39

## Summary - Заменяет straight-line `ST_Distance(geom::geography)` в POI-скоринге `/analyze` реальным **дорожным** расстоянием центроид→POI из локального OSRM, за флагом. - **OSRM table API (правильный):** `GET http://osrm:5000/table/v1/driving/{lon0},{lat0};{lon1},{lat1};...?sources=0&annotations=distance` → `{"code":"Ok","distances":[[0, d1, d2, ...]]}` (метры). Читаем `distances[0][1:]` (пропуская self origin→origin = 0). НЕ durations×скорость, НЕ POST-body (это ORS). - **Флаг `use_osrm_distances` default OFF → деплой поведенчески-нейтрален.** При OFF OSRM не дёргается вообще, `/analyze` байт-в-байт как сегодня. - **Score-shift note:** включение сдвинет все POI-score на ~20-30% (дорога длиннее прямой) — осознанное **позднее** решение после валидации, НЕ этот PR. - **Graceful fallback:** любой сбой OSRM (HTTP / timeout / `code!=Ok` / битая форма / рассинхрон длины) → `OsrmLocalUnavailableError` → straight-line по всем POI. `/analyze` **не падает в 500**. Недостижимый POI (`null` от OSRM) сохраняет straight-line. - Покрыто и для OSM POI (`osm_poi_ekb`), и для custom POI (`user_custom_pois`). - Нет изменения response-схемы (`distance_m` уже в `score_breakdown`, `AnalyzeResponse` = `extra="allow"`) → codegen НЕ нужен. ### Files - `backend/app/core/config.py` — `use_osrm_distances: bool = False`, `osrm_local_url`, `osrm_distance_timeout_s`. - `backend/app/services/site_finder/osrm_client_local.py` — новый клиент (`get_road_distances_m`, `OsrmLocalUnavailableError`). - `backend/app/api/v1/parcels.py` — flag-gated применение road-distance в POI-блоках + helper `_apply_osrm_road_distances`. - `backend/tests/test_osrm_client_local.py`, `backend/tests/api/v1/test_analyze_osrm_distances.py` — юнит + интеграция. ## Test plan - [x] `uv run pytest -k "osrm or analyze or poi" -q` → 195 passed, 3 skipped, 0 failed - [x] FULL suite `uv run pytest -q` → 3567 passed, 51 skipped, 0 failed - [x] `uv run ruff check` → clean - [x] flag ON + mocked OSRM → distance заменён дорожным; flag ON + OSRM raises → fallback straight-line, analyze=200; flag OFF → OSRM не вызван ни разу Refs #39
bot-backend added 1 commit 2026-06-26 21:06:52 +00:00
feat(site-finder): OSRM road-distance in /analyze behind flag (#39 A2)
All checks were successful
CI / changes (pull_request) Successful in 8s
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Successful in 1m47s
CI / backend-tests (pull_request) Successful in 12m5s
1d367f5d8e
Заменяет straight-line ST_Distance в POI-скоринге /analyze реальным
дорожным расстоянием центроид→POI из локального OSRM (/table GET с
annotations=distance → метры), за флагом use_osrm_distances.

Флаг default OFF → деплой поведенчески-нейтрален: при OFF OSRM не
дёргается вообще, /analyze байт-в-байт как сегодня. Включение сдвигает
все POI-score на ~20-30% (дорога длиннее прямой) — осознанное позднее
решение после валидации, НЕ этот PR.

Graceful fallback: любой сбой OSRM (HTTP / timeout / code!=Ok / битая
форма) → OsrmLocalUnavailableError → straight-line по всем POI, /analyze
НЕ падает в 500. Недостижимый POI (null от OSRM) → сохраняет straight-line.

Покрыто и для OSM POI (osm_poi_ekb), и для custom POI (user_custom_pois).

Refs #39
bot-backend merged commit f48db87d39 into main 2026-06-26 21:25:51 +00:00
bot-backend deleted branch feat/39-osrm-analyze-distances 2026-06-26 21:25:51 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#1932
No description provided.