fix(tradein-phase-c): house_type 'monolith'→'monolithic' + log 400 body #537
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#537
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-phase-c-house-type-mapping"
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?
Problem
Live test после merge PR #534 показал что Avito IMV API отвечает HTTP 400 (silent empty body) для
house_type='monolith'иhouse_type='monolith_brick'. Нашpick_lot_paramsберётmode()raw изlistings.house_type— для большинства домов ЕКБ это именно'monolith'. Все 5 тестовых вызовов fail'нулись с одинаковой ошибкой.Verified live (через docker exec)
panelbrickmonolithicblockmonolithmonolith_brickAvito переименовал
monolith→monolithic, убрал гибридmonolith_brick.Fix
scripts/backfill-house-imv.py:_HOUSE_TYPE_TO_IMVdict для нормализации raw → API-valid value_map_house_type()helper сpaneldefaultpick_lot_paramsиспользует helper вместо raw fallbackapp/services/scrapers/avito_imv.py:_raise_for_status_categorized()логируетresp.text[:200]передraise_for_status()на >=400 status. Будущие contract changes Avito (PR #524 был один пример, этот — второй) теперь будут видны в логах сразу.Roadmap
Follow-up к PR #534 (Phase C). После merge можно повторить script на failed houses (
--only-status error) — все monolith dom'а должны зайти.Verify after deploy
Deep Code Review — verdict
Summary
Verified
scripts/backfill-house-imv.py:_HOUSE_TYPE_TO_IMVmap matches PR body's verified table (panel/brick/monolithic/block/wood OK at API;monolithandmonolith_brickget rerouted tomonolithic)._map_house_type()correctness:None/blank/unknown →paneldefault (matches prioror 'panel'fallback; no regression)._raise_for_status_categorized(): warning log fires only onstatus >= 400AFTER 401/403/5xx are siphoned off — so it covers the 400-class blind spot exactly as advertised.IMVAddressNotFoundErrorpath doesn't go through HTTP 4xx so log won't be noisy on "address not found"._imv_evaluate) will surface immediately. Net positive.pick_lot_paramsquery unchanged (already usesCAST(... AS integer)from PR #534).High (non-blocking, mandatory follow-up)
backend/app/services/estimator.py:111-119— live estimator path is still broken for the SAME root causeLive
/api/v1/trade-in/estimateflow:HouseTypeliteral (tradein-mvp/frontend/src/types/trade-in.ts:3-9) sends"monolith"or"monolith_brick".TradeInEstimateInput.house_type(backend/app/schemas/trade_in.py:22) accepts only those legacy values —"monolithic"is rejected by frontend type and not in backend Literal.estimator.py:755runs_IMV_HOUSE_TYPE_MAP.get(target_house_type)and feeds result toevaluate_via_imv(house_type=...)→ POSThouseType: "monolith"to Avito → 400, same as your verified table._get_or_fetch_imv_cachedcatches and returnsNonegracefully — so users don't see an error, but IMV signal is silently dropped for every monolith building ("для большинства домов ЕКБ это именноmonolith" — your PR body).Consequence: backfill script will now successfully populate
avito_imv_evaluationswithhouse_type='monolithic', but live estimator cache lookups will miss becausecompute_imv_cache_key(avito_imv.py:145-160) hashes the literalhouse_typestring. Script writes rows keyed onmonolithic, live computes cache_key withmonolith→ no hit → fresh fetch → 400 → no IMV in user-facing estimate. The backfill cache effectively never serves the live path for monolith houses.Follow-up PR scope (separate from this one — keep this PR's scope intact):
_IMV_HOUSE_TYPE_MAPso the OUTPUT is the new API value:backend/tests/test_estimator_imv_integration.py:22— asserts current broken mapping_IMV_HOUSE_TYPE_MAP["monolithic"] == "monolith", must become== "monolithic".backend/app/services/scrapers/avito_imv.py:269(# panel/brick/monolith/monolith_brick/block/wood— same outdated list).DELETE FROM avito_imv_evaluations WHERE house_type IN ('monolith', 'monolith_brick')— they were written via failed mapping anyway, so likely already-empty or non-existent.Medium
backfill-house-imv.py:71—_HOUSE_TYPE_TO_IMVcoverspanel/brick/monolith/monolithic/monolith_brick/block/woodbut Pydantic schema also allows"other"(schemas/trade_in.py:22). With this PR"other"→ defaultpanel, same as oldor "panel"fallback — functionally identical, but worth a# 'other' falls to panel defaultcomment so reader knows it's deliberate, not a miss.backfill-house-imv.py:62—monolith_brick→monolithic: live verification only tested puremonolithiccells. Hybrid panels in EKB are usually closer to brick by structural class than to pure monolithic. The PR table doesn't claim IMV returns identical pricing for both, only that 400 stops. Worth a follow-up note: "verify recommended_price for monolith_brick houses matches comparable Yandex valuation within ±15% — currently we silently round them down tomonolithic."Low
avito_imv.py:336—body_preview = (resp.text or "")[:200]: curl_cffiresp.textis a property that may be expensive to compute (decodes body each access). Cheap here since it's already buffered post-post(), but if future migration to streaming HTTP comes, this fires before resp body is fully streamed. Non-issue today.avito_imv.py:269— docstring# panel/brick/monolith/monolith_brick/block/woodis now misleading (left intentionally out of scope). Cleanup in follow-up.Vault cross-check
house_typein INSERT tohouse_imv_evaluations. The value written is the API-mapped one (monolithic), not the raw listing value. That's intentional (cache_key consistency) and matches PR #534 behavior.evaluate_via_imvdoes sendhouseTypedirectly to Avito (no further mapping in scraper). Script fix takes effect end-to-end.Test coverage
_map_house_type()added. Trivial pure function, low risk, acceptable.test_estimator_imv_integration.py:22will keep passing — but it codifies the broken estimator mapping. Follow-up PR must flip both code AND test together.Complexity / blast radius
None).Recommended next steps
--only-status errorhouses).estimator._IMV_HOUSE_TYPE_MAPwith new API + flip test assertion + stale docstring onavito_imv.py:269— before running the rerun command in your verify-after-deploy block (cache_key drift means rerunning script populates rows the live path won't see).qa-testersmoke on a monolith building (Belinskogo 86 or similar) — expect IMV signal in response payload (currently always missing).