Локальный веб-сервис для технических интервью по дата-инженерному стеку команды. Ядро — интерактивный направленный граф вопросов: каждая нода = вопрос/задача + ответ + оценка; ветвление по теме, сложности и условное (по оценке). Контент импортируется из Markdown и JSON.
Стек: FastAPI + SQLite (бэкенд) · React + Vite + React Flow (фронт). Только локально.
./run.sh # поднимет venv, соберёт фронт (если нужно) и запустит сервер # открыть http://localhost:8000
Пересобрать фронт принудительно: ./run.sh --build.
Два процесса:
# терминал 1 — бэкенд cd backend && . .venv/bin/activate && uvicorn app.main:app --reload --port 8000 # терминал 2 — фронт (Vite проксирует /api на :8000) cd frontend && npm run dev # http://localhost:5173
Раскладка — swimlanes: вертикальная колонка на каждое направление (Фреймворки / Базы данных / Python / Платформа) с заголовком и цветным полупрозрачным фоном; внутри колонки вопросы ранжированы сверху вниз по сложности (base → junior → middle → senior, левая ось).
Блок можно делить на под-колонки по технологиям через поле subblock во frontmatter
(напр. Фреймворки → Airflow / PySpark / dbt / Streaming): под общим заголовком блока
появляются под-колонки с собственными хедерами. Если у нод блока один subblock — блок
остаётся одной колонкой. Порядок под-блоков задаётся в frontend/src/layout.ts (PREFERRED_SUB).
Карточка показывает короткий заголовок (title) и теги — полный текст вопроса/ответа
открывается в drawer. Зависимостей между вопросами (рёбер) нет — это просто доска карточек,
сгруппированная по направлениям и сложности.
- Клик по ноде — открывает drawer (полный текст) и делает её «текущей».
- HUD снизу — текущий вопрос + оценка 1–5 + «Дальше →» (следующий неоцененный вопрос) + «✕».
- Панель фильтров (справа) — переключатели направлений, сложностей и тегов + прогресс
done/total. Активные теги гасят нерелевантные карточки (подсветка/фильтр). - Клавиатура:
1–5— оценить текущий,↑↓— по сложности в колонке,←→— между колонками,Enter— открыть drawer,n— следующий неоцененный,Esc— снять текущий. - Тема — кнопка 🌙/☀️ в шапке переключает светлую/тёмную (запоминается в localStorage; по умолчанию берётся системная). Канва React Flow, контролы и миникарта тоже темнеют.
- 📥 Скачать — выгружает результаты текущей сессии самодостаточным HTML-отчётом
(кандидат, дата, средний балл и охват по блокам, таблицы оценённых вопросов с темой/тегами/
баллами). Открывается в браузере и печатается в PDF. Генерируется на клиенте (
src/report.ts).
Вопросы лежат в content/<блок>/*.md или *.json. Блоки: frameworks, databases,
python, platform. Веса блоков (балансировка) — в content/weights.yaml.
--- id: spark-shuffle-01 kind: question # question | task block: frameworks # frameworks | databases | python | platform subblock: pyspark # (опц.) под-колонка внутри блока title: Shuffle в Spark # короткий заголовок для карточки topic: distributed-batch difficulty: middle # base | junior | middle | senior weight: 13 tags: [spark, optimization] --- ## Вопрос Текст вопроса... ## Ответ Текст ответа (Markdown, поддерживаются блоки кода)...
title— короткий заголовок, отображается на карточке (полный текст — в drawer).tags— отображаются чипами на карточке и доступны для фильтра справа.- Для
kind: taskдоступны поляstarterCode(стартовый код) иrubric(критерии), а тело можно писать через## Задача/## Решение.
Парсинг тела: строки, начинающиеся с
#, трактуются как заголовки-маркеры разбиения на вопрос/ответ. Если в ответе нужен текст, начинающийся с#вне блока кода, — задайтеquestion/answerпрямо во frontmatter (тогда тело не парсится).
| метод | путь | назначение |
|---|---|---|
| GET | /api/graph |
ноды + ошибки импорта |
| GET | /api/weights |
веса блоков |
| POST | /api/interview |
собрать набор вопросов пропорц. весам ({count, difficulties?, seed?}) |
| POST | /api/sessions |
создать сессию кандидата |
| POST | /api/sessions/{id}/score |
выставить оценку ({nodeId, score}) |
| GET | /api/sessions/{id} |
сессия с оценками |
Это ядро. Полный список ручек (кандидаты/интервьюеры, CRUD нод /api/nodes, импорт /api/import,
сравнение сессий /api/sessions/compare, live-обновления по SSE /api/sessions/{id}/events) и схемы —
в Swagger UI на /docs.
Бэкенд (54 теста — test_app.py импорт MD/JSON, sampler, API, сессии; test_nodes.py CRUD нод; test_people.py кандидаты/интервьюеры, изоляция тенантов):
cd backend && . .venv/bin/activate && pytest -q
Фронтенд — headless smoke-тест реального рантайма (граф рендерится, нода → drawer, оценка проставляется и отражается в HUD/прогрессе). Требует запущенного сервера на :8000:
cd frontend && npm run smoke
INTERVIEW_CONTENT_DIR— каталог контента (по умолч../content)INTERVIEW_DB_PATH— путь к SQLite (по умолч.backend/interview.db)INTERVIEW_FRONTEND_DIR— каталог собранного фронта (по умолч.frontend/dist)
Автодеплой на сервер при merge в main (GitHub Actions → SSH). Порт 8800.
Подробности и разовая настройка секретов/ключа — в DEPLOY.md.
См. REPORT.md — отчёт-исследование и архитектурные решения.