feat(airflow): пульт мира объясняет ось дней в форме и журнале #175

Merged
ddmitry merged 2 commits from 111-world-date into main 2026-08-28 12:16:40 +03:00
3 changed files with 33 additions and 6 deletions

No files matched your search

+5
View File
@@ -262,6 +262,11 @@ uv run --project generator python -m clickstream_generator batch \
`0` — очистка закончена, а начальная генерация ещё не завершена. Работники
начинают играть только с позиции `8` и больше.
В форме запуска можно выбрать только число дней. Первый день работник берет из
`world_position`. Поля «Logical date» и «Run ID» спрятаны в «Advanced Options».
Они нужны самому Airflow и на выбор дня не влияют. Сыгранные дни и новую
позицию работник пишет в журнал шага `remember_played`.
Выключатель создаётся на паузе. Снимите — мир поедет сам; поставите обратно —
встанет на границе модельных суток, доиграв начатый день. Форма пульта и доводы
целиком — [ADR 0009](docs/adr/0009-world-control.md).
+25 -3
View File
@@ -28,6 +28,7 @@
from __future__ import annotations
import datetime
import logging
import os
from airflow.exceptions import AirflowException
@@ -141,7 +142,19 @@ def _ingest_orders() -> TriggerDagRunOperator:
tags=TAGS,
params={
"days": Param(
1, type="integer", minimum=1, maximum=7, title="Сколько дней прожить"
1,
type="integer",
minimum=1,
maximum=7,
title="Сколько дней прожить",
# description_md рендерится подсказкой прямо в форме запуска
# (params.rst, Airflow 3.3), чтобы правило было видно сразу.
description_md=(
"Форма задает только число дней. Номер первого несыгранного "
"дня работник берет из переменной `world_position` (Admin → "
"Variables). Поля Logical date и Run ID нужны самому Airflow "
"и на выбор дня не влияют."
),
)
},
)
@@ -149,14 +162,20 @@ def world_next_day():
"""Прожить следующие дни пачкой, без пауз.
Запускается руками. День по умолчанию один, но разгон вперёд идёт одним
нажимом, а не десятью: сколько дней играть — параметр запуска.
нажимом, а не десятью: сколько дней играть — параметр запуска. Первый
день работник берет из `world_position`. Логическая дата прогона на выбор
дня не влияет.
"""
@task
def remember_played(first_day: int) -> None:
"""Позиция ставится по сыгранным дням и только по успеху."""
days = get_current_context()["params"]["days"]
Variable.set(WORLD_POSITION, str(first_day + days))
position = first_day + days
Variable.set(WORLD_POSITION, str(position))
logging.info(
"дни %s–%s сыграны; новая позиция — %s", first_day, position - 1, position
)
first_day = first_unplayed_day()
played = _generator(
@@ -196,12 +215,15 @@ def world_live_day():
Ускорение ×60: модельные сутки укладываются примерно в двадцать четыре
реальные минуты, и суточная волна разворачивается на глазах. Запускается
руками; чтобы мир жил так день за днём сам, есть выключатель `world_live`.
Следующий день работник берет из `world_position`. Логическая дата прогона
на выбор дня не влияет.
"""
@task
def remember_played(first_day: int) -> None:
"""Позиция ставится по сыгранному дню и только по успеху."""
Variable.set(WORLD_POSITION, str(first_day + 1))
logging.info("сыгран день %s; новая позиция — %s", first_day, first_day + 1)
first_day = first_unplayed_day()
played = _generator("play_day", ["live", "--day", PLAYED_DAY])
+3 -3
View File
@@ -137,9 +137,9 @@ ODS и ещё не собранный в DDS, затем при необходи
## Не входит
- Партиционная переобработка — переиграть произвольный прошлый день или
диапазон, включая форму необязательных параметров того же дага, — этап 5
(#7); туда же стык #111 про дату модельного мира и `logical_date`.
- Инкрементальный ETL вместо пересборки — техдолг #2.
диапазон, включая форму необязательных параметров того же дага.
- Инкрементальный ETL вместо пересборки отклонен (#2, wontfix). Идея отдельной
лабы на эту тему — #149.
- Состав витрин `dm_transform` и проверок `dq_check` —
[дока слоя DM](dm.md).