fix(tradein): filter ДКП-only in rosreestr importer + re-enable deals #549
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#549
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/tradein-rosreestr-dkp-only"
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
rosreestr_deals(open dataset Росреестра в gendesign-БД) содержит ОБА типа договоров:Текущий
import-rosreestr.shльёт ОБА вtradein.dealsбез фильтра поdoc_type. Из-за этого PR #504 сознательно отключил deals в estimator — компромисс на нехватку чистого источника. См.Decision_TradeIn_DataQuality_8PR_Roadmap.Fix
Filter источник до ДКП → возвращаем deals в estimator.
Changes
tradein-mvp/deploy/import-rosreestr.shAND doc_type = 'ДКП'в WHEREdedup_hashпрефикс изменён на'ros:dkp:'— чтобы при будущем re-import ДДУ как отдельного источника не было коллизийtradein-mvp/backend/app/services/estimator.py:837-841deals: list[dict[str, Any]] = []заменён на вызов уже существующего_fetch_deals()(line 1501)tradein-mvp/backend/tests/test_estimator_cian_integration.py(bonus fix)_fetch_analogsmock возвращал 2-tuple([], False)вместо 3-tuple([], False, "W")— broken после PR #507 (Tier S/H/W). Worker нашёл при pytest run, исправил во всех 5 вхождений.Post-merge ops (NOT in PR)
После merge — на prod manual через SSH:
Затем
./deploy/import-rosreestr.shre-runs с правильным фильтром.Verification
ruff check— passpytest test_estimator_cian_integration.py— 7/7 passReverses PR #504 на правильной стороне (источник, не estimator).
Deep Code Review — verdict
Summary
Correctness
rosreestr_deals_dealtype_idxis a partial btree ON(doc_type)WHEREdoc_type IN ('ДКП','ДДУ'). EXPLAIN ANALYZE on the importer's WHERE-clause (region 66 + ЕКБ + apt code + 2024+) shows only 2 distinct doc_type values. No leakage from дарение/наследование/мена — those transaction types simply do not appear in this slice of the dataset, so the newdoc_type='ДКП'filter is sufficient (no risk of unfiltered non-market types)._fetch_deals(db, *, lat, lon, rooms, area, radius_m)— all required kwargs supplied.geois non-None at line 840 (earlier_empty_estimateearly-return at line 698).payload.rooms/area_m2are Pydantic-required, so safe to pass.deals: (1) saved totrade_in_estimates.actual_dealsJSONB, (2)_estimate_days_on_market, (3) UI/PDF display. Not used for median/range — that's purely fromlistings_clean. So re-enabling does not perturb the median; it restores the supporting display data + days-on-market signal.DealsCard.tsx,HeroSummary.tsx,SourcesProgress.tsx) already gracefully handles both empty and populatedactual_deals(uses.length > 0/?? 0). No frontend regression.dedup_hash prefix change — good catch
md5('ros:' || id::text)→md5('ros:dkp:' || id::text)is the right move:DELETE WHERE source='rosreestr' AND dedup_hash NOT LIKE 'ros:dkp:%'.ros:ddu:prefix) — no hash collisions across data types.Test coverage
_fetch_analogsmocks updated from 2-tuple([], False)to 3-tuple([], False, "W")— correct shape per_fetch_analogssignature at lines 1284/1383/1504 (returns(candidates, fallback_radius_used, tier)). Restores pytest after PR #507 (Tier S/H/W)._fetch_deals → [], so the deals-reintegration code path is not directly asserted in unit tests. Acceptable scope — the path is trivial (call_fetch_dealsand pass through), and verifying the ДКП SQL filter would require a fixture against the realrosreestr_dealspartition (out of unit-test reach). Post-merge smoke (/estimatefor any ЕКБ address with new data) is the appropriate verification.Minor — stale doc comment (LOW, non-blocking)
estimator.py:578still carries the stale docstring NOTE:This is now obsolete — the PR re-enables deals. Suggest a follow-up edit (or fixup if you want to bundle): replace with a one-line
NOTE: rosreestr_deals filtered to doc_type='ДКП' at importer (PR #549).Post-merge ops verified
The PR body's manual ops are correct and necessary:
This is required because old rows with
ros:<id>hashes would otherwise persist alongside newros:dkp:<id>rows after re-import. TheLIKEpattern is correct (NOT LIKEmatches all old-prefix rows + any anomalies). The re-run./deploy/import-rosreestr.shthen populates clean ДКП-only data;geocode-dealscron will fill lat/lon.Cross-PR consistency with #504
Logically sound: PR #504 stubbed out
_fetch_dealswithdeals = []as a workaround for the ДДУ contamination. This PR addresses the upstream cause at the importer and reverses the workaround. The decision docDecision_TradeIn_DataQuality_8PR_RoadmapPR-2 row explicitly anticipated this ("Возвращаемся к этому когда отделим вторичные сделки от ДДУ в источнике").Security
'ДКП'correctly single-quoted inside the double-quoted-carg.Verdict: approve. Merging.