fix(parcels): guard NULL geom + None lat/lon в isochrones/poi-score (#1201) #1251

Merged
bot-backend merged 1 commit from fix/isochrones-poi-null-geom-guard into main 2026-06-13 05:24:25 +00:00
Collaborator

Summary

Closes #1201 (P2 prod 500). GET /{cad}/isochrones и GET /{cad}/poi-score отдавали 500 для участков с NULL-геометрией в cad_parcels_geom (VIEW над cad_parcels где geom nullable, ~964 prod-строк задокументировано в analyze:1504-1507).

Root cause

SQL UNION без geom IS NOT NULL фильтра:

  • NULL-строка ранней ветки (cad_quarters_geom) ПЕРЕКРЫВАЕТ валидную из поздней — LIMIT 1 берёт первую попавшуюся.
  • ST_Centroid(NULL)NULLcoord_row["lat"] = Nonefloat(None)TypeError → 500 (вместо честного 404).

Guard if not coord_row не ловил: RowMapping с None-полями truthy.

Fix

Зеркало паттерна из analyze (parcels.py:1418-1438) для обоих эндпоинтов:

  1. AND geom IS NOT NULL во всех 3 ветках UNION.
  2. Belt-and-suspenders: явная проверка coord_row.get("lat") is None — на случай битого polygon'а (NOT-NULL geom, NULL centroid).

Test plan

  • pytest tests/api/v1/ -k "isochrones or poi_score or analyze" → 26 passed.
  • ruff check → All checks passed.
  • Post-deploy: smoke на cad без геометрии — expect 404, не 500.

Closes #1201

## Summary Closes #1201 (P2 prod 500). `GET /{cad}/isochrones` и `GET /{cad}/poi-score` отдавали 500 для участков с NULL-геометрией в `cad_parcels_geom` (VIEW над `cad_parcels` где `geom` nullable, ~964 prod-строк задокументировано в `analyze:1504-1507`). ## Root cause SQL UNION без `geom IS NOT NULL` фильтра: - NULL-строка ранней ветки (`cad_quarters_geom`) ПЕРЕКРЫВАЕТ валидную из поздней — `LIMIT 1` берёт первую попавшуюся. - `ST_Centroid(NULL)` → `NULL` → `coord_row["lat"] = None` → `float(None)` → `TypeError` → 500 (вместо честного 404). Guard `if not coord_row` не ловил: RowMapping с None-полями truthy. ## Fix Зеркало паттерна из `analyze` (parcels.py:1418-1438) для обоих эндпоинтов: 1. `AND geom IS NOT NULL` во всех 3 ветках UNION. 2. Belt-and-suspenders: явная проверка `coord_row.get("lat") is None` — на случай битого polygon'а (NOT-NULL geom, NULL centroid). ## Test plan - [x] `pytest tests/api/v1/ -k "isochrones or poi_score or analyze"` → 26 passed. - [x] `ruff check` → All checks passed. - [ ] Post-deploy: smoke на cad без геометрии — expect 404, не 500. Closes #1201
bot-backend added 1 commit 2026-06-13 05:13:23 +00:00
fix(parcels): guard NULL geom + None lat/lon в isochrones/poi-score (#1201)
All checks were successful
CI / changes (push) Successful in 6s
CI / frontend-tests (push) Has been skipped
CI / changes (pull_request) Successful in 6s
CI / frontend-tests (pull_request) Has been skipped
CI / backend-tests (push) Successful in 6m28s
CI / backend-tests (pull_request) Successful in 6m27s
b6fa4a2773
`cad_parcels_geom` — VIEW над `cad_parcels` где `geom` nullable (~964 prod-
строк с NULL geom задокументировано в analyze:1504-1507). Без фильтра
`geom IS NOT NULL` в UNION-ветках:
- NULL-строка из ранней ветки (`cad_quarters_geom`) ПЕРЕКРЫВАЕТ валидную
  геометрию из поздней (`cad_parcels_geom`) — LIMIT 1 берёт первую попавшуюся.
- ST_Centroid(NULL) → NULL → coord_row.lat = NULL → float(None) → TypeError
  → 500 (вместо честного 404).

Guard `if not coord_row` НЕ ловил эту дыру: RowMapping с None-полями
truthy. Зеркало паттерна из analyze (parcels.py:1418-1438) для обоих
эндпоинтов (`/isochrones` + `/poi-score`):
1. `AND geom IS NOT NULL` во ВСЕХ ветках UNION.
2. Belt-and-suspenders: явная проверка `coord_row.get("lat") is None`
   на случай топологически битого polygon'а (NOT-NULL geom, NULL centroid).

26 analyze/isochrones/poi-score юнит-тестов зелёные. ruff clean.

Closes #1201
bot-backend merged commit 2a68c2e202 into main 2026-06-13 05:24:25 +00:00
bot-backend deleted branch fix/isochrones-poi-null-geom-guard 2026-06-13 05:24:26 +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#1251
No description provided.