feat(generator): добавлен связанный визит в генератор

- Зачем:
  - нужен первый проверяемый срез новой модели, где один визит содержит несколько связанных событий.
- Что:
  - добавлен индекс browser-событий по click_id для выбора сид-визита как основы.
  - generate_batch теперь выпускает несколько browser-событий с одним новым click_id и общим device/geo-контекстом.
  - добавлен тест публичного контракта связанного визита.
- Проверка:
  - make generator-test.
This commit is contained in:
Dmitry Dementiev
2026-06-11 11:00:39 +03:00
parent f0370cdb90
commit d38124263a
2 changed files with 69 additions and 5 deletions
+32 -5
View File
@@ -153,12 +153,15 @@ class EventDictionary:
geo_events: list[dict[str, Any]]
# Индексы для быстрого поиска
browser_by_click_id: dict[str, list[dict]] = field(default_factory=dict)
location_by_event_id: dict[str, dict] = field(default_factory=dict)
device_by_click_id: dict[str, dict] = field(default_factory=dict)
geo_by_click_id: dict[str, dict] = field(default_factory=dict)
def __post_init__(self):
# Строим индексы для связности
for browser in self.browser_events:
self.browser_by_click_id.setdefault(browser["click_id"], []).append(browser)
for loc in self.location_events:
self.location_by_event_id[loc["event_id"]] = loc
for dev in self.device_events:
@@ -281,16 +284,40 @@ class EventGenerator:
"geo_events": [],
}
for _ in range(batch_size):
# Выбираем случайное браузерное событие как базу
if batch_size <= 0:
return batch
visit_candidates = [
click_id for click_id, browser_events in self.dictionary.browser_by_click_id.items()
if (
len(browser_events) >= batch_size
and click_id in self.dictionary.device_by_click_id
and click_id in self.dictionary.geo_by_click_id
and all(
event["event_id"] in self.dictionary.location_by_event_id
for event in browser_events[:batch_size]
)
)
]
if visit_candidates:
base_click_id = self.rng.choice(visit_candidates)
base_browser_events = self.dictionary.browser_by_click_id[base_click_id][:batch_size]
else:
# Крайний случай для очень малого сида: сохраняем форму визита,
# даже если приходится брать события с повторением.
base_browser = self.rng.choice(self.dictionary.browser_events)
base_click_id = base_browser["click_id"]
base_browser_events = [base_browser for _ in range(batch_size)]
base_device = self.dictionary.device_by_click_id.get(base_click_id)
base_geo = self.dictionary.geo_by_click_id.get(base_click_id)
new_click_id = self._new_uuid()
for base_browser in base_browser_events:
base_location = self.dictionary.location_by_event_id.get(base_browser["event_id"])
base_device = self.dictionary.device_by_click_id.get(base_browser["click_id"])
base_geo = self.dictionary.geo_by_click_id.get(base_browser["click_id"])
# Генерируем новые ID
new_event_id = self._new_uuid()
new_click_id = self._new_uuid()
new_timestamp = self._current_timestamp()
# Создаём новое браузерное событие