fix(parcels): remove non-existent units_count column from permits query (#105 PR #213 fixup)

🔴 Critical from review-bot: column units_count doesn't exist в
ekburg_construction_permits (mock tests прошли т.к. db.execute мокается;
на проде psycopg.errors.UndefinedColumn для каждого /analyze).

Removes (7 refs):
- SQL SELECT не выбирает units_count
- dict build, PermitsSummary.rve_total_units
- RecentPermit.units_count Pydantic field
- PermitsSummary.rve_total_units field
- 2 fixture rows assertions

🟡 Dedupe _quarter_prefix → reuse existing _quarter_cad (computed
ранее для market_price block).

Tests adjusted, ruff clean.
This commit is contained in:
lekss361 2026-05-16 15:14:46 +03:00
parent 7eb686bd7d
commit 3abaf047a9
3 changed files with 6 additions and 15 deletions

View file

@ -1853,13 +1853,11 @@ def analyze_parcel(
# Spatial filter через geom недоступен до Phase 3 (geocoding → geom).
# Используем quarter prefix (первые 3 сегмента кад. номера) как прокси.
# После Phase 3: заменить на ST_DWithin(..., 500) по geom.
_quarter_prefix = ":".join(_cad_parts[:3]) if len(_cad_parts) >= 3 else cad_num
recent_permits: list[dict[str, Any]] = []
permits_summary: dict[str, Any] = {
"rns_count": 0,
"rve_count": 0,
"rns_total_area_sqm": 0.0,
"rve_total_units": 0,
"by_developer": [],
}
try:
@ -1870,14 +1868,14 @@ def analyze_parcel(
SELECT
permit_type, permit_number, issue_date,
developer_name, developer_inn, object_name, object_type,
construction_address, total_area_sqm, units_count
construction_address, total_area_sqm
FROM ekburg_construction_permits
WHERE LEFT(cadastral_number, LENGTH(CAST(:q AS text))) = CAST(:q AS text)
AND issue_date > NOW() - INTERVAL '24 months'
ORDER BY issue_date DESC
LIMIT 50
"""),
{"q": _quarter_prefix},
{"q": _quarter_cad},
)
.mappings()
.all()
@ -1893,21 +1891,18 @@ def analyze_parcel(
"object_type": r["object_type"],
"construction_address": r["construction_address"],
"total_area_sqm": float(r["total_area_sqm"]) if r["total_area_sqm"] else None,
"units_count": r["units_count"],
}
for r in permits_rows
]
rns_list = [p for p in recent_permits if p["permit_type"] == "RNS"]
rve_list = [p for p in recent_permits if p["permit_type"] == "RVE"]
by_dev: dict[str, int] = {}
for p in recent_permits:
if p["developer_name"]:
by_dev[p["developer_name"]] = by_dev.get(p["developer_name"], 0) + 1
permits_summary = {
"rns_count": len(rns_list),
"rve_count": len(rve_list),
"rve_count": len([p for p in recent_permits if p["permit_type"] == "RVE"]),
"rns_total_area_sqm": sum(p["total_area_sqm"] or 0.0 for p in rns_list),
"rve_total_units": sum(p["units_count"] or 0 for p in rve_list),
"by_developer": [
{"name": name, "permits_count": cnt}
for name, cnt in sorted(by_dev.items(), key=lambda x: -x[1])[:3]

View file

@ -18,7 +18,6 @@ class RecentPermit(BaseModel):
object_type: str | None
construction_address: str | None
total_area_sqm: float | None
units_count: int | None
class PermitsSummary(BaseModel):
@ -27,7 +26,6 @@ class PermitsSummary(BaseModel):
rns_count: int
rve_count: int
rns_total_area_sqm: float
rve_total_units: int
by_developer: list[dict[str, Any]]

View file

@ -183,7 +183,6 @@ def test_recent_permits_found_in_quarter() -> None:
"object_type": "МКД",
"construction_address": "г. Екатеринбург, ул. Ленина, 1",
"total_area_sqm": 45000.0,
"units_count": None,
}
rve_permit: dict[str, Any] = {
"permit_type": "RVE",
@ -195,7 +194,6 @@ def test_recent_permits_found_in_quarter() -> None:
"object_type": "МКД",
"construction_address": "г. Екатеринбург, ул. Ленина, 1",
"total_area_sqm": None,
"units_count": 320,
}
db = _make_db_for_analyze(permits_rows=[rns_permit, rve_permit])
@ -220,14 +218,14 @@ def test_recent_permits_found_in_quarter() -> None:
assert rns["total_area_sqm"] == pytest.approx(45000.0)
rve = next(p for p in permits if p["permit_type"] == "RVE")
assert rve["units_count"] == 320
assert rve["issue_date"] == "2025-03-10"
assert "units_count" not in rve
summary = body["permits_summary"]
assert summary["rns_count"] == 1
assert summary["rve_count"] == 1
assert summary["rns_total_area_sqm"] == pytest.approx(45000.0)
assert summary["rve_total_units"] == 320
assert "rve_total_units" not in summary
assert len(summary["by_developer"]) == 1
assert summary["by_developer"][0]["name"] == "Атомстрой"
assert summary["by_developer"][0]["permits_count"] == 2
@ -257,7 +255,7 @@ def test_recent_permits_empty_quarter() -> None:
assert summary["rns_count"] == 0
assert summary["rve_count"] == 0
assert summary["rns_total_area_sqm"] == pytest.approx(0.0)
assert summary["rve_total_units"] == 0
assert "rve_total_units" not in summary
assert summary["by_developer"] == []
finally:
app.dependency_overrides.clear()