fix(yandex-valuation): tighten area regex + chronological date ordering #541
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#541
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/tradein-yandex-parser-quality"
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
3 parser quality bugs observed live on Базовый 52 sweep (132 rows):
A.
area_m2captured year+area concat like202442.6from chunked text where DOM whitespace was stripped between publish-year and area tokens → 52% of rows had area dropped by sanity cap. Fix:RE_ITEM_AREAgets negative lookbehind(?<![\d.,])+ 4-digit cap on whole part.B.
area_m2 = 2.20for 2-к/8M ₽ flats — same regex matched a sub-fragment2,2inside52,2. Same fix as A.C. Date inversion —
start_price_date > removed_dateon ~1% of rows._parse_item_textassigned by page-text order, not chronologically. Fix: sort parsed dates list before splitting into publish/removed.Tests
Followup
external_valuationscache for yandex_valuation before next sweep (cached rows still hold old parses)Deep Code Review — verdict APPROVE
Scope: 2 files (P1 scraper + tests), +78 / -3, single-domain (backend tradein scraper). Surgical fix on the heels of #508/#526/#538.
Correctness — sound
A. Regex tightening
(?<![\d.,])(\d{1,4}(?:[.,]\d{1,2})?)\s*м²[\d.,]correctly blocks the2024+42,6concat →20242,6failure on chunked text. Traced position-by-position for202452,2 м²input — regex engine has no valid starting position after fix.\d{1,4}whole-part cap is safe: flats are <500 m², 4-digit ceiling = 9999 m² leaves room without enabling overflow rows. Existing> 10_000sanity cap at line 297 stays as belt-and-suspenders (acknowledged in PR body).[.,]\d{1,2}decimal cap matches observed Yandex precision (1–2 dp).42,6 м²at start-of-string passes lookbehind (no prior char).B. Chronological date sort
[0]/[1]is the correct semantic fix forremoved_date > start_price_dateinversion observed on ~1% prod rows.publish_dateis by definitionmin(dates),removed_dateismax(dates)for ≤2 dates.for d in dates_parsed if d is not Noneinsidesorted(...)is harmless —dates_parsed.appendat line 325 already gates on non-None.Cross-file impact — author-aware
estimator.py:511buildsext_item_id = sha256(address|publish_date|area_m2|floor|prices). Both fixes changearea_m2from bogus →None(or different parsed value) → freshext_item_id→ON CONFLICT (source, ext_item_id) DO NOTHINGwon't dedupe old zombie rows.external_valuationscache for yandex_valuation before next sweep. Acknowledged followup — not a blocker.trade_in.py:520, schemastart_price_date/removed_date) consume dates withORDER BY COALESCE(last_price_date, start_price_date)— chronological sort is monotonic-friendly, no breakage.Tests — 4/5 sharp, 1 non-discriminating
test_area_regex_rejects_year_concat— verified by hand: regex finds no valid starting position in202452,2 м². Test will fail if lookbehind ever drops. ✓test_area_regex_accepts_isolated_token—58,2 м²after whitespace → matches. ✓test_area_regex_capped_at_4_digits—12345 м²rejected (greedy\d{1,4}matches1234, then\s*м²requires space/мnext, but next is5; backtracking impossible due to lookbehind blocking lower-pos starts on digit-runs). ✓test_single_date_is_publish_only— single date assigned to publish, not removed. ✓test_dates_sorted_publish_before_removed— 🟢 Low (test quality): text input has dates13.08.2025then12.02.2026which are already in chronological order in page-text. Pre-PR code (dates_parsed[0]/dates_parsed[1]) would yield the same assignment, so this test would pass even with the sort line removed. Comment claims "reversed order" but it isn't. Suggest swapping to e.g.13.08.2026 В экспозиции 156 дней 12.02.2025(later date first) to actually exercise the sort. Not a blocker — the other 4 tests + the prod observation justify the change.Performance / Security / Conventions
\d{1,4}/\d{1,2}quantifiers are faster than the prior unbounded\d+[.,]?\d*. Sort of ≤2-3 items is O(1) practically.Positive
# Negative lookbehind on...and# Sort dates chronologically —comments inline document the why, including production observation rate.Recommended next steps (post-merge)
external_valuationscache forsource = 'yandex_valuation'before next Yandex sweep so the zombie rows get re-parsed with the tighter regex.house_placement_historyfor: anyarea_m2 > 1000, any rows withremoved_date < start_price_date, distribution ofarea_m2 IS NULL(should drop from 52% to single digits if PR A landed).test_dates_sorted_publish_before_removedso the page-text order is genuinely reversed.Complexity / blast radius