feat(tradein): Phase C — Avito IMV per-house backfill (SQL + script) #534
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#534
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/tradein-phase-c-house-imv-backfill"
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?
Context
Phase C of
[[Decision_TradeIn_DataQuality_8PR_Roadmap]]— наполнитьhouse_placement_history+ 2 новые таблицы данными от Avito IMV для каждого из 6,507 houses. Survives restart, runs локально пишет в продовую БД.Depends on:
evaluate_via_imv()workingSQL migration 064
houses.imv_status text('pending' | 'ok' | 'not_found' | 'transient_error' | 'error' | 'no_params' | 'no_address')houses.last_imv_attempt_at timestamptzhouses.imv_error_reason texthouses_imv_status_idxдля быстрой выборки pending/transienthouse_imv_evaluations(1:1 c houses, UPSERT поhouse_id) — recommended/lower/higher_price + market_count + raw_response + lot-params usedhouse_suggestions(8 Avito-аналогов per house, UNIQUE(house_id, ext_item_id)) — title/address/price/exposure/publish_date/item_url/image/metroScript
scripts/backfill-house-imv.pyResumable iterator:
percentile_cont, house_type viamode()) из linked listingsevaluate_via_imv()(PR #524 3-step chain)imv_status='ok'/ appropriate error statusArgs:
--batch N --delay SEC --limit-total N --only-status pending|transient_errorRecovery:
IMVAddressNotFoundError→ status='not_found' (won't retry)IMVAuthError→ 60-sec backoff + status='transient_error' (can retry с --only-status transient_error)IMVTransientError→ status='transient_error' (retry)Workflow для запуска (после merge+deploy)
Агрессивный темп ~3-5 sec → 6,507 houses ≈ 5-9 часов. Прерываемо в любой момент: следующий запуск продолжит с
imv_status='pending'.After backfill complete
Follow-up PR (отдельно): surface
house_imv_evaluations+house_suggestionsна UI (расширитьHouseInfoCard, добавить новуюSuggestionsCard).SQL migration 064: adds houses.imv_status + last_imv_attempt_at + imv_error_reason columns, creates house_imv_evaluations (1:1 with houses, latest IMV result) and house_suggestions (8 Avito similar lots per house, UNIQUE on (house_id, ext_item_id)). Script scripts/backfill-house-imv.py: resumable iterator that picks houses with imv_status='pending' AND lat/lon NOT NULL, runs evaluate_via_imv() per house with median lot-params from linked listings, saves house_imv_evaluations + house_placement_history (from result.placement_history items) + house_suggestions (from result.suggestions items). Configurable --batch, --delay, --limit-total. Survives restart via imv_status state machine ('pending' -> 'ok'/'not_found'/ 'transient_error'/'error'/'no_params'/'no_address'). Long backoff on IMVAuthError (anti-captcha).Deep Code Review — verdict
Summary
[[Decision_TradeIn_DataQuality_8PR_Roadmap]]e868b01278e3b53ae3243f2618ebce3b80a81c30(matches)Verified strengths
064fully idempotent:IF NOT EXISTSeverywhere, singleBEGIN/COMMIT, lex-sort 063→064 correct fordeploy-tradein.ymlSQL applier loop.ON CONFLICTtargets backed by existing UNIQUE:house_imv_eval_house_uniq_idx(house_id),house_suggestions_uniq_idx(house_id, ext_item_id), and pre-existinghouse_placement_history (source, ext_item_id)from 017.houses_imv_status_idx WHERE imv_status IN ('pending','transient_error')makes per-batchSELECT ... ORDER BY last_imv_attempt_at NULLS FIRSTcheap.db.commit()after eachprocess_one; pre-commit failure insave_imv_resultleaves house at'pending'→ re-run picks up cleanly.evaluate_via_imv(address=, rooms=, area_m2=, floor=, floor_at_home=, house_type=, renovation_type=, has_balcony=, has_loggia=)call matches the 3-step adapter signature from PR #524 exactly.IMVAddressNotFoundError → not_found(terminal),IMVAuthError → transient_error + 60s sleep,IMVTransientError → transient_error, genericException → error + repr.postgresql+psycopg://) +CAST(:raw AS jsonb)everywhere, no:x::typetraps.pick_lot_paramsuses correct FKlistings.house_id_fk+percentile_cont(0.5) WITHIN GROUP (ORDER BY ...)median +mode()for house_type + reasonable fallbacks (floor=1, total_floors=9, panel/cosmetic).raw_payloadfilters out heavyitemImage/imageLinkkeys inside adapter dataclass parser (already done upstream).Non-blocking suggestions (post-merge follow-ups)
scripts/backfill-house-imv.py:474. PR #533 (sweep-yandex-valuation-ekb.py) usesawait asyncio.sleep(delay * random.uniform(0.85, 1.3))for Avito-captcha resilience. Phase C uses deterministicargs.delay; recommend matching the jitter pattern.process_oneonIMVAuthErrormarks one house + sleeps 60s then returns. If Avito blocks auth, all 50 houses in a--batchget markedtransient_errorover ~50 min. Suggest a counter in main loop: 3+ consecutiveauth_error→ break withlogger.error('Avito auth blocked, aborting batch').save_imv_resultnot in try/except — a SQL-side failure (e.g. concurrent house delete → FK violation) kills the whole batch with nomark_status='error'audit trail. Wrap intry/except Exceptionandmark_statusbefore re-raising or returning'error'.--only-statusarg validation — no whitelist; typo'pendng'silently returns 0 rows; passing'ok'reprocesses healthy houses (unintended). Recommendchoices=['pending','transient_error'].house_suggestionsover-specified columns — DDL hasarea_m2, rooms, floor, total_floors, image_linkbutIMVSuggestiondataclass doesn't expose these so INSERT leaves them NULL. Either drop the columns or populate from raw_payload now to avoid future schema drift confusion.FOR UPDATE SKIP LOCKED— two concurrent runs against the same DB pick overlapping rows. UPSERT prevents corruption; Avito side gets 2× rate. Docstring states "locally with SSH tunnel" so single-instance is the intended mode; document--only-status transient_errorretry pattern as the canonical resumption path.raw_responsejsonb growth — full IMV response × 6,507 houses ≈ 0.5–1.0 GB jsonb. Acceptable for backfill audit; long-term consider pruning to parsed fields once Phase D consumes them.--skip-recent-daysspec drift — body promised this flag; implementation provides only--only-status. To re-scan staleokhouses, operator must manuallyUPDATE houses SET imv_status='pending' WHERE imv_status='ok' AND last_imv_attempt_at < now() - interval '30 days'. Worth noting in script docstring.Cross-file impact
house_placement_history1.4× write amplification per backfilled house (8 suggestions + ~5–20 placement items each). Existing indexeshph_house_idx,hph_source_item_idx,hph_house_source_extid_uniq_idxcover the UPSERT cost.housesUPDATE per row writesimv_status, last_imv_attempt_at, imv_error_reason. Partial indexhouses_imv_status_idxwill see ~6,500 deletes (rows leavingpending) over the full sweep — cheap.house_imv_evaluationsis 1:1 with houses (UNIQUE house_id);house_suggestions≤ 8 per house. Storage cap ~52k rows + jsonb.Vault cross-check
[[Decision_TradeIn_DataQuality_8PR_Roadmap]]covered the original 8/8 estimator-quality PRs (Event_TradeIn_DataQuality_8PR_Done_May24). Phase C (this PR) is a separate per-house data buildup roadmap not in the original 8 — recommend a brief inbox decision note when Phase D lands so the chain is traceable.[[avito_scraper]]:evaluate_via_imv()3-request flow +IMVAddressNotFoundError/IMVAuthError/IMVTransientErrorexception types — script consumes the API surface correctly.Risk / blast radius
housescolumns + UPSERTs intohouse_placement_historywith documented constraint.IF NOT EXISTS; rollback =DROP TABLE house_imv_evaluations, house_suggestions; ALTER TABLE houses DROP COLUMN imv_status, last_imv_attempt_at, imv_error_reason.Approving for any-scope auto-merge.