feat(tradein): cian_valuation.py — auth-required Calculator scraper + 24h cache #465
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#465
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/tradein-cian-valuation"
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
Stage 7 / Wave 5 of CianScraper v1. Cian.ru authenticated Valuation Calculator scraper — 6-й evaluation source для estimator (Stage 9).
Depends Stage 4 (cookies via
app.services.cian_session.load_session) ✅ merged.Files
app/services/scrapers/cian_valuation.py(NEW) —estimate_via_cian_valuation()+ cache helpers + dataclasstests/test_cian_valuation.py(NEW) — 28 testsPush verify
6d99e6a41aef34554b775e0df26adfc98ceaab176d99e6a41aef34554b775e0df26adfc98ceaab17Match ✅.
State extraction
https://www.cian.ru/kalkulator-nedvizhimosti/?address=...&totalArea=...valuation-for-agent-frontendinitialStateload_session(db)(Stage 4)isAuthenticated=false→mark_session_invalid()+ return None gracefullyReal Cian state shape (verified)
estimation.sale.data.price(NOTvaluation.sale.price)estimationChart.data.chartData.data(unix ms → ISO date)estimationChart.data.title.changeValue("8,3%" → 8.3) —_parse_change_pct()handles comma decimalestimation.sale.data.filtersHashalso saved (dedup helper)chart_change_direction: Cian's nativeincrease/decrease/neutralCache (
external_valuations)24h TTL по
cache_key = sha256(address + totalArea + rooms + floor + repair_type + deal_type).Schema cols verified (migrations 026 + 029):
ON CONFLICT (source, cache_key) DO UPDATE — re-fetch refreshes cache.
Auth flow
load_session(db)→ cookies dict or Nonecurl_cffi(impersonate="chrome120")state.user.isAuthenticatedmark_session_invalid()+ return None (forces re-upload prompt)Tests
28/28 pass. Coverage:
Ruff
Clean (auto-fixed UP017
datetime.UTC+ import sort).Anti-bot
curl_cffi.AsyncSession(impersonate="chrome120")— consistent across cian.py / cian_detail.py / cian_newbuilding.py.Next: Stage 8 (matching) + Stage 9 (estimator integration)
Stage 8 (cross-source matching) и Stage 9 (estimator 6th source) теперь unblocked.
Refs
decisions/CianScraper_v1_Implementation_Plan.mdStage 7decisions/Schema_Cian_SERP_Inventory.mdsec 24 (URL/auth), sec 25 (response shape), sec 26 (cache DDL)load_sessionAPI)external_valuationsextension)Test plan
/admin/scrape/cian/upload-cookies→ POST /estimate с ЕКБ address →sources_usedincludescian_valuationMerged via deep-code-reviewer — verdict APPROVE.
Critical checks verified:
026_external_valuations.sql:34—ON CONFLICT (source, cache_key)resolves correctly, no permanent cache-miss bug.cian_session.py:135(db: Session) -> dict[str, str] | None). Returns None gracefully on no cookies / DB error / decryption fail.cian_session.py:186. Idempotent (simpleUPDATE last_invalid_at = NOW()), safe for parallel auth-fail races.address.strip().lower()+ pipe-separated params + sha256. Tests cover normalization + distinct-on-deal_type/area. Caveat: caller must passtotal_areaconsistently as float (int 45 vs float 45.0 produce different keys) — not exercised in tests but caller-side concern.CAST(:x AS type)per psycopg v3 convention. No:x::typeviolations.load_session()(saves DB hit + token check).isAuthenticated=false→mark_session_invalid()only ifuser_idtruthy (handles missing userId edge case).datetime.UTC(Python 3.12+ confirmed in pyproject.toml). 7 test points cover full deserialization._parse_change_pct: handles"8,3%"(RU comma),"-2,5%"(negative),"1.5%"(English dot),""(None). All 4 cases tested.price_rub/accuracyin favor ofsale_*/rent_*split). Intended per #448 design.curl_cffi(impersonate="chrome120")consistent withcian.py:20,avito_houses.py:29sibling pattern.Minor (non-blocking, optional follow-ups):
import datetimeat line 294 inconsistent with sibling files that use top-levelfrom datetime import UTC. Cosmetic only._load_from_cachere-uses cachedraw_stateonly if dict; logged values pass-through_parse_numeven when DB returns numeric (slight overhead, no correctness issue).Stage 8 (matching) + Stage 9 (estimator integration) unblocked.
Post-merge:
deploy-tradein.ymlwill trigger; verify CI pytest 28/28 green, then upload cookies via/admin/scrape/cian/upload-cookies(Stage 4 endpoint) → POST/estimateс ЕКБ address → expect logCian valuation extracted: sale=...→ repeat within 24h → expectCian valuation cache HIT.Merge commit:
b0a72351.