fc684e7c7d
- Миграция 005: user_id в test_attempts (дефолт 1 = Гость) - GET /api/attempts с фильтрами по тесту, дате и пагинацией - Страница /tracker: таблица попыток, фильтры, пагинация - Ссылка «Трекер» в шапке приложения Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
236 lines
16 KiB
Markdown
236 lines
16 KiB
Markdown
# План спринтов
|
||
|
||
**Дата:** 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`)
|
||
- [x] Модель БД: `Setting` (key-value, ключ `deepseek_api_key`)
|
||
- [x] Миграция `004`
|
||
- [x] API: `GET /api/settings/{key}`, `PUT /api/settings/{key}`
|
||
- [x] API: `POST /api/llm/check` — проверить подключение (тестовый запрос к DeepSeek)
|
||
- [x] Фронт: страница `/settings` — поле для ввода ключа + кнопка «Проверить подключение»
|
||
|
||
### AI-функции в форме создания/редактирования теста
|
||
- [x] API: `POST /api/llm/generate` — сгенерировать вопросы и ответы по теме
|
||
- [x] API: `POST /api/llm/improve` — улучшить формулировку вопроса
|
||
- [x] API: `POST /api/llm/distractors` — добавить варианты-дистракторы к вопросу
|
||
- [x] API: `POST /api/llm/review` — проверить качество всего теста
|
||
|
||
### Интеграция в UI
|
||
- [x] Кнопка «Сгенерировать с AI» над списком вопросов — ввод темы → превью → «Применить все вопросы»
|
||
- [x] Кнопка «Улучшить» в шапке каждого вопроса — заменяет формулировку AI-версией
|
||
- [x] Кнопка «Дистракторы» в шапке каждого вопроса — добавляет 3 новых неправильных варианта
|
||
- [x] Кнопка «Проверить тест» — AI анализирует весь тест и выдаёт рекомендации в модале
|
||
- [x] Ссылка «Настройки» в шапке приложения (новый Layout с AppHeader)
|
||
|
||
### Технические детали
|
||
- DeepSeek API совместим с форматом OpenAI — используем библиотеку `openai` с `base_url=https://api.deepseek.com`
|
||
- Модель: `deepseek-chat`
|
||
- Ключ хранится в таблице `settings`, передаётся из бэкенда — фронт ключ не видит
|
||
- `response_format={"type": "json_object"}` для generate, distractors, improve, improve_all — гарантирует структурированный ответ
|
||
|
||
### Доработки после тестирования
|
||
- [x] «Сгенерировать с AI»: убран вопрос про тему — используется название теста; кнопка задизаблена пока название не заполнено
|
||
- [x] «Улучшить»: открывает модал с постатейным сравнением (вопрос + каждый ответ) и галочками вместо прямой замены
|
||
- [x] «Проверить тест»: добавлена кнопка «Предложить вариант» — вызывает `POST /api/llm/improve_all`, показывает сравнение всего теста с галочками
|
||
|
||
---
|
||
|
||
## Спринт 5 — Трекер результатов ✅
|
||
|
||
**Результат:** Таблица всех попыток прохождения тестов.
|
||
**Статус:** Завершён и протестирован вручную в браузере.
|
||
|
||
- [x] Миграция `005`: поле `user_id` в `test_attempts` (дефолт 1 = «Гость»)
|
||
- [x] API: `GET /api/attempts` — все попытки (с фильтрами по тесту, дате, пагинацией)
|
||
- [x] Фронт: страница `/tracker`
|
||
- Таблица: сотрудник, тест + версия, дата начала, дата завершения, результат, зачёт
|
||
- Фильтрация по тесту и диапазону дат
|
||
- Пагинация (20 записей на страницу)
|
||
- [x] Ссылка «Трекер» в шапке приложения
|
||
|
||
**Примечание:** `user_id = 1` («Гость») — временно до Спринта 6 (авторизация).
|
||
|
||
---
|
||
|
||
## Спринт 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/
|
||
```
|