feat(mera/b2c): метрики лэндинга считаются по проду, а не лежат литералами #3228
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
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference: lekss361/gendesign#3228
Loading…
Add table
Reference in a new issue
No description provided.
Delete branch "feat/b2c-landing-stats"
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?
Волна A подготовки нового лэндинга к проду. Сегодня числа витрины выдуманы в
frontend/src/app/mera-public/marketing-v3.ts; этот PR даёт им источник.Что внутри
data/sql/275_landing_stats.sql— таблицаlanding_stats(metricPK,value_num/value_text,sample_n,note,computed_at) + сид расписанияlanding_stats_refresh(05:00–06:00 UTC).app/tasks/landing_stats.py—collect_landing_metrics()(чистый расчёт, отделён ради теста по значениям) иrefresh_landing_stats()(UPSERT + lifecycle прогона).GET /api/public/mera/stats— публичная ручка, путь добавлен вrbac._PUBLIC_PATHS. Пустая таблица ={}с 200, не 500.Метрики:
estimates_total,estimates_period_days,analogs_median,listing_age_median_days,price_cut_share_pct,price_cut_median_pct_per_month,deals_total_12m. У каждой естьsample_nиnote. Метрики без входа строка НЕ пишется — ни одного подставленного нуля.Точность прогноза и срок продажи здесь намеренно НЕ считаются: первое приходит из бэктеста (соседний PR), второго не существует как данных.
Знаменатель — главное место этого PR
Разведка предлагала показать «72% продавцов снижают цену». Это было бы неверно вдвойне, и обе ошибки исправлены в коде:
Первая — кто в знаменателе. Продавцы, не тронувшие цену, имеют в
offer_price_historyровно одну запись и из выборкиHAVING count(*) >= 2выпадают:Вторая — смесь источников. Стартовую цену пишет только Домклик. Триггер на
listingsфиксирует лишь ИЗМЕНЕНИЕ, поэтому у Авито и Яндекса «первая цена» — это уже первое снижение, аyandex_price_historyвдобавок сеет синтетическую паруnow − 1 день. По той же выборке Яндекс даёт 47,4% снижавших при медиане +0,49%.Запрос в задаче считает по
source = 'domklik'и включает в знаменатель тех, у кого запись одна. Замер сошёлся у двух независимых путей: агент получил «85% против 48%», я своим запросом — 84,2% против 47,1%.Отдельно: скорость снижения нормируется на 30 дней по интервалу между крайними правками, а не по всему наблюдению — после последней правки цена не менялась, и растягивая знаменатель на «висит до сих пор», мы мерили бы терпение продавца, а не торг.
Приёмка на проде (числом)
Форсировать первый прогон и посмотреть результат:
Ожидается
price_cut_share_pct ≈ 47приsample_n ≈ 6400. Если увидите ≈85 — знаменатель уехал на «только менявших», и это регресс.Публичность ручки:
200 без заголовка
X-Authenticated-User; 401 означал бы, что путь не попал в_PUBLIC_PATHS.Осталось
Фронтенд НЕ тронут —
marketing-v3.tsдо сих пор содержит литералы, лэндинг продолжает показывать выдуманные числа. Перевод витрины на эту ручку — отдельная волна, потому что набор метрик и набор цифр в макете совпадают не один в один: часть макетных величин (срок продажи, «×2,4 дольше») придётся снять, а не заменить.Числа на публичном лэндинге лежали литералами во фронте (mera-public/marketing-v3.ts) — то есть были выдуманы и не имели срока годности. Теперь их считает ночная задача и отдаёт публичная ручка, вместе с размером выборки и описанием того, что именно измерено. Что считается: число расчётов и период работы, медиана аналогов на расчёт, медианная ЭКСПОЗИЦИЯ активного объявления по ЕКБ (не срок продажи — так и написано в note), доля снижавших цену и медианное снижение за 30 дней, сделки Росреестра по ЕКБ за 12 месяцев. Ценовые метрики берут ТОЛЬКО domklik: у avito/yandex триггер не пишет стартовую цену, а yandex вдобавок сеет синтетическую пару со сдвигом в сутки — на такой смеси «снизил» и «не снижал» неразличимы. Знаменатель доли — все объявления, наблюдавшиеся от 14 дней, включая не менявшие цену; считая только по менявшим, получили бы 85% вместо честных 48%. Метрика без входных данных строку НЕ пишет: подставленный ноль читался бы как измеренный ноль. Пустая таблица — валидные {} и 200, а не 500. «Точность прогноза» и «срок продажи» здесь не считаются намеренно — таких величин в данных нет.