feat(mera): слой ДТП (dtp-stat/ГИБДД) в PostGIS + радиусные агрегаты #3428

Merged
lekss361 merged 2 commits from feat/mera-dtp-layer into main 2026-09-08 22:10:57 +00:00
Owner

Что и зачем

Первая локационная фича вне OSM. Открытая выгрузка по Свердловской области — GeoJSON, 5 МБ zip / ~71 МБ распакованных, 31 253 ДТП за 2015-01…2026-01, из них ~15 тыс. с адресом в Екатеринбурге. Потоковый парс через ijson, файл целиком в память не читается.

Атрибуты богаче ожидаемого: тяжесть, категория, погибшие/раненые, освещение, погода, объекты УДС рядом.

Агрегаты в радиусе — bbox-префильтр + ST_DWithin(geom::geography, …), с функциональным GIST по (geom::geography): обычный GIST по geometry планировщик под такой предикат не берёт (замеры миграций 270 и 134 — 180 мс → 38 мс). Пустая таблица → status="unavailable", а не 500 и не сфабрикованные нули.

Персональные данные

В vehicles[].participants[] лежат пол, роль и нарушения физлиц. Не сохраняем — ни колонкой, ни в raw-jsonb. Зафиксировано в миграции и докстринге, покрыто тестом на реальной записи.

Отдельно: моя первая проверка «утечки» дала 74 совпадения, но все они оказались из названия категории ГИБДД «Наезд на лицо, не являющееся участником дорожного движения» — ложная тревога, фильтр корректен.

Найденное на реальных данных

Битые координаты источника. Из 31 253 записей 86 лежат вне региона, в том числе 10 ровно в (0,0) и такие значения, как (1.0, 1.0) и (41.0, 47.0). Слой кормит радиусные агрегаты локации, пускать туда мусор нельзя — режем на парсе.

Санити-рамка намеренно шире официальных границ области (55.0–63.5 N, 56.0–67.5 E), чтобы не срезать легитимный север (~63°). После фильтра остаётся 31 167 записей.

Границы

dtp_index пока не подключён ни к одному эндпоинту — потребитель отдельным PR. estimator.py не тронут.

Дамп источника заморожен на Last-Modified 2026-02-26 — зафиксировано в докстринге, чтобы пустая дельта не читалась как баг загрузчика.

Проверка

ruff check — чисто; полный сьют pytest5 647 passed, 37 skipped. Парсер прогнан на реальном 71 МБ GeoJSON.

Миграции 294 (таблица + индексы) и 295 (сид расписания, enabled=false, interval_days 30). Новая зависимость: ijson.

