feat(cadastre): NSPDBulkClient + schemas (#168 PR2/5) #170
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#170
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/cadastre-bulk-client"
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
NSPDBulkClient(async, httpx) — wraps NSPD's bulk-ish endpoints with rate limiting + WAF handlingsearch_by_quarter,wms_feature_info,list_objects_in_buildingextra='allow'→ preserves unknown fields downstreamFiles
backend/app/schemas/nspd_bulk.pyNSPDBulkFeature,QuarterSnapshot,ObjectsListing,NSPDOptionsbackend/app/scrapers/nspd_bulk_client.pyNSPDBulkClientasync context manager + 3 methods + retry/backoffbackend/tests/scrapers/test_nspd_bulk_client.pybackend/tests/scrapers/__init__.pyEndpoints wrapped (verified in HAR audit 2026-05-15)
search_by_quarter(quarter)→QuarterSnapshotReturns
QuarterSnapshot.overflow_categoriesдля категорий гдеtotalCount > 20(нужен grid-walk в PR3).wms_feature_info(layer_id, bbox_3857, click_xy, w=512, h=512)→list[NSPDBulkFeature]list_objects_in_building(objdoc_id, registers_id=36441)→ObjectsListing(Q3 deferred)Не используется в bulk MVP (Q3 помещений deferred). Метод готов для later.
Rate limiting + WAF
asyncio.Semaphore(3)+asyncio.sleep(0.05)jitter → ≤3 req/s sustainedNspdBulkRateLimitErrorNspdBulkWafErrorraised immediately (no retry — нужен manual investigation)QuarterSnapshot(graceful)Headers (точно из HAR)
Test plan
cd backend && uv run pytest tests/scrapers/test_nspd_bulk_client.py -v— 15 unit tests passuv run pytest tests/scrapers/test_nspd_bulk_client.py -m slow -s -vtest_search_by_quarter_real_ekb—66:41:0303161returns 117+ features, meta has 36368:170, 36369:256test_wms_feature_info_real— real bbox click returns 1+ featurestest_list_objects_in_building_real— building objdocId=42065602 returns >150 cad_numstest_rate_limit_semaphore— 5 concurrent ≤ 3 in flightWhat's NOT in this PR
bulk_harvest_quarterCelery task с upsert logic)parcels/analyzeflow (PR 7+ после verification)Закрывает 2/5 of #168.
Final review (SHA
70c079d, CI ✅)Cross-checked агентом — self-contained PR без DB/frontend coupling, безопасно мержить ahead of #169.
Verified by agent
✅ Targets main directly — не stacked на #169. Diff = только 4 Python файла (schemas + client + tests + init). Никакого SQL, никаких runtime dependencies на блокированный #169.
✅ Pydantic schemas align с #169 columns:
cad_num,quarter_cad_number,area,cost_value,cost_index,purpose,floors,year_built,readable_address,ownership_type,right_type,status,objdoc_id,registers_id+extra='allow'дляraw_props JSONB. Нет PR #147-style field drift.✅ Async httpx client design:
asyncio.Semaphore(3)+ 50ms jitter rate-limitNspdBulkRateLimitErrorNspdBulkWafErrorimmediately (no retry, manual investigation)httpx.Timeout(30, connect=10)✅ No DB writes / SAVEPOINT /
:param::cast— все DB write logic deferred в PR3. Этот PR — pure client library.✅ Tests — 15 unit (mocked httpx.get): schema parse, overflow_categories, 404→empty, 403→WAF, 429→backoff exhaustion, semaphore max-concurrent ≤3, WMS parse. 4 e2e marked slow+skipped в CI. Coverage decent.
✅ Backend + frontend CI green.
Минор (не блокер, follow-up note)
_SEMAPHOREshared across instances в одном process, но multiple Celery worker processes → effective concurrency = 3 × workers. Acceptable, но note для PR3 когда integration в celery task._SEMAPHORE._value— fragile но isolated.flats_countredundant сlen(flats_cad_nums).Note about sequencing
PR3 (integration с
bulk_harvest_quarterCelery task) потребует исправленного #169 schema. То есть #169 blockers (DROP cad_parcels_geom + K.3 rename) должны быть зафикшены до PR3 merge. Но #170 — pure library, safe to merge independently.approve merge