fix(yandex-valuation): relax area regex lookbehind (2-комн regression) #542

Merged
lekss361 merged 1 commit from fix/tradein-yandex-area-regex-relax into main 2026-05-24 16:02:06 +00:00
Owner

Summary

  • RE_ITEM_AREA lookbehind relaxed: (?<![\d.,])(?<!\d) — only digit-adjacency blocks now, comma/dot before token is fine
  • Quantifier tightened: \d{1,4}\d{2,4} — min 2 digits blocks sub-fragments like '2,2' from inside '52,2'

Why (live regression)

PR #541 over-tightened the regex. Re-sweep Базовый 52 (after cache clear): area_filled dropped from 48% → 8.8% (13/148 rows). 100% of 2-комн rows lost area, while 1-к and 3-к kept theirs — likely because chunked-text fallback joins a preceding price-per-m2 digit to 2-комн areas (52,2) but not to others.

Year-concat (the original bug of PR #541) is still rejected: greedy \d{2,4} grabs '2024', then [.,] separator doesn't follow → no match. Sub-fragment '2,2' inside '52,2' rejected by min-2-digit quantifier.

Tests

  • 36/36 pytest pass (33 prior + 3 new for 2-комн / year-concat / sub-fragment regressions)
  • ruff clean
  • Sanity cap from PR #538 (>10000 m² → None) stays for any junk that slips through.

Followup

  • After merge: clear external_valuations yandex_valuation cache, re-sweep Базовый 52, verify area_filled back to ≥80%.
## Summary - `RE_ITEM_AREA` lookbehind relaxed: `(?<![\d.,])` → `(?<!\d)` — only digit-adjacency blocks now, comma/dot before token is fine - Quantifier tightened: `\d{1,4}` → `\d{2,4}` — min 2 digits blocks sub-fragments like '2,2' from inside '52,2' ## Why (live regression) PR #541 over-tightened the regex. Re-sweep Базовый 52 (after cache clear): `area_filled` dropped from 48% → **8.8%** (13/148 rows). 100% of 2-комн rows lost area, while 1-к and 3-к kept theirs — likely because chunked-text fallback joins a preceding price-per-m2 digit to 2-комн areas (52,2) but not to others. Year-concat (the original bug of PR #541) is still rejected: greedy `\d{2,4}` grabs '2024', then `[.,]` separator doesn't follow → no match. Sub-fragment '2,2' inside '52,2' rejected by min-2-digit quantifier. ## Tests - 36/36 pytest pass (33 prior + 3 new for 2-комн / year-concat / sub-fragment regressions) - ruff clean - Sanity cap from PR #538 (>10000 m² → None) stays for any junk that slips through. ## Followup - After merge: clear `external_valuations` yandex_valuation cache, re-sweep Базовый 52, verify `area_filled` back to ≥80%.
lekss361 added 1 commit 2026-05-24 15:50:39 +00:00
PR #541 lookbehind (?<![\d.,]) was too strict — re-sweep showed
area_filled dropped from 48% to 8.8% with all 2-комн rooms losing
area (chunked text often has a price-per-m2 digit immediately before
the room's area token).

Loosen to (?<!\d) (only digit-adjacency blocked, comma/dot fine)
and tighten quantifier to \d{2,4} (min 2 digits blocks sub-fragments
like '2,2' inside '52,2'). Year-concat 20244,6 still rejected because
greedy '\d{2,4}' grabs '2024', then [.,] separator doesn't follow.
Author
Owner

Deep review verdict: APPROVE

Summary

  • Status: APPROVE
  • Files: 2 (yandex_valuation.py +6/-4, test_yandex_valuation.py +34/-0)
  • Verified at head SHA 9b07a0f

Regex analysis

The change in lookbehind: from blocking (digit OR comma OR dot) to blocking only digits. The quantifier is tightened from min-1 digit to min-2 digits to compensate. Net result: same coverage against the year-concat and subfragment bugs from PR #541, but accepts areas preceded by punctuation (real production scenario - many chunked Yandex texts have a comma directly preceding the area number).

Verification (traced regex behavior in Python interpreter)

  • 2-комн legit chunk -> match 52.2 OK
  • 1-к legit chunk -> match 42.6 OK
  • Студия chunk -> match 28 OK
  • Year-concat 202442,6 -> no match (blocked) OK
  • Subfragment ,2,2 -> no match (min-2-digits blocks) OK
  • Sanity cap from PR #538 at line 294 (> 10_000 or <= 0) preserved OK
  • All 4 prior tests from PR #541 verified still passing under the new regex OK

Minor nits (non-blocking)

  1. Doc/code mismatch on cap: comment in PR says "penthouse extremes are <500 m²" but the actual cap is 10_000. Either tighten cap or soften comment. Low severity, scope-creep to fix here.

  2. Test text does not exactly reproduce OLD failure: test_area_regex_two_komn_chunk claims "100% rejected by PR #541 lookbehind" but the chosen text actually matches under the OLD regex too (space precedes the 5). The real OLD-failing texts had comma/dot directly before the area number. Test still validates NEW behavior correctly - documentation precision issue only.

  3. Single-digit areas no longer matchable: the min-2-digits quantifier rejects 9 м². Apartments under 10 m² are unrealistic so this is acceptable, but worth knowing if Yandex ever emits parking-space or storage data through the same parser.

Positive

  • Three regression tests with clear naming document both sides of the trade-off
  • Inline regex docstring explains the trade-off well
  • Sanity cap from PR #538 not touched - good scope discipline
  • Tests use resilient assert item is None or item.area_m2 is None pattern

Blast radius

  • Risk: low - regex change with explicit regression tests on both sides
  • Reversibility: trivial git revert
  • Affects: _parse_item_text only - all callers (history items) benefit
  • No DB schema / API contract change
## Deep review verdict: APPROVE <!-- gendesign-review-bot: sha=9b07a0f verdict=approve --> ### Summary - Status: APPROVE - Files: 2 (yandex_valuation.py +6/-4, test_yandex_valuation.py +34/-0) - Verified at head SHA 9b07a0f ### Regex analysis The change in lookbehind: from blocking (digit OR comma OR dot) to blocking only digits. The quantifier is tightened from min-1 digit to min-2 digits to compensate. Net result: same coverage against the year-concat and subfragment bugs from PR #541, but accepts areas preceded by punctuation (real production scenario - many chunked Yandex texts have a comma directly preceding the area number). ### Verification (traced regex behavior in Python interpreter) - 2-комн legit chunk -> match 52.2 OK - 1-к legit chunk -> match 42.6 OK - Студия chunk -> match 28 OK - Year-concat 202442,6 -> no match (blocked) OK - Subfragment ,2,2 -> no match (min-2-digits blocks) OK - Sanity cap from PR #538 at line 294 (> 10_000 or <= 0) preserved OK - All 4 prior tests from PR #541 verified still passing under the new regex OK ### Minor nits (non-blocking) 1. **Doc/code mismatch on cap**: comment in PR says "penthouse extremes are <500 m²" but the actual cap is 10_000. Either tighten cap or soften comment. Low severity, scope-creep to fix here. 2. **Test text does not exactly reproduce OLD failure**: `test_area_regex_two_komn_chunk` claims "100% rejected by PR #541 lookbehind" but the chosen text actually matches under the OLD regex too (space precedes the 5). The real OLD-failing texts had comma/dot directly before the area number. Test still validates NEW behavior correctly - documentation precision issue only. 3. **Single-digit areas no longer matchable**: the min-2-digits quantifier rejects `9 м²`. Apartments under 10 m² are unrealistic so this is acceptable, but worth knowing if Yandex ever emits parking-space or storage data through the same parser. ### Positive - Three regression tests with clear naming document both sides of the trade-off - Inline regex docstring explains the trade-off well - Sanity cap from PR #538 not touched - good scope discipline - Tests use resilient `assert item is None or item.area_m2 is None` pattern ### Blast radius - Risk: low - regex change with explicit regression tests on both sides - Reversibility: trivial git revert - Affects: `_parse_item_text` only - all callers (history items) benefit - No DB schema / API contract change
lekss361 merged commit 3ed58e3e7a into main 2026-05-24 16:02:06 +00:00
lekss361 deleted branch fix/tradein-yandex-area-regex-relax 2026-05-24 16:02:07 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference: lekss361/gendesign#542
No description provided.