feat(generator): корзина отвязана от страницы, у товара — уровень спроса
- Зачем: - приёмка #40 нашла в торговых данных два точных равенства, каких в живом магазине не бывает: событие корзины случалось ровно у визитов со страницей /cart, а внутри такого визита в корзину уходили все открытые карточки. Привлекательность товара было нечем измерить, а аналитик читал бы эти равенства как склейку в разметке. - Что: - класть в корзину может любой визит, открывший карточку; страница /cart осталась шагом воронки, а у визита, дошедшего до неё, корзина непуста. - у товара появился уровень спроса — колонка каталога с тремя значениями и два ряда вероятностей в числах мира: намерение визита берётся из шага воронки, а не из метки покупателя, которая уже действует через неё. - уровни рассыпаны по каталогу одной колодой: одинакового расклада по категориям нет, связи с ценой нет, и то и другое сторожится тестами. - числа мира перемерены: конверсия карточки в корзину 8,62% (по уровням 13,1 / 9,2 / 5,8), кладут без открытия корзины 41,4% таких визитов, средний день 49 834 события, конверсия визита в покупку 2,43%. - спека генератора и мастер-спека приведены к новой форме каталога. - Проверка: - make lint && make typecheck && make test — 394 passed (было 383). - трафиковая половина побайтово та же: sha256 по (URL, времени, WatchID) всех просмотров за 14 канонических дней совпадает со снимком до правки.
This commit is contained in:
@@ -6,7 +6,7 @@
|
||||
«в среднем 3–4 возврата» и «средняя кука активна ≈1,9 дня».
|
||||
"""
|
||||
|
||||
from clickstream_generator import world
|
||||
from clickstream_generator import catalog, world
|
||||
|
||||
|
||||
def mean_by_weights(values: tuple[int, ...], weights: tuple[int, ...]) -> float:
|
||||
@@ -150,6 +150,23 @@ def test_the_buyer_mark_shows_on_both_steps_of_the_funnel():
|
||||
assert 2 <= lift <= 6
|
||||
|
||||
|
||||
def test_the_demand_levels_keep_their_order_and_yield_to_the_intent():
|
||||
"""Ряды вероятностей читаются по уровням каталога, и оба убывают.
|
||||
|
||||
Совпадение длин — не формальность: ряды индексируются номером уровня,
|
||||
и новый уровень в файле каталога обязан получить здесь своё число.
|
||||
"""
|
||||
rows = (world.SHOPPING_ADD_PERCENT, world.BROWSING_ADD_PERCENT)
|
||||
for row in rows:
|
||||
assert len(row) == len(catalog.DEMAND_LEVELS)
|
||||
assert row[0] > row[1] > row[2], row
|
||||
# Намерение сильнее товара: визит, пришедший покупать, кладёт чаще на
|
||||
# любом уровне — иначе слово «намерение» ничего бы не значило.
|
||||
assert all(shopping > browsing for shopping, browsing in zip(*rows, strict=True))
|
||||
# Но и не всё подряд: товар решает у обоих, спрос не декорация.
|
||||
assert min(world.SHOPPING_ADD_PERCENT) < 100
|
||||
|
||||
|
||||
def test_the_buyer_mark_makes_the_cookie_live_longer():
|
||||
"""Второй рычаг метки: без долгой жизни второй покупке негде случиться."""
|
||||
assert world.BUYER_ONE_SHOT_PERCENT < world.ONE_SHOT_PERCENT
|
||||
|
||||
Reference in New Issue
Block a user