layout_tz_pdf рендерит supply_only_layouts (#2177 шаг 3): таблица
комплекс/тип/площадь/лотов/медиана ₽/м² + честный caveat, аддитивно после
топа продаж. Graceful на старых снапшотах (пусто → секции нет). HTML-сборка
вынесена в pure build_layout_tz_html — юнит-тесты без native WeasyPrint
(до этого весь файл скипался на macOS). Экранирование скрапленных строк
html.escape — консистентно соседним блокам (ревью-verified).
Примечание: остальные блоки недели (§4.3 offer_trend, «Как участок
сходится», §3 gas) — frontend-only; полного backend-экспортёра /analyze
не существует → issue #2259.
LIVE BUG (from #994, already merged): the analysis_runs persist call-site in
analyze_parcel extracted district via result_payload["district"]["district_name"]
— but a district dict without that key (real data: partial district lookup)
raised KeyError OUTSIDE the best-effort SAVEPOINT (extraction is at the call
site, not inside persist_analysis_run) → 500 on the LIVE /analyze endpoint.
Fix: .get("district_name") → None instead of raising. Caught by reviving the
analyze test suite (below).
Test-infra (the suite was un-runnable, which is why the bug shipped):
- Lazy-import WeasyPrint in layout_tz_pdf.py + trade_in_pdf.py (matches the
existing report_pdf.py / snapshot_pdf.py pattern). The eager top-level imports
made `app.main` (→ parcels/trade_in routers) un-importable on hosts without
WeasyPrint native libs (e.g. macOS dev), breaking pytest COLLECTION of the
whole api/v1 suite. WeasyPrint is still imported when a PDF is actually rendered.
- tests/conftest.py: autouse fixture clears app.dependency_overrides after each
test (anti-leak — a leaked get_db override caused real-DB connection attempts).
- test_parcel_by_bbox.py: rewrite get_db mocking from patch("...get_db") (a no-op
— FastAPI Depends holds the original ref) to app.dependency_overrides[get_db],
add explicit X-Authenticated-User header (RBAC gate), patch latest_run_dates,
+ a new test asserting last_analysis_date from a latest run (#994). 5/5 green.
NOTE: reviving collectability exposes PRE-EXISTING rot in other api/v1 suites
(analyze/admin/best_layouts: RBAC-header + stale-assertion/mock drift) — those
are NOT regressions from this PR (they were uncollectable before) and are
tracked separately for a deliberate suite-rehab + CI test-gate effort.
Refs #994.