feat(tradein): API домов «доли квартир в продаже» — /buildings/sale-share (+ listings, summary) #2055

Merged
lekss361 merged 1 commit from feat/tradein-sale-share-api into main 2026-06-28 14:14:19 +00:00
Owner

Summary

Backend для страницы gendsgn.ru/trade-in/sale-share. 3 endpoint'а поверх готового view v_building_sale_share (миграция 143, уже в main) — логику доли не пересчитываю, читаю как есть.

  • GET /api/v1/buildings/sale-share — список вторичных домов с sale_share_pct >= min_pct. Фильтры: min_pct(=5)/max_pct/city(ILIKE)/price_min/price_max(median)/year_min/year_max/house_type/sort/limit(1..1000). Флаг over_100 для неправдоподобных матчей (показываю, не прячу).
  • GET /api/v1/buildings/{house_id}/listings — активная вторичка дома (для клик-дровера со ссылками на источник).
  • GET /api/v1/buildings/sale-share/summary — total / with-denominator / coverage_pct / max / p95 + гистограмма по 7 корзинам — для ползунка % и баннера покрытия.

Новые файлы: api/v1/buildings.py, services/buildings_query.py, schemas/buildings.py, +main.py (router), +tests/test_buildings_api.py (17 тестов).

Качество

  • Параметризация text() + bind, везде CAST(:x AS type) (psycopg v3); sort — двойной whitelist (router pattern + service map с fallback), user-ввод в SQL не попадает.
  • median_* через CAST(round(...) AS bigint)percentile_cont отдаёт double, иначе Pydantic int падает на дробном.
  • code-reviewer: APPROVE (ruff clean, 17 pytest green, SQL-injection clean, Pydantic корректны).

Test plan

  • 17 unit/contract-тестов зелёные; единственный соседний фейл test_search_cache_hit — pre-existing (401 RBAC, идентично на чистом main).
  • Prod read-only: все 3 запроса выполняются; до загрузки ГАР coverage=0% / list пустой — ожидаемо.
  • End-to-end % через ROLLBACK-tx на prod: house 10636 (68 активных вторичек) + gar_flat_count=200 → sale_share_pct=34.0%, после ROLLBACK ничего не записано.
  • Post-deploy smoke внутри контейнера: docker exec tradein-backend curl -s localhost:8000/api/v1/buildings/sale-share/summary -H "X-Authenticated-User: admin".

Часть серии: PR2/4 (API). Знаменатель заполнит ГАР-loader; фронт-страница — следующий PR.

## Summary Backend для страницы `gendsgn.ru/trade-in/sale-share`. 3 endpoint'а поверх готового view `v_building_sale_share` (миграция 143, уже в main) — логику доли **не** пересчитываю, читаю как есть. - **`GET /api/v1/buildings/sale-share`** — список вторичных домов с `sale_share_pct >= min_pct`. Фильтры: `min_pct`(=5)/`max_pct`/`city`(ILIKE)/`price_min`/`price_max`(median)/`year_min`/`year_max`/`house_type`/`sort`/`limit`(1..1000). Флаг `over_100` для неправдоподобных матчей (показываю, не прячу). - **`GET /api/v1/buildings/{house_id}/listings`** — активная вторичка дома (для клик-дровера со ссылками на источник). - **`GET /api/v1/buildings/sale-share/summary`** — total / with-denominator / coverage_pct / max / p95 + гистограмма по 7 корзинам — для ползунка % и баннера покрытия. Новые файлы: `api/v1/buildings.py`, `services/buildings_query.py`, `schemas/buildings.py`, +`main.py` (router), +`tests/test_buildings_api.py` (17 тестов). ## Качество - Параметризация `text()` + bind, везде `CAST(:x AS type)` (psycopg v3); `sort` — двойной whitelist (router pattern + service map с fallback), user-ввод в SQL не попадает. - `median_*` через `CAST(round(...) AS bigint)` — `percentile_cont` отдаёт double, иначе Pydantic int падает на дробном. - code-reviewer: ✅ APPROVE (ruff clean, 17 pytest green, SQL-injection clean, Pydantic корректны). ## Test plan - [x] 17 unit/contract-тестов зелёные; единственный соседний фейл `test_search_cache_hit` — pre-existing (401 RBAC, идентично на чистом main). - [x] Prod read-only: все 3 запроса выполняются; до загрузки ГАР coverage=0% / list пустой — ожидаемо. - [x] End-to-end % через ROLLBACK-tx на prod: house 10636 (68 активных вторичек) + gar_flat_count=200 → `sale_share_pct=34.0%`, после ROLLBACK ничего не записано. - [ ] Post-deploy smoke внутри контейнера: `docker exec tradein-backend curl -s localhost:8000/api/v1/buildings/sale-share/summary -H "X-Authenticated-User: admin"`. Часть серии: PR2/4 (API). Знаменатель заполнит ГАР-loader; фронт-страница — следующий PR.
lekss361 added 1 commit 2026-06-28 14:13:37 +00:00
feat(tradein): API домов «доли квартир в продаже» — /buildings/sale-share (+ listings, summary)
All checks were successful
CI / changes (pull_request) Successful in 8s
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
0fe23833ca
lekss361 merged commit f3e10a04ad into main 2026-06-28 14:14:19 +00:00
lekss361 deleted branch feat/tradein-sale-share-api 2026-06-28 14:14:19 +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#2055
No description provided.