- #1338 concepts.py: geometry.generate вынесен в run_in_threadpool (не блокирует event loop) - #1363 house_imv_backfill.py + scrape_pipeline.py: периодический heartbeat в долгих IMV-фазах (reap_zombies не помечает живой sweep zombie) - #1419 site-finder-api.ts: placeholderData keepPreviousData для bbox-запроса (KPI не мигает «0/0» при пане/зуме) - #1421 site-finder-api.ts: area_ha nullable + адаптер не подставляет 0; null-гейт потребителей EntryMap.tsx, ParcelDrawer.tsx - #1424/#1480 EstimateResult.tsx: блок «Параметры объекта» скрыт при восстановлении по shared-ссылке (нет мусора «0 м²/Студия/Этаж 0 из 0») Верификация: tsc --noEmit 0 ошибок; py_compile OK.
33 lines
1.4 KiB
Python
33 lines
1.4 KiB
Python
import logging
|
||
|
||
from fastapi import APIRouter, HTTPException
|
||
from fastapi.concurrency import run_in_threadpool
|
||
|
||
from app.schemas.concept import ConceptInput, ConceptOutput
|
||
from app.services.generative import geometry
|
||
from app.services.generative.geometry import ParcelGeometryError
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
router = APIRouter()
|
||
|
||
|
||
@router.post("", response_model=ConceptOutput)
|
||
async def create_concept(payload: ConceptInput) -> ConceptOutput:
|
||
"""Generate 3 building variants for the given parcel polygon.
|
||
|
||
Stage 1a: Shapely parse + buildable area (setback) + placement grid.
|
||
Stage 1b: greedy section placement with STRtree collisions (3 strategies).
|
||
Stage 1c: real ТЭП + financial model attached to each variant.
|
||
|
||
A degenerate parcel (setback consumes everything, malformed geometry) yields a
|
||
422 rather than empty variants — that is a bad request, not a valid empty result.
|
||
"""
|
||
try:
|
||
# geometry.generate — синхронный CPU-bound (Shapely/STRtree), мостим через
|
||
# run_in_threadpool, чтобы НЕ блокировать event loop (тот же приём, что и в chat.py).
|
||
variants = await run_in_threadpool(geometry.generate, payload)
|
||
except ParcelGeometryError as exc:
|
||
logger.warning("concept generation rejected parcel: %s", exc)
|
||
raise HTTPException(status_code=422, detail=str(exc)) from exc
|
||
return ConceptOutput(variants=variants)
|