## Что и зачем Первая локационная фича вне OSM. Открытая выгрузка по Свердловской области — GeoJSON, 5 МБ zip / ~71 МБ распакованных, **31 253 ДТП за 2015-01…2026-01**, из них ~15 тыс. с адресом в Екатеринбурге. Потоковый парс через `ijson`, файл целиком в память не читается. Атрибуты богаче ожидаемого: тяжесть, категория, погибшие/раненые, освещение, погода, объекты УДС рядом. Агрегаты в радиусе — bbox-префильтр + `ST_DWithin(geom::geography, …)`, с **функциональным GIST по `(geom::geography)`**: обычный GIST по geometry планировщик под такой предикат не берёт (замеры миграций 270 и 134 — 180 мс → 38 мс). Пустая таблица → `status="unavailable"`, а не 500 и не сфабрикованные нули. ## Персональные данные В `vehicles[].participants[]` лежат **пол, роль и нарушения физлиц**. Не сохраняем — ни колонкой, ни в `raw`-jsonb. Зафиксировано в миграции и докстринге, покрыто тестом на реальной записи. Отдельно: моя первая проверка «утечки» дала 74 совпадения, но все они оказались из названия категории ГИБДД «Наезд на **лицо, не являющееся участником** дорожного движения» — ложная тревога, фильтр корректен. ## Найденное на реальных данных **Битые координаты источника.** Из 31 253 записей **86 лежат вне региона**, в том числе 10 ровно в `(0,0)` и такие значения, как `(1.0, 1.0)` и `(41.0, 47.0)`. Слой кормит радиусные агрегаты локации, пускать туда мусор нельзя — режем на парсе. Санити-рамка намеренно шире официальных границ области (55.0–63.5 N, 56.0–67.5 E), чтобы не срезать легитимный север (~63°). После фильтра остаётся 31 167 записей. ## Границы `dtp_index` пока **не подключён ни к одному эндпоинту** — потребитель отдельным PR. `estimator.py` не тронут. Дамп источника заморожен на `Last-Modified 2026-02-26` — зафиксировано в докстринге, чтобы пустая дельта не читалась как баг загрузчика. ## Проверка `ruff check` — чисто; полный сьют `pytest` — **5 647 passed, 37 skipped**. Парсер прогнан на реальном 71 МБ GeoJSON. Миграции **294** (таблица + индексы) и **295** (сид расписания, `enabled=false`, `interval_days` 30). Новая зависимость: `ijson`.
lekss361 added 1 commit 2026-09-08 21:40:21 +00:00
feat(mera): слой ДТП (dtp-stat/ГИБДД) в PostGIS + радиусные агрегаты
All checks were successful
CI Trade-In / changes (pull_request) Successful in 7s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / changes (pull_request) Successful in 10s
CI / backend-tests (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m17s
3a73c9582c
Первая локационная фича вне OSM. Открытая выгрузка по Свердловской области —
GeoJSON, 5 МБ zip / ~71 МБ распакованных, 31 253 ДТП за 2015-01..2026-01,
из них ~15 тыс. с адресом в Екатеринбурге. Потоковый парс через ijson,
файл целиком в память не читается.

Атрибуты: тяжесть, категория, погибшие/раненые, освещение, погода, объекты
УДС рядом. Агрегаты в радиусе — bbox-префильтр + ST_DWithin(geom::geography),
с функциональным GIST по (geom::geography): обычный GIST по geometry
планировщик под такой предикат не берёт (замеры мигр. 270 и 134).

ПДн НЕ СОХРАНЯЕМ: в vehicles[].participants[] лежат пол, роль и нарушения
физлиц. Ни колонкой, ни в raw-jsonb — зафиксировано в миграции, докстринге
и покрыто тестом.

Битые координаты источника режутся на парсе: из 31 253 записей 86 лежат вне
региона, в том числе 10 ровно в (0,0) и такие значения, как (1.0, 1.0) и
(41.0, 47.0). Слой кормит агрегаты локации, пускать туда мусор нельзя.
Санити-рамка намеренно шире официальных границ области (55.0-63.5 N,
56.0-67.5 E), чтобы не срезать легитимный север (~63°).

dtp_index пока не подключён к API — потребитель отдельным PR.

Дамп источника заморожен на Last-Modified 2026-02-26: зафиксировано в
докстринге, чтобы пустая дельта не читалась как баг загрузчика.

Миграции 294 (таблица + индексы) и 295 (сид расписания, enabled=false,
interval_days 30). Новая зависимость: ijson.
bot-backend added 1 commit 2026-09-08 22:04:59 +00:00
merge main в feat/mera-dtp-layer
All checks were successful
CI Trade-In / changes (pull_request) Successful in 8s
CI Trade-In / browser-tests (pull_request) Has been skipped
CI Trade-In / frontend-checks (pull_request) Has been skipped
CI / frontend-tests (pull_request) Has been skipped
CI / openapi-codegen-check (pull_request) Has been skipped
CI / changes (pull_request) Successful in 11s
CI / backend-tests (pull_request) Has been skipped
CI Trade-In / backend-tests (pull_request) Successful in 5m7s
aa02b68219
# Conflicts:
#	tradein-mvp/backend/pyproject.toml
lekss361 merged commit 50f0674977 into main 2026-09-08 22:10:57 +00:00
lekss361 deleted branch feat/mera-dtp-layer 2026-09-08 22:10:57 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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#3428
No description provided.