feat(sf-b7): GET /parcels/{cad}/snapshot.pdf — 1-page WeasyPrint export #334
No reviewers
Labels
No labels
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
Fable 5 ревью
feedback/max
generative
GG-форсайт
needs-discussion
needs-human
observability
pause-bots
performance
priority/p0
priority/p1
priority/p2
priority/p3
scope/backend
scope/db
scope/devops
scope/frontend
scope/qa
scrapers
security
site-finder
stage/1
stage/2
status/blocked
status/done
status/needs-analysis
status/needs-fix
status/qa
status/ready
status/review
status/wip
tech-debt
tradein
ux
week ревью 1
wontfix
вторичка
ИРД
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#334
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/sf-b7-snapshot-pdf"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
SF Wave 1 / Group B / sub-task B7. NEW snapshot PDF export.
Files
backend/app/services/exporters/snapshot_pdf.py— NEW WeasyPrint + Jinja2 generatorbackend/app/templates/parcel_snapshot.html— NEW A4 templatebackend/app/api/v1/parcels.py— NEW endpointGET /{cad_num}/snapshot.pdfbackend/pyproject.toml+uv.lock— добавленjinja2>=3.1.0Content (1-page A4)
Dependencies
/usr/share/fonts/truetype/dejavu/)Possible conflict
Этот PR + B5 #332 + B6 #333 — все меняют
parcels.py. Sequential merge через rebase.Test plan
GET /api/v1/parcels/66:41:0204016:10/snapshot.pdf→ valid PDFPart of plan vault
code/patterns/SiteFinder_Backend_Migration_Plan_May17.md§B7.Add 1-page A4 snapshot PDF endpoint for cadastral parcels (SF task B7). - New service: app/services/exporters/snapshot_pdf.py — WeasyPrint + Jinja2 renderer with DejaVu Sans Cyrillic font support - New template: app/templates/parcel_snapshot.html — styled A4 layout with header, KPI block, POI top-7 table, competitors top-5 table, footer - New endpoint GET /parcels/{cad}/snapshot.pdf in parcels.py — fetches parcel meta (cad_parcels), geometry (cad_parcels_geom), POI (osm_poi_ekb 1km), competitors (domrf_kn_objects 3km), district (ekb_districts), renders PDF and returns application/pdf attachment - Add jinja2>=3.1.0 to pyproject.toml + uv.lock Disclaimer included: not an official EGRN extract.Deep Code Review — verdict: APPROVE
Files: 5 (+646/-0) —
snapshot_pdf.py(NEW),parcel_snapshot.html(NEW),parcels.py,pyproject.toml,uv.lock.Security
Environment(autoescape=select_autoescape(["html"]))+ template name ends in.html→ autoescape triggers. All user-controlled fields (cad_num,address,district,vri,land_category,poi.name,comm_name,dev_name) rendered via{{ }}without|safe. XSS-safe.:bind+CAST(:c AS text)(psycopg v3 convention).print(), nopsycopg2, norequests.POI scoring dependency
_POI_WEIGHTS/_CATEGORY_RUduplicated inline insnapshot_pdf.py(lines 28-54) with explicit comment "exporter не импортирует из api-слоя". No dependency on PR #333 (B6) — this PR is independent. Acceptable; future follow-up: extract toapp/services/site_finder/poi_weights.pyand import from both places after #333 merges.Correctness
_find_font_url()returns""if DejaVu missing →@font-faceCSS becomes invalid → WeasyPrint falls back toArial, sans-seriffrom font stack. Graceful.db.execute()— consistent with other exporters (trade_in_pdf,layout_tz_pdf).Minor nits (non-blocking, follow-up)
parcel_snapshot.htmlheader:Данные НСПД / ЕГРНsource: cad_parcels— missing space (ЕГРН source).—HTML entities in{{ x or '—' }}will be double-escaped by autoescape → user sees literal—instead of em-dash. Replace with—(UTF-8 character) directly:{{ x or "—" }}.area_m2 = 0edge case:area_m2 / 10_000 if area_m2 else "—"treats0as falsy → renders—. Should beif area_m2 is not None(matches thecadastral_cost_rub is not Nonestyle used elsewhere in same call)._POI_WEIGHTSafter PR #333 merges — extract to shared module.Verdict: APPROVE. Merging via squash.