houses.house_class (comfort/business/premium/economy) заполнен у 0% из 6958
домов. Два независимых бага:
Bug 2 (quick win): avito EXPAND_PARAMS_MAP не извлекал «Класс жилья»
(developmentPage/новостройки) в колонку — добавлена нормализация RU→канон
+ COALESCE-write в _persist_house (avito = топ-приоритетный источник,
перезаписывает при наличии значения, None не затирает существующее).
Зеркальная правка в scraper-kit копии (parity-тест этого требует).
Bug 1 (частично): resolve_house_field/HOUSE_FIELD_PRIORITY
(conflict_resolution.py) подтверждён dead code (0 прод-вызовов) — не
трогал. Вместо этого починен yandex_newbuilding_sweep.py: парсил
house_class, но нигде не персистил (терялось безвозвратно) — добавлен
fill-only UPDATE (COALESCE(houses.house_class, :new) — yandex_realty_nb
низший приоритет, не перетирает более приоритетный источник).
- pyproject.toml: httpx[socks]>=0.27.0 — тянет socksio, socks5:// прокси в _probe_current_ip работает прозрачно
- admin.py list_scrape_runs_unified: status Literal["done","running","banned","zombie","failed","cancelled"] вместо str|None — невалидные значения → 422
- avito_houses.py save_house_catalog_enrichment: гард ext_id=0 + нет адреса → skip without DB touch; два id-less novostroyka URL не схлопываются в bogus avito:0 в house_sources
- тесты: test_unified_runs_invalid_status_422 + test_unified_runs_valid_statuses_200; test_save_house_catalog_enrichment_skips_zero_ext_id_no_address + *_with_address_persists
Корень проблемы: avito_houses.py обходил канонический matching-путь через
прямой INSERT ON CONFLICT(source, ext_house_id), создавая дубли для физически
одного дома (~33% таблицы houses = ~4479 лишних строк).
Forward fix: upsert_house/upsert_house_with_url → _persist_house(db, h, url),
который вызывает match_or_create_house (3-tier: source_exact→fingerprint→geo→new,
с pg_advisory_xact_lock) и затем UPDATE с enrichment-полями. avito-авторитетные
поля (rating, developer, технические хар-ки) перезаписываются напрямую;
address/lat/lon/year_built — через COALESCE чтобы не затирать canonical-данные.
Merge-миграция 108: cluster_key по cadastral/address/geo → canonical (max
не-NULL полей, geom присутствует, min(id)) → репойнт всех 11 зависимых таблиц
с обработкой UNIQUE-коллизий → DELETE дублей → backfill house_sources/aliases.