fix(cadastre): auto-heal cad_quarters_geom from CAT_QUARTER_STATS #181
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#181
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/cadastre-quarters-geom-autoheal"
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
Auto-heal broken
cad_quarters_geomrows during regular cadastre harvest. Data-quality follow-up to #179.upsert_quarter_geom_from_featureinbulk_harvest.py: CTE-based UPDATE that takes NSPD CAT_QUARTER_STATS (36381) polygon, transforms 3857 → 4326, writes only when existing geom is broken (width <100m or >10000m) and new geom is valid.upsert_quarter_statswithdb.begin_nested()savepoint — malformed NSPD GeoJSON rolls back only geom UPDATE, preserves successful stats upsert.Why
Job #8 (full ЕКБ) revealed
cad_quarters_geom: 1735/2408 (72%) rows have micro-precision width <0.01m vs expected 200-4000m. PR #179 introduced sanity check skipping grid-walk for broken quarters — but that's masking, not fixing. NSPD returns valid quarter polygon in CAT_QUARTER_STATS feature, we just discard it.After this PR: every harvest_quarter run that returns CAT_QUARTER_STATS feature will heal the row IF current geom is broken. ~1735 broken quarters should be healed at next
ekb_fullrun.SQL safety guards
RETURNING cqg.cad_number+scalar()signals row-touched.ST_Multi()coerces Polygon→MultiPolygon (matchesgeometry(MultiPolygon, 4326)schema).raw_propsNOT updated — original provenance preserved.Test plan
pytest tests/services/test_cadastre_bulk.py→ 35 passed locallycad_quarters_geomhealed rows count via:Closes part of #168, depends on conceptual fix from #179.
Review
CI зелёный (backend 1m17s + frontend 1m33s) + cross-check (agent):
✅ SAVEPOINT pattern textbook-correct
try: with db.begin_nested(): ... except: ...— канонический паттерн SQLAlchemy 2.0. На raise внутри savepoint → auto-rollback savepoint, outer tx остаётся usable, outerexceptловит → последующийdb.commit()сохраняет успешныйupsert_quarter_stats. В отличие от #155 (гдеdb.rollback()положил outer tx), здесь semantics правильные.✅ SQL safety — двойные gates
RETURNING cad_number+scalar()сигналит touched.✅ CAST compliance
Тест
test_upsert_quarter_geom_sql_uses_cast_not_double_colon—CAST(:geom AS text)присутствует,::jsonb/::textотсутствуют. Соответствует #178 паттерну.✅ Schema match
ST_Multi(ST_Transform(..., 4326))— coerce Polygon → MultiPolygon дляgeometry(MultiPolygon, 4326)column. ✓✅ Provenance
source = 'quarter_stats_autoheal'— ops смогут считать сколько healed rows.raw_propsне перезаписывается. Single-row UPDATE by PK — no table lock.✅ Test coverage
7 unit tests: invalid geometry skip, scalar=None→False, scalar=cad_number→True, MultiPolygon coerce, CAST compliance, custom bounds, integration exception path.
Минор:
test_harvest_quarter_geom_heal_failure_does_not_propagateмокаетdb.begin_nestedкак MagicMock — это не exercise real savepoint rollback, только Python-уровневый try/except. Acceptable test-fidelity gap: SAVEPOINT semantics гарантируются самим SQLAlchemy (subject of fetched docs), не нашим кодом.✅ Phase 4 ordering safe
upsert_quarter_statsOUTSIDE savepoint → если fail, raise до открытия savepoint. Geom heal INSIDE → fail rollback'нёт только savepoint. Затемdb.commit()фиксирует stats (или stats+heal если оба ок).✅ No conflict с PR #182
Touch разные секции
bulk_harvest.py— #181 Phase 4 (~L237) + новая функция (~L946); #182 dataclass + Phase 1/1.5. Zero overlap.✅ Drift отсутствует
Server-side only, no TS callers.
approve merge