fix(sf): ST_MakeValid genplan geom + backfill 881 invalid polygons (#1157)
Some checks failed
Deploy / build-frontend (push) Blocked by required conditions
Deploy / deploy (push) Blocked by required conditions
Deploy / changes (push) Successful in 5s
Deploy / build-worker (push) Has been cancelled
Deploy / build-backend (push) Has been cancelled

Co-authored-by: lekss361 <lekss361@gendsgn.local>
Co-committed-by: lekss361 <lekss361@gendsgn.local>
This commit is contained in:
lekss361 2026-06-07 17:59:04 +00:00 committed by bot-reviewer
parent 515ac89eaa
commit 3921180d07
3 changed files with 29 additions and 3 deletions

View file

@ -7,7 +7,8 @@
SessionLocal() + try/finally close; logger (не print).
SAVEPOINT per-row (``with db.begin_nested():``) одна битая фича не валит батч.
CAST(:x AS type) НИКОГДА :x::type (psycopg v3).
Геометрия: ST_SetSRID(ST_GeomFromGeoJSON, 4326) без transform (уже 4326).
Геометрия: ST_MakeValid(ST_SetSRID(ST_GeomFromGeoJSON, 4326)) без transform (уже 4326);
ST_MakeValid исправляет self-intersecting/ring-order дефекты геопортала (#1156).
ON CONFLICT (globalid) DO UPDATE: обновляет все поля кроме id/fetched_at (latest wins).
commit один раз в конце.
@ -31,7 +32,9 @@ from app.workers.celery_app import celery_app
logger = logging.getLogger(__name__)
# UPSERT одной фичи. geom = ST_SetSRID(ST_GeomFromGeoJSON, 4326) — данные уже в 4326.
# UPSERT одной фичи. geom = ST_MakeValid(ST_SetSRID(ST_GeomFromGeoJSON, 4326)) —
# ST_MakeValid оборачивает результат чтобы исправить self-intersecting/ring-order
# дефекты из источника геопортала (881/6436 полигонов имели ST_IsValid=false, #1156).
# ON CONFLICT обновляет все аналитические поля; fetched_at = excluded (latest wins).
# CAST(:x AS type) — psycopg v3 (НИКОГДА :x::type).
_UPSERT_SQL = text(
@ -46,7 +49,7 @@ _UPSERT_SQL = text(
CAST(:status AS text),
CAST(:area_ha AS numeric),
CAST(:doc_ref AS text),
ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geojson AS text)), 4326),
ST_MakeValid(ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geojson AS text)), 4326)),
CAST(:raw_props AS jsonb),
CAST(:fetched_at AS timestamptz)
)

View file

@ -199,3 +199,15 @@ def test_sync_empty_wfs_response(monkeypatch: Any) -> None:
assert result == {"zones": 0}
assert db.committed is True
assert db.closed is True
def test_upsert_sql_wraps_geom_with_st_makevalid() -> None:
"""_UPSERT_SQL содержит ST_MakeValid вокруг ST_SetSRID (#1156).
Лёгкая проверка без PostGIS: строковое представление compiled SQL
должно включать ST_MakeValid гарантирует что backfill-логика
присутствует в тексте запроса.
"""
sql_text = str(genplan_zones_sync._UPSERT_SQL)
assert "ST_MakeValid" in sql_text
assert "ST_SetSRID" in sql_text

View file

@ -0,0 +1,11 @@
-- 143_genplan_makevalid_backfill.sql
-- Одноразовый backfill: исправить невалидные геометрии генплан-зон (#1156).
-- 881/6436 полигонов имели ST_IsValid=false (self-intersecting rings из геопортала).
-- Идемпотентно: повторный прогон no-op (WHERE NOT ST_IsValid).
BEGIN;
UPDATE ekb_genplan_functional_zone
SET geom = ST_MakeValid(geom)
WHERE NOT ST_IsValid(geom);
COMMIT;