# План спринтов **Дата:** 2026-03-21 **Статус:** Согласовано --- ## Принцип Каждый спринт — это готовое работающее приложение (frontend + backend), которое можно запустить локально командой `docker compose up` и протестировать вручную в браузере. --- ## Спринт 1 — Инфраструктура + Создание тестов ✅ **Результат:** Поднят весь стек, можно зайти на страницу и создать тест. **Статус:** Завершён и протестирован вручную в браузере. ### Инфраструктура - [x] Структура репозитория: `backend/`, `frontend/`, `nginx/`, `docker-compose.yml` - [x] `docker-compose.yml`: сервисы `db`, `backend`, `frontend`, `nginx` - [x] PostgreSQL: контейнер, volume для данных - [x] FastAPI: контейнер, `GET /api/health` → `{"status": "ok"}` - [x] Alembic: инициализирован, первая миграция (`001_init`) - [x] React + Vite: контейнер, базовая страница открывается в браузере - [x] Nginx: `/` → React SPA, `/api/` → FastAPI ### Создание тестов (без авторизации) - [x] Модели БД: `Test`, `Question`, `Answer` - [x] API: `POST /api/tests` — создать тест с вопросами и ответами - [x] API: `GET /api/tests` — список тестов - [x] API: `GET /api/tests/{id}` — детали теста - [x] Фронт: страница создания теста (название, вопросы, варианты, настройки) - [x] Фронт: список тестов - [x] Фронт: страница просмотра теста **Настройки теста:** порог зачёта (%), таймер (опционально), разрешить возврат к предыдущему вопросу ### Баги, найденные и исправленные при тестировании - [x] `permission denied` на `entrypoint.sh` — volume mount перекрывал `chmod +x` из Dockerfile → исправлено: `CMD ["bash", "entrypoint.sh"]` - [x] `No module named 'app'` в Alembic — Python не видел `/app` → исправлено: `ENV PYTHONPATH=/app` в Dockerfile - [x] `host not found in upstream "backend"` в nginx — nginx резолвил хост при старте, до поднятия backend → исправлено: Docker DNS resolver + `set $backend` - [x] `http://localhost/api/docs` → 404 — FastAPI отдавал docs по `/docs`, а не `/api/docs` → исправлено: явные `docs_url`, `redoc_url`, `openapi_url` в FastAPI --- ## Спринт 2 — Прохождение теста ✅ **Результат:** Можно выбрать тест из списка и пройти его, увидеть результат и разбор ошибок. **Статус:** Завершён и протестирован вручную в браузере. - [x] Модели БД: `TestAttempt`, `AttemptAnswer` - [x] API: `POST /api/attempts` — начать попытку (фиксируем время начала) - [x] API: `POST /api/attempts/{id}/submit` — завершить попытку, подсчитать результат - [x] API: `GET /api/attempts/{id}/result` — результат с разбором ошибок - [x] Фронт: страница прохождения теста - Случайный порядок вопросов - Таймер с обратным отсчётом (если задан) — автосабмит по истечении - Навигация назад (если разрешена настройкой теста) - [x] Фронт: страница результата - Балл и процент - Сдал / Не сдал (относительно порога) - Разбор ошибок: вопрос, ответ сотрудника, правильный ответ - [x] Фронт: кнопка «Пройти тест» прямо в строке таблицы списка тестов ### Доработки после тестирования - [x] Страница теста разделена на два вида: - `/tests/:id` — вид сотрудника: вопросы и варианты ответов без отметок правильных - `/tests/:id/edit` — вид автора: правильные ответы отмечены, жёлтый баннер, кнопка «Редактировать» (задизаблена до Спринта 4) - [x] Список тестов: три кнопки действий заменены на выпадающее меню «⋯» — колонка с названием стала полноширинной ### Баги, найденные и исправленные при тестировании - [x] «Не удалось загрузить тест» × 2 при нажатии «Пройти тест» — миграция `002_attempts` не применилась, т.к. `--reload` перезапускает только код приложения, но не `entrypoint.sh` → исправлено: `docker compose restart backend` --- ## Спринт 3 — Редактирование теста + версионность ✅ **Результат:** Тест можно редактировать. Если тест уже проходили — создаётся новая версия, старая сохраняется для истории. **Статус:** Завершён и протестирован вручную в браузере. ### Backend - [x] Миграция `003`: добавить поле `parent_id` в таблицу `tests` - [x] `PUT /api/tests/{id}` — редактировать тест: - Нет попыток → обновить на месте - Есть попытки → создать новый тест (`version + 1`, `parent_id = id`), вернуть `{test, is_new_version: true}` - [x] `GET /api/tests` — показывать только активные версии (`is_active = True`) - [x] `GET /api/tests/{id}/versions` — цепочка всех версий теста - [x] `POST /api/tests/{id}/activate` — сделать версию активной (деактивирует остальные в цепочке) ### Frontend - [x] Страница `/tests/:id/edit` разделена на режим просмотра и режим редактирования - [x] Форма редактирования с предзаполненными данными (общий компонент `TestForm`) - [x] При сохранении с новой версией — редирект + уведомление «Создана новая версия v2» - [x] Кнопка «← К просмотру теста» в форме редактирования - [x] Секция «История версий»: таблица с версиями, статусом, датой, кнопкой «Сделать активной» - [x] Активная версия — единственная видимая в списке тестов ### Баги, найденные и исправленные при тестировании - [x] `ForeignKeyViolationError` при сохранении — bulk `DELETE questions` не каскадирует на `answers` → исправлено: сначала удаляем `answers`, потом `questions` - [x] Обе версии показывались «Активными» при создании до введения логики деактивации → исправлено: кнопка «Сделать активной» в шапке и в строке таблицы --- ## Спринт 4 — AI-помощник (DeepSeek) **Результат:** При создании и редактировании теста доступен AI-ассистент на базе DeepSeek. Ключ API настраивается через страницу настроек. ### Страница настроек (`/settings`) - [ ] Модель БД: `Setting` (key-value, ключ `deepseek_api_key`) - [ ] Миграция `004` - [ ] API: `GET /api/settings/{key}`, `PUT /api/settings/{key}` - [ ] API: `POST /api/llm/check` — проверить подключение (тестовый запрос к DeepSeek) - [ ] Фронт: страница `/settings` — поле для ввода ключа + кнопка «Проверить подключение» ### AI-функции в форме создания/редактирования теста - [ ] API: `POST /api/llm/generate` — сгенерировать вопросы и ответы по теме - [ ] API: `POST /api/llm/improve` — улучшить формулировку вопроса - [ ] API: `POST /api/llm/distractors` — добавить варианты-дистракторы к вопросу - [ ] API: `POST /api/llm/review` — проверить качество всего теста ### Интеграция в UI - [ ] Кнопка «Сгенерировать с AI» на странице создания теста — вводишь тему, получаешь готовый набор вопросов - [ ] Кнопка «✨» рядом с каждым вопросом — улучшить формулировку - [ ] Кнопка «+ Дистракторы» рядом с каждым вопросом — дополнить неправильные варианты - [ ] Кнопка «Проверить тест» — AI анализирует весь тест и выдаёт рекомендации - [ ] Ссылка на страницу `/settings` в шапке приложения ### Технические детали - DeepSeek API совместим с форматом OpenAI — используем библиотеку `openai` с `base_url=https://api.deepseek.com` - Модель: `deepseek-chat` - Ключ хранится в таблице `settings`, передаётся из бэкенда — фронт ключ не видит --- ## Спринт 5 — Трекер результатов **Результат:** Таблица всех попыток прохождения тестов. - [ ] API: `GET /api/attempts` — все попытки (с фильтрами по тесту, дате) - [ ] Фронт: страница трекера - Таблица: тест, версия, дата начала, дата завершения, результат, зачёт - Фильтрация по тесту и дате - Пагинация --- ## Спринт 6 — Авторизация и управление пользователями **Результат:** Вход по логину/паролю, роли ограничивают доступ. Можно создавать сотрудников и подразделения. ### Авторизация - [ ] Модели БД: `User`, `Department` - [ ] API: `POST /api/auth/login` → JWT access token - [ ] API: `POST /api/auth/logout` - [ ] API: `GET /api/auth/me` - [ ] Middleware: проверка JWT на защищённых эндпоинтах - [ ] Фронт: страница входа - [ ] Фронт: защищённые роуты (редирект на логин если нет токена) ### Роли и права | Роль | Тесты | Трекер | |------|-------|--------| | HR-менеджер / Директор | Создаёт и редактирует все тесты | Вся клиника | | Руководитель подразделения | Создаёт и редактирует свои тесты | Только свой отдел | | Сотрудник | Проходит назначенные тесты | Только свои результаты | ### Управление пользователями - [ ] API: CRUD подразделений - [ ] API: CRUD пользователей (создание, редактирование, деактивация) - [ ] Фронт: страница управления подразделениями (HR) - [ ] Фронт: страница управления сотрудниками (HR / руководитель) ### Назначение тестов - [ ] Модели БД: `TestAssignment` - [ ] API: `POST /api/assignments` — назначить тест (получатели, дедлайн, кол-во попыток) - [ ] Фронт: форма назначения теста - [ ] Фронт: дашборд сотрудника — список назначенных тестов со статусами (`Не начат`, `В процессе`, `Завершён`, `Просрочен`) --- ## Спринт 7 — Уведомления в MAX **Результат:** Сотрудники получают уведомления в мессенджер MAX. - [ ] Изучить документацию MAX API - [ ] Реализовать сервис уведомлений в backend - [ ] Уведомление при назначении теста сотруднику - [ ] Уведомление за N дней до дедлайна (настраивается) - [ ] Поле `max_user_id` в профиле пользователя - [ ] Фронт: в профиле пользователя — поле для MAX ID --- ## Структура репозитория (целевая после Спринта 1) ``` qa_test_app/ ├── backend/ │ ├── app/ │ │ ├── api/ ← роутеры FastAPI │ │ ├── models/ ← SQLAlchemy модели │ │ ├── schemas/ ← Pydantic схемы │ │ ├── services/ ← бизнес-логика │ │ └── main.py │ ├── alembic/ │ ├── Dockerfile │ └── requirements.txt ├── frontend/ │ ├── src/ │ │ ├── api/ ← Axios + TanStack Query │ │ ├── components/ ← переиспользуемые компоненты │ │ ├── pages/ ← страницы │ │ └── main.tsx │ ├── Dockerfile │ └── package.json ├── nginx/ │ └── nginx.conf ├── docker-compose.yml └── DOC/ ```