fix(generator): сохранена фактура визита при восстановлении

- Зачем:
  - визит после восстановления не должен менять браузер и источники перехода внутри одного click_id.
- Что:
  - добавлен base_click_id в state v3 для восстановления донора фактуры.
  - исправлено восстановление timestamp offset без потери микросекунд.
  - расширены тесты и стыковая проверка browser/source и device/os/geo.
- Проверка:
  - uv run --with-requirements generator/requirements.txt pytest generator/tests -q.
  - bash -n scripts/check_generated_analytics.sh.
  - git diff --cached --check.
This commit is contained in:
2026-07-04 21:46:23 +03:00
parent 0cbfe9b32e
commit e2d06841de
13 changed files with 464 additions and 77 deletions
+7 -3
View File
@@ -10,7 +10,7 @@ from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
logger = logging.getLogger("generator")
STATE_VERSION = "2.0"
STATE_VERSION = "3.0"
def _nested_list_to_tuple(obj):
@@ -74,7 +74,7 @@ def _validate_resume_fields(data: dict) -> None:
raise ValueError("gen_seed must be an integer or null")
def _validate_v2_payload(data: dict) -> None:
def _validate_v3_payload(data: dict) -> None:
_validate_resume_fields(data)
population = data.get("population")
active_visits = data.get("active_visits")
@@ -123,6 +123,7 @@ def _validate_v2_payload(data: dict) -> None:
(
"user_domain_id",
"click_id",
"base_click_id",
"next_index",
"started_at",
"offsets_us",
@@ -132,6 +133,7 @@ def _validate_v2_payload(data: dict) -> None:
)
user_domain_id = visit["user_domain_id"]
click_id = visit["click_id"]
base_click_id = visit["base_click_id"]
offsets = visit["offsets_us"]
page_url_paths = visit["page_url_paths"]
next_index = visit["next_index"]
@@ -139,6 +141,8 @@ def _validate_v2_payload(data: dict) -> None:
raise ValueError(f"active_visits[{index}].user_domain_id is unknown")
if not isinstance(click_id, str) or not click_id:
raise ValueError(f"active_visits[{index}].click_id must be a string")
if not isinstance(base_click_id, str) or not base_click_id:
raise ValueError(f"active_visits[{index}].base_click_id must be a string")
if not isinstance(offsets, list) or not offsets:
raise ValueError(f"active_visits[{index}].offsets_us must be a non-empty list")
if not all(isinstance(offset, int) and offset >= 0 for offset in offsets):
@@ -235,7 +239,7 @@ class GeneratorState:
version,
)
raise ValueError(f"unsupported state version: {version}")
_validate_v2_payload(data)
_validate_v3_payload(data)
model_timestamp = _parse_aware_utc(
data["model_timestamp"],
"model_timestamp",