docs(labs): файлы ТЗ переименованы по схеме S-N-slug

- Зачем:
  - в docs/labs/ будет много заданий; номер ступени и номер задания в имени
    делают папку оглавлением программы и избавляют от поиска по README (#230).
- Что:
  - шесть ТЗ переименованы через git mv: 1-1…1-5 для ступени 1,
    2-1-superset-chart.md для ступени 2; факультатив superset-dashboard.md
    остался без номера.
  - обновлены ссылки в docs/labs/README.md, 2-1-superset-chart.md,
    docs/architecture/dds/session.md и пути в dags/homework_check.py.
  - правило имен для docs/labs/ в AGENTS.md переписано под S-N-slug.
- Проверка:
  - make lint; скрипт обхода относительных ссылок в docs/ и AGENTS.md —
    битых нет.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
ddadminandClaude Fable 5.1 committed 2026-09-06 09:09:14 -04:00
1 parent 8b9258ed41
commit d955f7d694
10 files changed
+21 -17

No files matched your search

+118
View File
@@ -0,0 +1,118 @@
# Задание 3: `dm.session_overview`
**Чему учит:** хранить слагаемые, а не готовые средние и доли. Понимать,
почему сумма `visitors` по строкам сейчас сходится, но начнет завышать
аудиторию, если у одной куки появятся визиты с двух устройств.
## Сначала прочитать
- [«Хранилище и его слои»](../handbook/storage-layers.md);
- [«Потоки Airflow и даги модельного времени»](../handbook/airflow-dags.md).
## Роль витрины
Витрина нужна, чтобы по дням сравнивать визиты с телефона и компьютера: их
число, длительность, просмотры страниц и частоту покупок.
На прошлом стенде витрина выделяла визиты сама. Теперь их рассчитывают в
DDS, а повторять зерно сессий ради представления с переименованными
колонками незачем. Здесь вы учитесь дневной агрегации, поэтому зерно — день
и устройство. Выбор описан в разделе «Форма: по зерну, не по цене запроса»
[справочника DM](../architecture/dm.md).
## Что сделать
Источник — `dds.session_v`. Зерно — **день сессии × категория устройства**.
Зерно новое, поэтому витрина хранится готовым агрегатом — см. [«Как принято
на ступени 1»](README.md#как-принято-на-ступени-1). Создайте пять объектов:
`dm.session_overview_rep` / `_dist`, донор `_stage_rep` / `_stage_dist` и
публичное представление `dm.session_overview_v`. Ключ партиции — день.
### Состав колонок
| Колонка | Тип | Значение |
|---|---|---|
| `report_date` | `Date` | День сессии |
| `device_category` | `LowCardinality(String)` | Категория устройства |
| `sessions` | `UInt64` | Число визитов |
| `visitors` | `UInt64` | Число разных `client_id` |
| `bounce_sessions` | `UInt64` | Визиты ровно из одного события |
| `purchase_sessions` | `UInt64` | Визиты хотя бы с одной покупкой |
| `duration_seconds_total` | `UInt64` | Суммарная длительность визитов |
| `events_total` | `UInt64` | Число событий во всех визитах |
| `pageviews` | `UInt64` | Число просмотров страниц |
| `cart_adds` | `UInt64` | Число добавлений в корзину |
| `purchases` | `UInt64` | Число покупок |
| `_load_id` | `String` | Идентификатор запуска, собравшего день |
| `_load_ts` | `DateTime64(3, 'UTC')` | Время сборки |
Возьмите `device_category` из сессии: DDS уже заменил код словом. Из четырех
категорий встречаются `desktop`, `mobile` и `tablet`. Строку для `tv`
создавайте только при появлении таких визитов.
Вместо `avg_duration_seconds` храните слагаемые. Среднее из средних по трем
устройствам даст неверный ответ за день: число сессий в категориях
различается, поэтому средние нужно взвешивать. Потребитель делит сумму
`duration_seconds_total` на сумму `sessions`. Долю отказов считают так же:
сумму `bounce_sessions` делят на сумму `sessions`, а не усредняют готовые
доли.
### Ритм пересборки
Сессии прожитого дня больше не меняются. Как в задании 2, определяйте
диапазон от первого несобранного прожитого дня до последнего прожитого.
Соберите его в донор и замените дневные партиции. Добавьте задачу в
`dm_transform`.
### Ключ шардирования
Выберите ключ самостоятельно. Визитов с планшета мало, с телефонов и
компьютеров — значительно больше. Если хотите взять хеш только от
`device_category`, сначала посчитайте строки на каждом шарде. Затем
объясните, почему такое распределение вас устраивает.
Ключ донора — тот же, что у цели (см. [«Как принято на ступени
1»](README.md#как-принято-на-ступени-1) и раздел «Раскладка по шардам»
[справочника хранилища](../architecture/storage.md)).
## Почему сумма `visitors` сходится с `dm.daily_traffic_v`
Сравните сумму `visitors` по устройствам за день с `dm.daily_traffic_v`. На
стенде значения совпадут: устройство приписано куке на всю жизнь, поэтому за
день она учитывается только в одной строке. Это свойство мира стенда; см.
раздел «Паспорт куки» в [словаре понятий](../../CONTEXT.md).
Если появятся визиты одной куки с двух устройств, сумма начнет завышать
аудиторию. Проверки счетчиков по категориям этого не обнаружат. Число
посетителей дня берите из `dm.daily_traffic_v`, где оно рассчитано для дня
целиком.
## Типичная ошибка
Повторное выделение визитов из событий может дать другие границы сессий, чем
в DDS. Используйте готовые сессии из `dds.session_v`. Если общее число
событий совпало с источником, а число сессий завышено, вероятная причина —
сборка витрины из событий вместо сессий.
## Как проверить себя
Деловой ключ — `device_category` внутри дня. Получите прямой агрегат из
`dds.session_v` за день. Соедините его с `dm.session_overview_v` полным
внешним соединением по ключу. Посчитайте строки, где ключ отсутствует с
одной из сторон или различается любой счетчик. Расхождений должно быть ноль.
Еще две проверки дневных итогов:
- сумма `sessions` равна числу строк `dds.session_v` за день — без потерь и
повторного учета визитов;
- сумма `events_total` равна сумме `events_total` по сессиям того же дня.
## Приемка
- [ ] созданы пять объектов, донор объявлен через `AS` и шардируется ключом
цели;
- [ ] в `dm.session_overview_v` колонки перечислены явно;
- [ ] источник — `dds.session_v`, повторного выделения визитов нет;
- [ ] физическая таблица не хранит средние и доли;
- [ ] задача `session_overview` в `homework_check` завершилась успешно;
- [ ] задача витрины добавлена в `dm_transform`.