fix(cadastre): filter Point/LineString geometry in upsert_parcel/zouit (worker crash root cause) #184
2 changed files with 108 additions and 2 deletions
|
|
@ -406,7 +406,21 @@ def upsert_parcel(db: Session, feature: NSPDBulkFeature, source: str = "search")
|
|||
return
|
||||
|
||||
raw_opts = feature.properties.get("options") or {}
|
||||
geom_json = json.dumps(feature.geometry) if feature.geometry else None
|
||||
# cad_parcels.geom — geometry(Polygon, 4326). NSPD изредка возвращает Point
|
||||
# для участков без detailed boundary — INSERT fail'нит на schema check.
|
||||
# Сохраняем raw_props (есть координаты), geom=NULL. Не блокирует Phase 1.
|
||||
geom = feature.geometry or {}
|
||||
geom_type = geom.get("type") if isinstance(geom, dict) else None
|
||||
if geom_type in ("Polygon", "MultiPolygon"):
|
||||
geom_json: str | None = json.dumps(feature.geometry)
|
||||
else:
|
||||
if geom_type:
|
||||
logger.info(
|
||||
"upsert_parcel: cad_num=%s имеет geometry type=%s (не Polygon) — geom=NULL",
|
||||
cad_num,
|
||||
geom_type,
|
||||
)
|
||||
geom_json = None
|
||||
quarter_cad = opts.quarter_cad_number or _derive_quarter_cad(cad_num)
|
||||
|
||||
db.execute(
|
||||
|
|
@ -880,7 +894,20 @@ def upsert_zouit(db: Session, feature: NSPDBulkFeature, source: str = "search")
|
|||
)
|
||||
return
|
||||
|
||||
geom_json = json.dumps(feature.geometry) if feature.geometry else None
|
||||
# cad_zouit.geom — geometry(MultiPolygon, 4326). Polygon допустим (ST_Multi
|
||||
# обернёт в SQL), но Point/LineString → INSERT fail. Фильтруем как в upsert_parcel.
|
||||
geom = feature.geometry or {}
|
||||
geom_type = geom.get("type") if isinstance(geom, dict) else None
|
||||
if geom_type in ("Polygon", "MultiPolygon"):
|
||||
geom_json: str | None = json.dumps(feature.geometry)
|
||||
else:
|
||||
if geom_type:
|
||||
logger.info(
|
||||
"upsert_zouit: reg=%s имеет geometry type=%s (не Polygon) — geom=NULL",
|
||||
reg_numb_border,
|
||||
geom_type,
|
||||
)
|
||||
geom_json = None
|
||||
|
||||
db.execute(
|
||||
text("""
|
||||
|
|
|
|||
|
|
@ -923,6 +923,85 @@ def test_upsert_quarter_geom_sql_uses_cast_not_double_colon() -> None:
|
|||
assert "CAST(:geom AS text)" in sql
|
||||
|
||||
|
||||
def test_upsert_parcel_skips_geom_for_point_geometry() -> None:
|
||||
"""NSPD изредка возвращает Point для ЗУ — cad_parcels.geom strict Polygon,
|
||||
INSERT fail'нул бы. Фильтр на Python level → geom=NULL, raw_props сохранён.
|
||||
"""
|
||||
from app.services.cadastre.bulk_harvest import upsert_parcel
|
||||
|
||||
point_parcel = NSPDBulkFeature.model_validate(
|
||||
{
|
||||
"id": 99,
|
||||
"type": "Feature",
|
||||
"geometry": {"type": "Point", "coordinates": [6753892.91, 7747664.31]},
|
||||
"properties": {
|
||||
"category": 36368,
|
||||
"options": {
|
||||
"cad_num": "66:41:0104002:18",
|
||||
"quarter_cad_number": "66:41:0104002",
|
||||
"declared_area": 200.0,
|
||||
"readable_address": "г Екатеринбург, ул Тестовая",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
db = MagicMock()
|
||||
upsert_parcel(db, point_parcel, source="search")
|
||||
|
||||
# db.execute должен быть вызван (parcel НЕ skipped, только geom=NULL)
|
||||
db.execute.assert_called_once()
|
||||
call_params = db.execute.call_args[0][1]
|
||||
assert call_params["cad_num"] == "66:41:0104002:18"
|
||||
# geom параметр должен быть None
|
||||
assert call_params["geom"] is None
|
||||
|
||||
|
||||
def test_upsert_parcel_keeps_polygon_geometry() -> None:
|
||||
"""Polygon geometry для ЗУ — обычный happy path."""
|
||||
from app.services.cadastre.bulk_harvest import upsert_parcel
|
||||
|
||||
parcel = _make_parcel_feature() # Polygon
|
||||
db = MagicMock()
|
||||
upsert_parcel(db, parcel, source="search")
|
||||
|
||||
db.execute.assert_called_once()
|
||||
call_params = db.execute.call_args[0][1]
|
||||
assert call_params["geom"] is not None
|
||||
assert '"type": "Polygon"' in call_params["geom"]
|
||||
|
||||
|
||||
def test_upsert_zouit_skips_geom_for_linestring() -> None:
|
||||
"""ЗОУИТ schema MultiPolygon — LineString не валиден, geom=NULL."""
|
||||
from app.services.cadastre.bulk_harvest import upsert_zouit
|
||||
|
||||
line_zouit = NSPDBulkFeature.model_validate(
|
||||
{
|
||||
"id": 1,
|
||||
"type": "Feature",
|
||||
"geometry": {
|
||||
"type": "LineString",
|
||||
"coordinates": [[6090000, 7590000], [6090100, 7590100]],
|
||||
},
|
||||
"properties": {
|
||||
"category": 36940,
|
||||
"options": {
|
||||
"reg_numb_border": "66-0.1-2.1-99",
|
||||
"type_zone": "Linear",
|
||||
"registration_date": "2020-01-01",
|
||||
},
|
||||
},
|
||||
}
|
||||
)
|
||||
|
||||
db = MagicMock()
|
||||
upsert_zouit(db, line_zouit, source="search")
|
||||
|
||||
db.execute.assert_called_once()
|
||||
call_params = db.execute.call_args[0][1]
|
||||
assert call_params["geom"] is None
|
||||
|
||||
|
||||
def test_upsert_quarter_geom_custom_width_bounds() -> None:
|
||||
"""min_width_m и max_width_m пробрасываются как параметры SQL (не hardcoded)."""
|
||||
db = _mock_db_execute_scalar(None)
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue