refactor(generator): разнесён сервис генератора по src-пакету

- Зачем:
  - перед активными визитами нужно отделить генеративную модель от Kafka, состояния и сервисного цикла.
- Что:
  - перенесены модули генератора в пакет `src/clickstream_generator`.
  - `generator.py` оставлен фасадом и точкой входа с совместимыми импортами.
  - обновлены Dockerfile, тесты, README, спека и issue 02.5.
- Проверка:
  - `docker build -t generator:test generator`.
  - `docker run --rm -v /home/dmitry/sources/clickstream-ch-kafka-superset-demo:/workspace -w /workspace/generator generator:test pytest tests/ -q`.
  - `python -m py_compile generator.py src/clickstream_generator/*.py` в Docker.
This commit is contained in:
Dmitry Dementiev
2026-06-11 15:00:50 +03:00
parent 48e3bf2900
commit 642789d484
19 changed files with 1297 additions and 1189 deletions
+5 -2
View File
@@ -5,8 +5,11 @@ Pytest fixtures для тестирования генератора.
import sys
from pathlib import Path
# Добавляем родительскую директорию в путь
sys.path.insert(0, str(Path(__file__).parent.parent))
GENERATOR_DIR = Path(__file__).parent.parent
# Добавляем фасад generator.py и src-пакет в путь
sys.path.insert(0, str(GENERATOR_DIR))
sys.path.insert(0, str(GENERATOR_DIR / "src"))
import pytest
from generator import Config, EventDictionary
+2 -2
View File
@@ -7,7 +7,7 @@ from dataclasses import replace
from datetime import datetime
import pytest
from generator import EventGenerator, EventDictionary
from generator import EventGenerator, EventDictionary, generate_tick_batch
ALLOWED_PAGE_PATHS = {
@@ -95,7 +95,7 @@ class TestEventGeneration:
"""Тиковый батч набирает целевой бюджет из одного или нескольких визитов."""
generator = EventGenerator(event_dictionary, base_config)
batch = generator.generate_tick_batch(20)
batch = generate_tick_batch(generator, 20)
assert len(batch["browser_events"]) == 20
assert len(batch["location_events"]) == 20
+55
View File
@@ -0,0 +1,55 @@
"""
Контракт уборки сервиса генератора.
"""
from pathlib import Path
import subprocess
import sys
from generator import (
Config,
EventDictionary,
EventGenerator,
GeneratorService,
KafkaPublisher,
GeneratorState,
)
def test_dockerfile_copies_split_python_modules():
"""Контейнерный запуск видит все модули генератора после разбиения."""
dockerfile = (Path(__file__).parent.parent / "Dockerfile").read_text(encoding="utf-8")
assert "COPY src ./src" in dockerfile
assert 'ENV PYTHONPATH="/app/src"' in dockerfile
def test_generator_facade_imports_without_pythonpath():
"""Локальный фасад сам находит src-пакет без внешнего PYTHONPATH."""
generator_dir = Path(__file__).parent.parent
result = subprocess.run(
[sys.executable, "-c", "import generator"],
cwd=generator_dir,
env={"PATH": ""},
text=True,
capture_output=True,
check=False,
)
assert result.returncode == 0, result.stderr
def test_generator_facade_preserves_public_imports_after_split():
"""Старый импорт из generator работает, но классы живут в отдельных модулях."""
assert Config.__module__ == "clickstream_generator.config"
assert EventDictionary.__module__ == "clickstream_generator.dictionary"
assert EventGenerator.__module__ == "clickstream_generator.generation"
assert KafkaPublisher.__module__ == "clickstream_generator.kafka_io"
assert GeneratorState.__module__ == "clickstream_generator.state"
assert GeneratorService.__module__ == "clickstream_generator.service"
def test_tick_batch_is_not_part_of_pure_visit_generation():
"""Временная сборка тика вынесена из чистой модели одного визита."""
assert not hasattr(EventGenerator, "generate_tick_batch")