fix(tradein/domclick): sale_type перестаёт быть третьим словарём (#2674) #2696
No reviewers
Labels
No labels
Fable 5 ревью
GG-форсайт
admin
analytics
auth
automation
bug
business
chore
ci
compliance
data
data-moat
docs
duplicate
dx
enhancement
feedback/max
generative
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#2696
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "fix/2674-sale-type-dialects"
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?
Замер прода 2026-08-06
SELECT source, sale_type, count(*) FROM listings GROUP BY 1,2:free2976 ·assignment46 · NULL 45020free14800 ·fz2144673 ·alternative1327 ·dupt409 ·investment64 ·dzhsk3 ·pdkp1 · NULL 4636101 русская строка против 24 299 английских.
WHERE sale_type = 'free'не видит ни одной строки Домклика;GROUP BY sale_typeразносит одну и ту же свободную продажу на две корзины.Что аудит не досчитал: читателей у колонки НОЛЬ
Grep по всему репозиторию (2026-08-06) даёт только писателей —
providers/{avito,cian,domclick},scraper_kit/base.py,scripts/ingest_domclick_jsonl.py,scripts/domclick_local_runner.py— и DDL. Ни одного фильтра, агрегата, поля схемы API или элемента фронтенда. Единственное упоминание вapp/— комментарий вconfig.py, аv_data_qualityтянет колонку транзитом черезSELECT *.Поэтому «канон» целиком здесь не вводится: выбирать между cian-токеном
duptи avito-токеномassignment(409 против 46 строк, оба = переуступка права требования) без единого читателя — монетка, и любой выбор потом придётся переигрывать под настоящего потребителя. Чинится ровно то, что сломано объективно: один писатель кладёт сырую русскую фразу там, где два других уже кладут английский токен.Проверка смысла ДО маппинга (требование эпика)
cross-tab
sale_type × listing_segmentна проде — это одна ось, структура сделки:vtorichka, те же два полюса, что cianfree(8545 vtorichka) /alternative(1185 vtorichka);fz214/dupt/investment/dzhsk/pdkp— 5089 строк из 5107 вnovostroyki, т.е. ПОДмножество той же оси (какой договор на первичке), а не другой признак.Конфликта смыслов нет → склейка
free/alternativeбезопасна. Это и есть тот самый случай, где нормализация возможна; будь оси разными, вывод был бы обратный.Что сделано
canon_sale_type()вproviders/domclick/detail.py— на единой границе записи (save_detail_enrichment), а не в парсере: тот же dataclass пишут три производителя (парсер карточки,ingest_domclick_jsonl.py,domclick_local_runner.py— именно он налил 6101 строку 2026-07-18). Незнакомое →None, как уavito._map_lower:COALESCEв UPDATE не затрёт существующее.Свободная продажа→free(5546) +Альтернатива→alternative(553) = 6099 строк, счётчики сняты на проде ДО применения. Идемпотентна (повторный прогон — 0 строк).COMMENT ON COLUMN listings.sale_type— фактический словарь по источникам, несведённая параdupt/assignmentи предупреждение сверяться с реальнымGROUP BYперед фильтром.«Первичная продажа» (2 строки) осознанно НЕ маппится: у cian первичка закодирована типом ДОГОВОРА (
fz214/dzhsk/pdkp/dupt), а маркетинговая метка Домклика договор не называет — угадывать нельзя. Строки остаются как есть, факт зафиксирован в COMMENT.Test plan
canon_sale_type— параметризованный тест на КАЖДОЕ фактическое значение прода (+ регистр/пробелы, неизвестное, пустое, None).save_detail_enrichmentпишетfree, а не русскую фразу; для неотображаемого значения —Noneпод защитойCOALESCE.tests/scrapers/test_domclick_detail.py+test_migrations_manifest.pyзелёные (55 passed).SELECT source, sale_type, count(*) FROM listings WHERE source='domklik' GROUP BY 1,2→ ожидаетсяfree5546,alternative553, «Первичная продажа» 2, русскихСвободная продажа/Альтернатива— ноль.Refs #2674
Замер прода 2026-08-06 (SELECT source, sale_type, count(*) FROM listings): avito : free 2976 · assignment 46 · NULL 45020 cian : free 14800 · fz214 4673 · alternative 1327 · dupt 409 · investment 64 · dzhsk 3 · pdkp 1 · NULL 463 domklik : «Свободная продажа» 5546 · «Альтернатива» 553 · «Первичная продажа» 2 · NULL 493 n1/yandex: колонку не пишут вовсе (382 и 16721 NULL) Итого 6101 русская строка против 24 299 английских: `WHERE sale_type='free'` не видит ни одного Домклика, `GROUP BY sale_type` разносит одну и ту же свободную продажу на две корзины. ВАЖНОЕ ОГРАНИЧЕНИЕ, найденное по ходу: читателей у колонки НОЛЬ. Grep по всему репозиторию даёт только писателей (providers/{avito,cian,domclick}, base.py, scripts/ingest_domclick_jsonl.py) и DDL — ни фильтра, ни агрегата, ни поля схемы API, ни элемента фронтенда. Поэтому канон целиком тут НЕ вводится: выбирать между cian-токеном dupt и avito-токеном assignment (409 против 46 строк, оба = переуступка) без единого читателя — монетка. Чинится ровно объективно сломанное: один писатель клал сырую русскую фразу там, где два других уже кладут английский токен. Проверка смысла перед маппингом (cross-tab sale_type × listing_segment): это одна ось — структура сделки. Русские значения Домклика на 100% vtorichka и совпадают с полюсами cian free/alternative; хвост cian fz214/dupt/investment/dzhsk/pdkp — 5089 из 5107 строк novostroyki, т.е. ПОДмножество той же оси (какой договор на первичке), а не другой признак. Конфликта смыслов нет, склейка free/alternative безопасна. canon_sale_type стоит на ЕДИНОЙ границе записи (save_detail_enrichment), а не в парсере: в listings пишут три производителя одного dataclass'а — парсер карточки, ingest_domclick_jsonl.py и domclick_local_runner.py (именно он налил 6101 русскую строку 2026-07-18). «Первичная продажа» (2 строки) осознанно НЕ маппится: у cian первичка закодирована типом ДОГОВОРА, а маркетинговая метка Домклика договор не называет — угадывать нельзя. Строки остаются как есть, факт зафиксирован в COMMENT ON COLUMN вместе со всем фактическим словарём по источникам. Миграция 220 (backfill 5546 + 553 = 6099 строк, счётчики сняты на проде ДО применения; повторный прогон — 0 строк). Refs #2674