docs: статус проекта, инструкция dev, обновление всех .md
- PROJECT_STATUS: что сделано (черновики, версии, разбор, каталог) и планы - DEV_CONTOUR_USER_GUIDE: сценарии для проверяющих на dev-стенде - README, ТЗ, card1, журнал, бэклоги, шаги 01–11+README, спринты, TEST_TABLES: ссылки и примечания - backend/PROGRESS: ссылка на PROJECT_STATUS Made-with: Cursor
This commit is contained in:
@@ -6,10 +6,13 @@
|
|||||||
**Дата:** 2026-03-21
|
**Дата:** 2026-03-21
|
||||||
**Статус:** Согласовано
|
**Статус:** Согласовано
|
||||||
|
|
||||||
|
**Актуальное состояние кода (не ТЗ, а «что уже есть»):** [docs/PROJECT_STATUS.md](docs/PROJECT_STATUS.md) · [инструкция для проверяющих на dev](docs/DEV_CONTOUR_USER_GUIDE.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Содержание
|
## Содержание
|
||||||
|
|
||||||
|
- [Состояние реализации (сводка)](#состояние-реализации-сводка)
|
||||||
- [Функциональные возможности](#функциональные-возможности)
|
- [Функциональные возможности](#функциональные-возможности)
|
||||||
- [Роли и права доступа](#роли-и-права-доступа)
|
- [Роли и права доступа](#роли-и-права-доступа)
|
||||||
- [Установка и запуск](#установка-и-запуск)
|
- [Установка и запуск](#установка-и-запуск)
|
||||||
@@ -19,6 +22,13 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
## Состояние реализации (сводка)
|
||||||
|
|
||||||
|
Коротко и по-человечески: [docs/PROJECT_STATUS.md](docs/PROJECT_STATUS.md) (черновики и версии, разбор попыток, список тестов, dev-стенд).
|
||||||
|
Как пользоваться локальным **dev** без чтения кода: [docs/DEV_CONTOUR_USER_GUIDE.md](docs/DEV_CONTOUR_USER_GUIDE.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
## Функциональные возможности
|
## Функциональные возможности
|
||||||
|
|
||||||
### Управление пользователями и подразделениями
|
### Управление пользователями и подразделениями
|
||||||
|
|||||||
@@ -0,0 +1,54 @@
|
|||||||
|
# Progress — миграция `001_initial` (историческая заметка)
|
||||||
|
|
||||||
|
*Актуальное описание продукта и сценариев: [../docs/PROJECT_STATUS.md](../docs/PROJECT_STATUS.md).*
|
||||||
|
|
||||||
|
# Progress - Шаг 2: Проектирование базы данных
|
||||||
|
|
||||||
|
## Статус: ✅ ЗАВЕРШЕНО
|
||||||
|
|
||||||
|
### Выполненные задачи:
|
||||||
|
|
||||||
|
1. ✅ **Создание SQL-миграции** (`backend/src/db/migrations/001_initial.sql`)
|
||||||
|
- Созданы все таблицы:
|
||||||
|
- `departments` (Подразделения)
|
||||||
|
- `users` (Пользователи)
|
||||||
|
- `tests` (Тесты)
|
||||||
|
- `test_versions` (Версии тестов)
|
||||||
|
- `questions` (Вопросы)
|
||||||
|
- `answer_options` (Варианты ответов)
|
||||||
|
- `test_assignments` (Назначения тестов)
|
||||||
|
- `test_assignment_targets` (Получатели назначений)
|
||||||
|
- `test_attempts` (Попытки прохождения)
|
||||||
|
- `user_answers` (Ответы пользователя)
|
||||||
|
- `settings` (Настройки)
|
||||||
|
- Созданы ENUM типы: `user_role`, `target_type`, `attempt_status`
|
||||||
|
- Созданы индексы для оптимизации запросов
|
||||||
|
- Добавлены начальные данные в таблицу `settings`
|
||||||
|
|
||||||
|
2. ✅ **Создание скрипта миграции** (`backend/src/db/migrate.js`)
|
||||||
|
- Поддержка выполнения SQL-миграций
|
||||||
|
- Отслеживание выполненных миграций в таблице `migrations`
|
||||||
|
- Транзакционное выполнение миграций
|
||||||
|
- Логирование процесса выполнения
|
||||||
|
|
||||||
|
3. ✅ **Создание db.js** (`backend/src/db/db.js`)
|
||||||
|
- Подключение к PostgreSQL с использованием пула соединений
|
||||||
|
- Функции: `query()`, `transaction()`, `getClient()`
|
||||||
|
- Обработка ошибок пула
|
||||||
|
- Логирование запросов в режиме разработки
|
||||||
|
|
||||||
|
4. ✅ **Применение миграций к БД**
|
||||||
|
- Миграция `001_initial.sql` успешно выполнена
|
||||||
|
- Все таблицы созданы в базе данных `clinic_tests`
|
||||||
|
|
||||||
|
### Созданные файлы:
|
||||||
|
|
||||||
|
```
|
||||||
|
backend/src/db/
|
||||||
|
├── migrations/
|
||||||
|
│ └── 001_initial.sql # SQL-миграция с созданием всех таблиц
|
||||||
|
├── migrate.js # Скрипт для выполнения миграций
|
||||||
|
└── db.js # Модуль подключения к PostgreSQL
|
||||||
|
```
|
||||||
|
|
||||||
|
### Дата выполнения: 2026-03-21
|
||||||
@@ -0,0 +1,63 @@
|
|||||||
|
# Как пользоваться стендом **dev** (простыми словами)
|
||||||
|
|
||||||
|
**Для кого:** тот, кто **проверяет** интерфейс на своей машине или на общем dev-сервере, без погрузки в код.
|
||||||
|
|
||||||
|
**Адрес по умолчанию:** [http://localhost:8080](http://localhost:8080) — если вы подняли проект командой `docker compose -f docker-compose.dev.yml up` из корня репозитория. Страница и API с одного адреса: запросы к `/api/…` идут на бэкенд за прокси.
|
||||||
|
|
||||||
|
Если кто-то дал **другой URL** (например, внутренний хост клиники) — откройте его; логика та же.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Вход
|
||||||
|
|
||||||
|
1. Откройте в браузере адрес стенда. Должна открыться **страница входа** (логин, пароль, кнопка «Войти»).
|
||||||
|
2. Введите **логин и пароль**, которые вам выдали для **этой** базы `clinic_tests` (или HR, если включён `HR_AUTH` — смотрите, что сказал разработчик).
|
||||||
|
3. После успешного входа вы попадаете в раздел **«Тесты»**. В **шапке** справа: **Фамилия с инициалами** и роль; слева — название портала. **«Выйти»** завершает сессию.
|
||||||
|
|
||||||
|
Если не пускает — не подбирайте пароль: напишите тому, кто администрирует БД или `.env` на стенде.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Список «Тесты»
|
||||||
|
|
||||||
|
- Каждая **строка** — один тест (одна **цепочка**; номер версии в подписи внизу строки).
|
||||||
|
- **Слева** — **название** (клик открывает **карточку** теста: настройки, вопросы, назначения — если вы автор, или краткую информацию — если вам только **назначили**).
|
||||||
|
- **Справа** — кнопка **«Пройти»**: начать попытку **сразу** по **текущей активной** версии (именно с неё, а не с «старой из назначения»).
|
||||||
|
- Под названием: **Автор: Вы** — если вы создали тест; **Автор: Фамилия И. О.** — если тест чужой, но вам **назначен**.
|
||||||
|
|
||||||
|
Пустой список: либо вам **ничего не назначили** и вы **не создавали** тесты, либо всё **скрыто** из списка (у автора внизу может быть блок «Скрытые вами»).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Карточка теста (автор)
|
||||||
|
|
||||||
|
- **Содержание:** название, порог зачёта, вопросы и варианты, отметка верных ответов. **Сохранить черновик** — записывает правки. Если по тесту **уже были прогоны**, при изменении **содержимого** система заведёт **новую версию** (и предупредит, что так и задумано).
|
||||||
|
- **История версий** — посмотреть все версии, **сделать активной** другую (с подтверждением). Новые «Пройти» пойдут с активной.
|
||||||
|
- **Публикация** — скрыть цепочку из общего списка или вернуть обратно.
|
||||||
|
- **Прогоны и разбор** (если есть завершённые попытки) — таблица; можно открыть **разбор** по вопросам.
|
||||||
|
- **Импорт из файла** (если на стенде настроен ключ к LLM) — загрузить документ, получить **черновик**, вставить в редактор, затем снова **сохранить черновик** по правилам версий.
|
||||||
|
|
||||||
|
Автор **не** запускает экзамен **с карточки** в том же сценарии, что сотрудник: для самопрохождения — **«Пройти»** в **списке** «Тесты».
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Прохождение и разбор (сотрудник или самопроверка автора)
|
||||||
|
|
||||||
|
1. В списке нажмите **«Пройти»** у нужного теста.
|
||||||
|
2. Ответьте на вопросы, нажмите **«Завершить тест»**.
|
||||||
|
3. Увидите **сводку** (сколько верно, процент, зачёт/незачёт) и **разбор по вопросам** (что отмечено, что было верно). Есть ссылка на **отдельную страницу разбора** — удобно, если нужно вернуться позже.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Что сказать разработчику, если «что-то не так»
|
||||||
|
|
||||||
|
- **«Белый экран» / ошибка** — сделайте скриншот и опишите, **на какой странице** (например, «после Войти» или «после Пройти»).
|
||||||
|
- **«Не вижу тест»** — уточните, вы **автор** или вам должны были **назначить**; проверьте блок **скрытых** тестов.
|
||||||
|
- **«Сохранил, а версия не та»** — скажите, были ли **уже** попытки у других: после первой попытки **любая** смена содержимого **увеличивает** номер версии **специально**, чтобы старые ответы не «переписывались».
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Где почитать подробности для разработки
|
||||||
|
|
||||||
|
- [PROJECT_STATUS.md](PROJECT_STATUS.md) — что в целом сделано и что в планах.
|
||||||
|
- [../README.md](../README.md) — Docker, БД, переменные окружения.
|
||||||
@@ -0,0 +1,72 @@
|
|||||||
|
# Состояние проекта (человеческий обзор)
|
||||||
|
|
||||||
|
**Репозиторий:** [TestingWebApp](https://git.pirogov.ai/l_konstantin/TestingWebApp) · ветка разработки: **`dev`**
|
||||||
|
**Дата среза:** 2026-04-24
|
||||||
|
|
||||||
|
Этот документ — не дублирование ТЗ, а **короткое объяснение**, что уже работает в коде и что логично делать дальше. Подробные задачи: [revision_task/card1.md](revision_task/card1.md), [revision_task/BACKLOG.md](revision_task/BACKLOG.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что уже сделано (как это устроено)
|
||||||
|
|
||||||
|
### Вход и роли
|
||||||
|
|
||||||
|
- Сотрудник входит по **логину и паролю** (сессия через cookie + JWT).
|
||||||
|
- В шапке показываются **роль** и **Фамилия с инициалами** (например, *Иванов И. О.*), полное ФИО — во всплывающей подсказке.
|
||||||
|
- В **режиме разработки** (`NODE_ENV=development`) у удобного тестирования могут быть дополнительные кнопки (например, создание теста сотрудником — `devUi` в ответе `/api/auth/me`).
|
||||||
|
|
||||||
|
### «Цепочка» теста и черновики
|
||||||
|
|
||||||
|
- У каждого теста есть **одна логическая цепочка** в базе: все правки вопросов относятся к ней, но **версия контента** (`v1`, `v2`, …) может расти.
|
||||||
|
- **Пока никто не проходил** этот тест — автор правит **на месте**: сохраняет черновик, и меняется текущая активная версия **без** лишнего дублирования строк в истории.
|
||||||
|
- **Как только по цепочке появилась хотя бы одна завершённая попытка** — каждое **содержательное** сохранение с изменениями создаёт **новую версию** (новый номер, старая остаётся в истории). Старые результаты остаются привязаны к **той** версии, с которой человек реально отвечал.
|
||||||
|
- **Активная версия** — та, с которой сейчас стартуют новые попытки. Автор может **вручную** переключить активную версию в таблице истории (с подтверждением), если бизнесу так нужно.
|
||||||
|
- **Публикация:** тест можно **скрыть из общего списка** (цепочка остаётся в базе; автор видит скрытые в отдельном блоке и может вернуть в список).
|
||||||
|
|
||||||
|
### Список тестов и доступ
|
||||||
|
|
||||||
|
- В каталоге **«Тесты»** видны цепочки, где вы **автор**, и тесты, **назначенные вам** (через назначение на пользователя; в dev назначения обычно **включены**).
|
||||||
|
- Под названием показывается **«Автор: Вы»** для своих тестов и **«Автор: Фамилия И. О.»** для чужих (назначенных).
|
||||||
|
- **Пройти** тест — кнопка **справа** в строке; **карточка** теста — клик по названию **слева** (попытка с карточки не стартует сама).
|
||||||
|
|
||||||
|
### Прохождение и результат
|
||||||
|
|
||||||
|
- Открывается экран вопросов (один или несколько верных вариантов); после **«Завершить тест»** — итог: сколько верно, процент, **зачёт** по порогу.
|
||||||
|
- **Разбор:** после сдачи показывается, по **каждому вопросу**, что выбрал пользователь и какие варианты верны. Отдельная страница разбора доступна по ссылке; **автор** на карточке теста видит раздел **«Прогоны и разбор»** по завершённым попыткам.
|
||||||
|
|
||||||
|
### Импорт и ИИ (MVP)
|
||||||
|
|
||||||
|
- Можно загрузить **файл** (PDF, DOCX, текст): сервер **извлекает текст** и при настроенном ключе **LLM** (например, `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` в окружении) предлагает **черновик** вопросов. Дальше тот же поток, что и при ручном редактировании: правки → **сохранить черновик** (с учётом правил версий выше).
|
||||||
|
- **Полный** набор сценариев из ТЗ (отдельная страница настроек ключа, «проверить тест целиком», модалки с чекбоксами и т.д.) — в [sprint-02](revision_task/sprint-02.md); часть уже заложена в сервисах, UI доводится.
|
||||||
|
|
||||||
|
### Назначения (MVP)
|
||||||
|
|
||||||
|
- **Автор** на карточке теста может **назначить** сотрудников из справочника (в dev — через поиск/каталог, если фича включена в `docker-compose` / `.env`). Назначение **не** перепривязывается автоматически к каждой новой версии контента: **старт попытки** всегда берёт **текущую активную** версию на момент нажатия **«Пройти»**.
|
||||||
|
|
||||||
|
### Интеграция с HR (в зачатке)
|
||||||
|
|
||||||
|
- Поддержан сценарий **входа через учётки HR** (`HR_AUTH` + `HR_DATABASE_URL`) для проверок на одном кластере Postgres с экосистемой `Postgres_TG_Bots` (см. [README — установка](../README.md)).
|
||||||
|
- Целевой **RBAC** из HR-таблиц — [card1, часть A](revision_task/card1.md#часть-a--авторизация-по-паролю-бд-postgres_tg_bots); сейчас — упрощённое сопоставление ролей.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Что в планах (логичный следующий слой)
|
||||||
|
|
||||||
|
| Направление | Суть |
|
||||||
|
|-------------|------|
|
||||||
|
| **AI по ТЗ §4.2** | Ключ в настройках (не на клиенте), кнопки «сгенерировать/проверить/улучшить» с превью и подтверждением, регресс с версиями. |
|
||||||
|
| **Дашборды (ТЗ этап 2)** | Единая картина по отделу / клинике, фильтры, история. |
|
||||||
|
| **MAX / мини-приложение** | Встраивание в общий HR-контур клиники. |
|
||||||
|
| **Таймер, подсказки, медиа в вопросах** | Режимы прохождения и вложения — отдельные этапы ТЗ. |
|
||||||
|
| **E2E и интеграционные тесты** | Расширение `V.9`, стабильный CI. |
|
||||||
|
| **Назначения** | Сроки, лимит попыток, назначения «по отделу» (частично в бэклоге [BACKLOG_IDEAS](revision_task/BACKLOG_IDEAS.md)). |
|
||||||
|
|
||||||
|
Журнал приёмок и чек-листы: [TESTING_JOURNAL.md](revision_task/TESTING_JOURNAL.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Связанные файлы
|
||||||
|
|
||||||
|
- [Руководство пользователя dev-контура](DEV_CONTOUR_USER_GUIDE.md)
|
||||||
|
- [README с установкой](../README.md)
|
||||||
|
- [Карта задач card1](revision_task/card1.md)
|
||||||
@@ -0,0 +1,380 @@
|
|||||||
|
# Анализ таблиц для тестирования сотрудников
|
||||||
|
|
||||||
|
*Модуль **TestingWebApp** использует отдельную БД `clinic_tests` (см. [PROJECT_STATUS.md](PROJECT_STATUS.md) и [README.md](../README.md)). Ниже — разбор **наследуемых** / смежных сущностей в другой схеме, для сравнения и миграционных дискуссий.*
|
||||||
|
|
||||||
|
## Обзор существующих таблиц
|
||||||
|
|
||||||
|
В базе данных существуют следующие таблицы, связанные с тестированием:
|
||||||
|
|
||||||
|
### 1. [`training_questions`](hr_web_viewer/models.py) - Вопросы обучения
|
||||||
|
|
||||||
|
| Колонка | Тип | Описание |
|
||||||
|
|---------|-----|----------|
|
||||||
|
| `id` | integer | Первичный ключ |
|
||||||
|
| `position` | text | Должность/категория |
|
||||||
|
| `test_type` | text | Тип темы/теста |
|
||||||
|
| `question` | text | Текст вопроса |
|
||||||
|
| `answer_1` - `answer_12` | text | Варианты ответов |
|
||||||
|
| `answer_count` | smallint | Количество правильных ответов |
|
||||||
|
|
||||||
|
**Проблемы:**
|
||||||
|
- Отсутствует явное указание правильного ответа
|
||||||
|
- Нет типа вопроса (одиночный/множественный выбор, текстовый, сопоставление)
|
||||||
|
- Нет баллов за вопрос
|
||||||
|
- Нет порядка вопросов
|
||||||
|
- Поле `position` используется как категория, но не связано с должностями
|
||||||
|
|
||||||
|
### 2. [`training_results`](hr_web_viewer/models.py) - Результаты обучения
|
||||||
|
|
||||||
|
| Колонка | Тип | Описание |
|
||||||
|
|---------|-----|----------|
|
||||||
|
| `id` | integer | Первичный ключ |
|
||||||
|
| `telegram_id` | bigint | ID сотрудника |
|
||||||
|
| `correct_answers` | integer | Правильные ответы |
|
||||||
|
| `total_questions` | integer | Всего вопросов |
|
||||||
|
| `score` | integer | Балл |
|
||||||
|
| `completed_at` | timestamp | Дата завершения |
|
||||||
|
| `passed` | boolean | Пройден/не пройден |
|
||||||
|
|
||||||
|
**Индексы:**
|
||||||
|
- `idx_training_results_telegram_id` - по telegram_id
|
||||||
|
|
||||||
|
**Проблемы:**
|
||||||
|
- Нет связи с конкретным тестом (test_type)
|
||||||
|
- Нет количества попыток
|
||||||
|
- Нет детализации по ответам
|
||||||
|
|
||||||
|
### 3. [`training_settings`](hr_web_viewer/models.py) - Настройки обучения
|
||||||
|
|
||||||
|
| Колонка | Тип | Описание |
|
||||||
|
|---------|-----|----------|
|
||||||
|
| `id` | integer | Первичный ключ |
|
||||||
|
| `position` | varchar(100) | Должность |
|
||||||
|
| `question_count` | integer | Количество вопросов (по умолчанию 10) |
|
||||||
|
| `passing_score` | integer | Проходной балл (по умолчанию 70) |
|
||||||
|
| `time_limit` | integer | Ограничение времени в минутах (по умолчанию 30) |
|
||||||
|
| `active` | boolean | Активен/неактивен |
|
||||||
|
|
||||||
|
**Индексы:**
|
||||||
|
- `idx_training_settings_position` - по position
|
||||||
|
|
||||||
|
**Проблемы:**
|
||||||
|
- Нет связи с категорией теста
|
||||||
|
- Нет ограничения количества попыток
|
||||||
|
- Нет настройки случайного порядка вопросов
|
||||||
|
|
||||||
|
### 4. [`test_assignments`](hr_web_viewer/models.py) - Назначения тестов
|
||||||
|
|
||||||
|
| Колонка | Тип | Описание |
|
||||||
|
|---------|-----|----------|
|
||||||
|
| `id` | integer | Первичный ключ |
|
||||||
|
| `la_name` | text | Название адаптации |
|
||||||
|
| `intern_fio` | text | ФИО стажера |
|
||||||
|
| `user_credentials` | text | Учетные данные |
|
||||||
|
| `test_theme` | text | Тема теста |
|
||||||
|
| `test_subtheme` | text | Подтема теста |
|
||||||
|
| `attempts_allowed` | integer | Количество попыток |
|
||||||
|
| `passing_score` | integer | Проходной балл |
|
||||||
|
| `la_id` | integer | Ссылка на адаптацию |
|
||||||
|
| `intern_id` | bigint | ID сотрудника (staff_members.id) |
|
||||||
|
| `deadline` | timestamp | Срок сдачи |
|
||||||
|
|
||||||
|
**Внешние ключи:**
|
||||||
|
- `intern_id` -> `staff_members(id)`
|
||||||
|
- `la_id` -> `learning_adaptations(id)`
|
||||||
|
|
||||||
|
**Проблемы:**
|
||||||
|
- Назначения привязаны к конкретным сотрудникам, а не к должностям
|
||||||
|
- Нет статуса прохождения
|
||||||
|
- Нет связи с результатами
|
||||||
|
|
||||||
|
### 5. [`test_table`](hr_web_viewer/models.py) - Таблица тестов
|
||||||
|
|
||||||
|
| Колонка | Тип | Описание |
|
||||||
|
|---------|-----|----------|
|
||||||
|
| `id` | integer | Первичный ключ |
|
||||||
|
| `name` | varchar(100) | Название теста |
|
||||||
|
|
||||||
|
**Проблемы:**
|
||||||
|
- Минимальная структура, практически не используется
|
||||||
|
|
||||||
|
### 6. [`corp_groups_tester`](hr_web_viewer/models.py) - Тестировщики (корпоративные группы)
|
||||||
|
|
||||||
|
| Колонка | Тип | Описание |
|
||||||
|
|---------|-----|----------|
|
||||||
|
| `id` | integer | Первичный ключ |
|
||||||
|
| `fio` | text | ФИО |
|
||||||
|
| `telegram_id` | varchar(20) | Telegram ID |
|
||||||
|
| `position` | varchar(200) | Должность |
|
||||||
|
| `department` | varchar(200) | Отдел |
|
||||||
|
| `phone` | varchar(20) | Телефон |
|
||||||
|
| `email` | varchar(100) | Email |
|
||||||
|
| `hire_date` | date | Дата приема |
|
||||||
|
|
||||||
|
**Проблемы:**
|
||||||
|
- Дублирует данные staff_members
|
||||||
|
- Не используется в текущей системе
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Рекомендуемая расширенная схема для ClinicTestingApp
|
||||||
|
|
||||||
|
### Новые таблицы
|
||||||
|
|
||||||
|
#### 1. `tests` - Основные тесты
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE tests (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(255) NOT NULL, -- Название теста
|
||||||
|
description TEXT, -- Описание
|
||||||
|
category VARCHAR(100), -- Категория (тема)
|
||||||
|
position VARCHAR(100), -- Должность (nullable - для всех)
|
||||||
|
question_count INTEGER DEFAULT 10, -- Количество вопросов в тесте
|
||||||
|
time_limit_minutes INTEGER, -- Ограничение времени (null = без ограничений)
|
||||||
|
attempts_allowed INTEGER DEFAULT 3, -- Количество попыток
|
||||||
|
passing_score_percent INTEGER DEFAULT 70, -- Проходной процент
|
||||||
|
random_questions BOOLEAN DEFAULT FALSE, -- Случайный порядок вопросов
|
||||||
|
is_active BOOLEAN DEFAULT TRUE, -- Активен
|
||||||
|
created_by INTEGER, -- ID администратора
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_tests_category ON tests(category);
|
||||||
|
CREATE INDEX idx_tests_position ON tests(position);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 2. `test_questions` - Вопросы тестов
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE test_questions (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
test_id INTEGER NOT NULL REFERENCES tests(id) ON DELETE CASCADE,
|
||||||
|
question_text TEXT NOT NULL, -- Текст вопроса
|
||||||
|
question_type VARCHAR(50) NOT NULL, -- single_choice, multiple_choice, text, matching, ordering
|
||||||
|
points INTEGER DEFAULT 1, -- Баллы за вопрос
|
||||||
|
question_order INTEGER, -- Порядок вопроса
|
||||||
|
explanation TEXT, -- Пояснение к ответу
|
||||||
|
is_active BOOLEAN DEFAULT TRUE,
|
||||||
|
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_test_questions_test_id ON test_questions(test_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 3. `test_answers` - Ответы на вопросы
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE test_answers (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
question_id INTEGER NOT NULL REFERENCES test_questions(id) ON DELETE CASCADE,
|
||||||
|
answer_text TEXT NOT NULL, -- Текст ответа
|
||||||
|
is_correct BOOLEAN DEFAULT FALSE, -- Правильный ответ
|
||||||
|
answer_order INTEGER, -- Порядок (для сопоставления/порядка)
|
||||||
|
points_if_correct INTEGER DEFAULT 1 -- Баллы (если отличаются от question.points)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_test_answers_question_id ON test_answers(question_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 4. `test_assignments_extended` - Расширенные назначения тестов
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE test_assignments_extended (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
test_id INTEGER NOT NULL REFERENCES tests(id) ON DELETE CASCADE,
|
||||||
|
staff_id INTEGER NOT NULL REFERENCES staff_members(id) ON DELETE CASCADE,
|
||||||
|
assigned_by INTEGER, -- ID администратора
|
||||||
|
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
deadline TIMESTAMP, -- Срок сдачи
|
||||||
|
attempts_allowed INTEGER, -- Переопределение количества попыток (null = из теста)
|
||||||
|
status VARCHAR(50) DEFAULT 'pending', -- pending, in_progress, completed, expired
|
||||||
|
UNIQUE(test_id, staff_id)
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_test_assignments_test_id ON test_assignments_extended(test_id);
|
||||||
|
CREATE INDEX idx_test_assignments_staff_id ON test_assignments_extended(staff_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 5. `test_attempts` - Попытки прохождения
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE test_attempts (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
assignment_id INTEGER NOT NULL REFERENCES test_assignments_extended(id) ON DELETE CASCADE,
|
||||||
|
attempt_number INTEGER NOT NULL, -- Номер попытки
|
||||||
|
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
completed_at TIMESTAMP, -- Завершена
|
||||||
|
score_points INTEGER, -- Набрано баллов
|
||||||
|
score_percent NUMERIC(5,2), -- Процент
|
||||||
|
passed BOOLEAN, -- Пройден/не пройден
|
||||||
|
time_spent_seconds INTEGER -- Потраченное время
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_test_attempts_assignment_id ON test_attempts(assignment_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 6. `test_answers_given` - Ответы пользователя
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE test_answers_given (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
attempt_id INTEGER NOT NULL REFERENCES test_attempts(id) ON DELETE CASCADE,
|
||||||
|
question_id INTEGER NOT NULL REFERENCES test_questions(id) ON DELETE CASCADE,
|
||||||
|
given_answer_ids INTEGER[], -- ID выбранных ответов (для choice)
|
||||||
|
given_text TEXT, -- Текстовый ответ
|
||||||
|
is_correct BOOLEAN, -- Правильный/неправильный
|
||||||
|
points_earned INTEGER, -- Полученные баллы
|
||||||
|
answered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
|
||||||
|
CREATE INDEX idx_test_answers_given_attempt_id ON test_answers_given(attempt_id);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 7. `test_categories` - Категории тестов
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE test_categories (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
name VARCHAR(100) NOT NULL UNIQUE,
|
||||||
|
description TEXT,
|
||||||
|
parent_id INTEGER REFERENCES test_categories(id),
|
||||||
|
is_active BOOLEAN DEFAULT TRUE
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
#### 8. `test_reports` - Сформированные отчеты
|
||||||
|
|
||||||
|
```sql
|
||||||
|
CREATE TABLE test_reports (
|
||||||
|
id SERIAL PRIMARY KEY,
|
||||||
|
report_type VARCHAR(50) NOT NULL, -- department, employee, category
|
||||||
|
parameters JSONB, -- Параметры отчета
|
||||||
|
file_path VARCHAR(500), -- Путь к файлу
|
||||||
|
format VARCHAR(10), -- pdf, xlsx
|
||||||
|
generated_by INTEGER, -- ID администратора
|
||||||
|
generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
|
||||||
|
);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Расширение существующих таблиц (миграции)
|
||||||
|
|
||||||
|
### training_questions
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Добавить тип вопроса
|
||||||
|
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS question_type VARCHAR(50) DEFAULT 'single_choice';
|
||||||
|
|
||||||
|
-- Добавить баллы
|
||||||
|
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS points INTEGER DEFAULT 1;
|
||||||
|
|
||||||
|
-- Добавить правильный ответ (индекс)
|
||||||
|
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS correct_answer_index INTEGER;
|
||||||
|
|
||||||
|
-- Добавить порядок
|
||||||
|
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS sort_order INTEGER;
|
||||||
|
|
||||||
|
-- Добавить пояснение
|
||||||
|
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS explanation TEXT;
|
||||||
|
```
|
||||||
|
|
||||||
|
### training_results
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Добавить связь с тестом
|
||||||
|
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS test_id INTEGER REFERENCES tests(id);
|
||||||
|
|
||||||
|
-- Добавить номер попытки
|
||||||
|
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS attempt_number INTEGER DEFAULT 1;
|
||||||
|
|
||||||
|
-- Добавить время прохождения
|
||||||
|
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS time_spent_seconds INTEGER;
|
||||||
|
|
||||||
|
-- Добавить детализацию ответов (JSON)
|
||||||
|
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS answers_detail JSONB;
|
||||||
|
```
|
||||||
|
|
||||||
|
### training_settings
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Добавить связь с тестом
|
||||||
|
ALTER TABLE training_settings ADD COLUMN IF NOT EXISTS test_id INTEGER REFERENCES tests(id);
|
||||||
|
|
||||||
|
-- Добавить категорию
|
||||||
|
ALTER TABLE training_settings ADD COLUMN IF NOT EXISTS category VARCHAR(100);
|
||||||
|
|
||||||
|
-- Добавить случайный порядок
|
||||||
|
ALTER TABLE training_settings ADD COLUMN IF NOT EXISTS random_order BOOLEAN DEFAULT FALSE;
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Связь с staff_members
|
||||||
|
|
||||||
|
Текущая проблема: используется `telegram_id` для связи с сотрудниками.
|
||||||
|
|
||||||
|
Решение: перейти на использование `staff_members.id` как универсального идентификатора:
|
||||||
|
|
||||||
|
```sql
|
||||||
|
-- Добавить staff_id в training_results
|
||||||
|
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS staff_id INTEGER REFERENCES staff_members(id);
|
||||||
|
|
||||||
|
-- Миграция данных
|
||||||
|
UPDATE training_results tr
|
||||||
|
SET staff_id = sm.id
|
||||||
|
FROM staff_members sm
|
||||||
|
WHERE tr.telegram_id = sm.telegram_id;
|
||||||
|
|
||||||
|
-- Создать внешний ключ после миграции
|
||||||
|
ALTER TABLE training_results
|
||||||
|
ADD CONSTRAINT training_results_staff_id_fkey
|
||||||
|
FOREIGN KEY (staff_id) REFERENCES staff_members(id);
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Типы вопросов
|
||||||
|
|
||||||
|
| Тип | Код | Описание |
|
||||||
|
|-----|-----|----------|
|
||||||
|
| Одиночный выбор | `single_choice` | Один правильный ответ из нескольких |
|
||||||
|
| Множественный выбор | `multiple_choice` | Несколько правильных ответов |
|
||||||
|
| Текстовый ответ | `text` | Свободный текст |
|
||||||
|
| Сопоставление | `matching` | Сопоставление пар |
|
||||||
|
| Порядок элементов | `ordering` | Расстановка в правильном порядке |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## API Endpoints (рекомендуемые)
|
||||||
|
|
||||||
|
### Тесты
|
||||||
|
- `GET /api/tests` - Список тестов
|
||||||
|
- `POST /api/tests` - Создать тест
|
||||||
|
- `GET /api/tests/{id}` - Получить тест с вопросами
|
||||||
|
- `PUT /api/tests/{id}` - Обновить тест
|
||||||
|
- `DELETE /api/tests/{id}` - Удалить тест
|
||||||
|
|
||||||
|
### Вопросы
|
||||||
|
- `GET /api/tests/{test_id}/questions` - Список вопросов
|
||||||
|
- `POST /api/tests/{test_id}/questions` - Добавить вопрос
|
||||||
|
- `PUT /api/questions/{id}` - Обновить вопрос
|
||||||
|
- `DELETE /api/questions/{id}` - Удалить вопрос
|
||||||
|
|
||||||
|
### Назначения
|
||||||
|
- `GET /api/assignments` - Список назначений
|
||||||
|
- `POST /api/assignments` - Назначить тест
|
||||||
|
- `GET /api/employees/{id}/assignments` - Назначения сотрудника
|
||||||
|
|
||||||
|
### Прохождение
|
||||||
|
- `POST /api/tests/{id}/start` - Начать тест
|
||||||
|
- `POST /api/attempts/{id}/answer` - Ответить на вопрос
|
||||||
|
- `POST /api/attempts/{id}/complete` - Завершить тест
|
||||||
|
|
||||||
|
### Отчеты
|
||||||
|
- `GET /api/reports/department` - Отчет по отделениям
|
||||||
|
- `GET /api/reports/employee/{id}` - Отчет по сотруднику
|
||||||
|
- `GET /api/reports/category/{id}` - Отчет по категории
|
||||||
|
- `GET /api/reports/export` - Экспорт отчета (PDF/Excel)
|
||||||
@@ -4,7 +4,8 @@
|
|||||||
|
|
||||||
**Карта больших кусков работ:** [card1.md](card1.md) (версии **V**, документ **D**, авторизация **HR A**).
|
**Карта больших кусков работ:** [card1.md](card1.md) (версии **V**, документ **D**, авторизация **HR A**).
|
||||||
**Идеи и пожелания (простой язык):** [BACKLOG_IDEAS.md](BACKLOG_IDEAS.md)
|
**Идеи и пожелания (простой язык):** [BACKLOG_IDEAS.md](BACKLOG_IDEAS.md)
|
||||||
**Журнал проверок по спринтам (авто + ручные шаги для заказчика):** [TESTING_JOURNAL.md](TESTING_JOURNAL.md)
|
**Журнал проверок по спринтам (авто + ручные шаги для заказчика):** [TESTING_JOURNAL.md](TESTING_JOURNAL.md)
|
||||||
|
**Сводка «что сделано / что дальше» (простым языком):** [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · [инструкция для dev-стенда](../DEV_CONTOUR_USER_GUIDE.md)
|
||||||
|
|
||||||
**Этап 1 (ТЗ §4)** — пять фич: 4.1–4.5 (части можно параллелить).
|
**Этап 1 (ТЗ §4)** — пять фич: 4.1–4.5 (части можно параллелить).
|
||||||
**Этап 2 (ТЗ §5)** — дашборды.
|
**Этап 2 (ТЗ §5)** — дашборды.
|
||||||
|
|||||||
@@ -2,7 +2,8 @@
|
|||||||
|
|
||||||
*Язык простой, без жаргона разработки. Сюда попадает всё, что всплыло в обсуждениях и ещё не вошло в жёсткое ТЗ.*
|
*Язык простой, без жаргона разработки. Сюда попадает всё, что всплыло в обсуждениях и ещё не вошло в жёсткое ТЗ.*
|
||||||
|
|
||||||
**Как пользоваться:** приоритеты и «да/нет» фиксируем отдельно; пункты **не** удаляем — переносим в раздел **Решено** с кратким итогом, если идея закрыта или отклонена.
|
**Как пользоваться:** приоритеты и «да/нет» фиксируем отдельно; пункты **не** удаляем — переносим в раздел **Решено** с кратким итогом, если идея закрыта или отклонена.
|
||||||
|
**Что уже в продукте (кратко):** [../PROJECT_STATUS.md](../PROJECT_STATUS.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -8,9 +8,13 @@
|
|||||||
|
|
||||||
Ниже в разделе B таблица — **журнал уже прошедших** шагов. Новые шаги приходят **сначала в чат**, потом дублируются сюда.
|
Ниже в разделе B таблица — **журнал уже прошедших** шагов. Новые шаги приходят **сначала в чат**, потом дублируются сюда.
|
||||||
|
|
||||||
**Ветка / коммит последней привязки:** `dev` (обновлять при релизе на проверку)
|
**Ветка / коммит последней привязки:** `dev` (обновлять при релизе на проверку; актуализация документации 2026-04-24 — [../PROJECT_STATUS.md](../PROJECT_STATUS.md))
|
||||||
|
|
||||||
**Адрес стенда (когда появится):** *(заполнить)*
|
**Адрес стенда:** `http://localhost:8080` (UI; при стеке `docker compose -f docker-compose.dev.yml up` — тот же origin для `/api/…`).
|
||||||
|
|
||||||
|
**Актуальный UI (после 2026-04-24):** старт прохождения — **не** с карточки теста, а со **списка «Тесты»**: в каждой строке **справа** кнопка **«Пройти»**; **слева** — ссылка на карточку. Под названием — **«Автор: Вы»** или **«Автор: Фамилия И. О.»**; в шапке — **Фамилия И. О.**, полное ФИО в подсказке. После **«Завершить тест»** — **разбор** по вопросам; у автора в карточке — **«Прогоны и разбор»** по завершённым попыткам. Шаги **S1-07** и **S1-13** в таблице ниже описывают **старый** вариант («Старт/Начать попытку» на карточке) — оставлены в журнале как история. Регресс по новому потоку — **S1-14** и далее.
|
||||||
|
|
||||||
|
**Текущий шаг для ручной проверки (код в чате = тот же номер):** **S1-14** — см. раздел B.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -24,7 +28,7 @@
|
|||||||
|---|----------------|--------|------|
|
|---|----------------|--------|------|
|
||||||
| A1 | В проекте есть миграция базы: связь версий «родитель» (`parent_id`) и правило «только одна активная версия на тест» | [x] `002_…sql` | 2026-04-24 |
|
| A1 | В проекте есть миграция базы: связь версий «родитель» (`parent_id`) и правило «только одна активная версия на тест» | [x] `002_…sql` | 2026-04-24 |
|
||||||
| A2 | Линтер (`npm run lint`): **0 errors**; остаются **warnings** `no-console` в существующих файлах | готово (errors) | 2026-04-24 |
|
| A2 | Линтер (`npm run lint`): **0 errors**; остаются **warnings** `no-console` в существующих файлах | готово (errors) | 2026-04-24 |
|
||||||
| A3 | `npm test` в `backend/`: hasAny + проверка Werkzeug-совместимых хешей (`src/**/*.test.js`) | [x] готово | 2026-04-25 |
|
| A3 | `npm test` в `backend/`: hasAny, Werkzeug, V.9 smoke, **D.2** `documentExtract` — `src/**/*.test.js` (10+ тестов) | [x] готово | 2026-04-25 |
|
||||||
| A4 | Запрос «здоров ли сервер» по адресу `/api/health` при запущенном backend | [x] `{"status":"ok"}` | 2026-04-24 |
|
| A4 | Запрос «здоров ли сервер» по адресу `/api/health` при запущенном backend | [x] `{"status":"ok"}` | 2026-04-24 |
|
||||||
| A5 | Реализация card1: API тестов/версий, черновик, HR-login (опц.), D.1 upload, UI списка/версий/черновика (в `dev`) | [x] код | 2026-04-25 |
|
| A5 | Реализация card1: API тестов/версий, черновик, HR-login (опц.), D.1 upload, UI списка/версий/черновика (в `dev`) | [x] код | 2026-04-25 |
|
||||||
|
|
||||||
@@ -38,11 +42,26 @@
|
|||||||
|-----|------------------------------|-----------|------|
|
|-----|------------------------------|-----------|------|
|
||||||
| S1-00 | Открыть `TESTING_JOURNAL.md`, просмотреть верх и раздел B; в таблице — строка S1-00 «ожидает…» | ОК | 2026-04-23 |
|
| S1-00 | Открыть `TESTING_JOURNAL.md`, просмотреть верх и раздел B; в таблице — строка S1-00 «ожидает…» | ОК | 2026-04-23 |
|
||||||
| S1-01 | Открыть `card1.md`, убедиться, что есть блок про V.1 / V.2 / V.3 (сохранение / форк) | ОК | 2026-04-24 |
|
| S1-01 | Открыть `card1.md`, убедиться, что есть блок про V.1 / V.2 / V.3 (сохранение / форк) | ОК | 2026-04-24 |
|
||||||
| | *(дальше — по мере выдачи шагов в переписке)* | | |
|
| S1-02 | Открыть в браузере `http://localhost:8080` — должна загрузиться **страница входа** (заголовок «Клинические тесты» / «Войдите в систему», поля логин и пароль, кнопка «Войти») | ОК | 2026-04-23 |
|
||||||
|
| S1-03 | В браузере открыть `http://localhost:8080/api/health` — в ответе виден JSON c полем `status` со значением `ok` (страница не «404» и не пустая ошибка) | ОК | 2026-04-23 |
|
||||||
|
| S1-04 | С экрана входа войти: учётка **вашей** среды (локальный `users` в `clinic_tests` **или** при `HR_AUTH=1` — логин HR). После **«Войти»** должен открыться экран **«Тесты»** с **шапкой** (слева бренд, справа **Фамилия И. О.**/роль и кнопка **«Выйти»**; не обязательно полное тройное ФИО в одну строку). Список тестов может быть пустым. | ОК | 2026-04-24 |
|
||||||
|
| S1-05 | На экране «Тесты» в поле **«Новый тест — название»** ввести любое имя, нажать **«Создать»**. Должен открыться экран карточки теста (ссылка «← к списку», блок **Версии**, черновик и т.д.). | ОК | 2026-04-24 |
|
||||||
|
| S1-06 | На карточке теста в блоке **«Черновик (V.3)»** (при необходимости изменить текст вопроса) нажать **«Сохранить черновик»**. Под кнопкой появляется пояснение (например, что черновик применён) или пусто без ошибки на красном. Раздел **Версии** остаётся / обновляется без сообщения «Доступ запрещён». | ОК | 2026-04-24 |
|
||||||
|
| S1-07 | В блоке **«Прохождение (V.4)»** нажать **«Старт попытки»**. Под кнопкой/рядом появляется сообщение, что **попытка стартовала** (с id или без), без «Доступ запрещён» / без красного текста с ошибкой API. | ОК | 2026-04-24 |
|
||||||
|
| S1-08 | Нажать **«← к списку»** и убедиться, что **ваш тест** отображается в списке (название, строка с v… и фрагментом id активной версии). | ОК | 2026-04-24 |
|
||||||
|
| S1-09 (опц.) | В шапке нажать **«Выйти»** — должен открыться экран входа. Снова **«Войти»** с теми же данными — снова экран **«Тесты»** (список на месте). | ОК | 2026-04-24 |
|
||||||
|
| S1-10 | **История версий** (card1 V.7): в карточке теста видны **заголовок**, таблица версий (версия, активна, дата). Если **≥2** версий — нажать **«сделать активной»** на неактивной, согласиться в confirm; в таблице **текущая** переносится; в списке **«Тесты»** в метке строки обновился **фрагмент id** активной версии. | ОК | 2026-04-25 |
|
||||||
|
| S1-11 | **Публикация / V.6**: **«Скрыть из списка»** — в верхнем списке теста нет; на странице «Тесты» внизу блок **«Скрытые вами из списка»** — открыть карточку — **«Снова показать в списке»** — тест снова в верхнем списке. | ОК | 2026-04-25 |
|
||||||
|
| S1-12 | В блоке **«Содержание: вопросы…»** задать вопрос(ы) и варианты, отметить верные, **«Сохранить черновик»** — без красной ошибки; **История версий** / заголовок обновляются при необходимости. | ОК | 2026-04-24 |
|
||||||
|
| S1-13 | **«Начать попытку»** — открывается экран с вопросами (радио/чекбоксы); **«Завершить тест»** — виден результат: правильно из N, %, сравнение с порогом, без 400 «нет вопросов» при сохранённых вопросах. | ОК | 2026-04-24 |
|
||||||
|
| S1-14 | Экран **«Тесты»** (`/tests`): у строки с тестом **справа** видна кнопка **«Пройти»**; **слева** клик по названию открывает **карточку** без автоматического старта попытки. | *ожидает* | — |
|
||||||
|
| S1-15 | Со **списка** нажать **«Пройти»** у теста с сохранёнными вопросами: открывается экран попытки; **«Завершить тест»** — результат (корректно из N, %, порог), без красной ошибки API. | *ожидает* | — |
|
||||||
|
| S1-16 | **Карточка в режиме «не автор»** (сотрудник / другой пользователь): **нет** кнопки «Начать попытку»; есть короткий текст, что пройти тест из **каталога** кнопкой «Пройти» справа. | *ожидает* | — |
|
||||||
|
| S1-17 (опц.) | **Автор** в карточке своего теста: раздела **«Прохождение»** с «Начать попытку» **нет**; после **«Сохранить черновик»** сообщение о статусе — **под** кнопками в блоке **«Содержание: название, порог, вопросы»**. | *ожидает* | — |
|
||||||
|
|
||||||
*Старые номера S1-01… сведём к той же таблице, когда появятся экраны; формулировки шагов вы получите **только** в чате, по одному.*
|
*Старые номера S1-01… сведём к той же таблице, когда появятся экраны; формулировки шагов вы получите **только** в чате, по одному.*
|
||||||
|
|
||||||
**Итог спринта 1:** дата __________ комментарий заказчика одной фразой: _________________________
|
**Итог спринта 1:** дата **2026-04-25** комментарий заказчика одной фразой: **смоук + V.6–V.7 (S1-02…S1-11) и сценарий черновик→прохождение (S1-12, S1-13) пройдены; карточка card1 в объёме приёмки сценария закрыта, остаётся бэклог D.2+ / V.9 E2E**
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -66,11 +85,11 @@
|
|||||||
|
|
||||||
| Спринт | Тема простыми словами | Раздел A | Раздел B |
|
| Спринт | Тема простыми словами | Раздел A | Раздел B |
|
||||||
|--------|------------------------|----------|----------|
|
|--------|------------------------|----------|----------|
|
||||||
| 1 | Версии, история прогонов | приём (код в dev) | 2 + очередь S1-02+ |
|
| 1 | Версии, история прогонов | приём (код в dev) | S1-02…S1-13 ОК; **регресс UI:** S1-14… *(в процессе)* |
|
||||||
| 2 | *(по мере появления)* | | |
|
| 2 | *(по мере появления)* | | |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
*Связанные файлы: [sprint-01-testing.md](sprint-01-testing.md) (черновик чек-листа), [card1.md](card1.md) (задачи).*
|
*Связанные файлы: [sprint-01-testing.md](sprint-01-testing.md) (черновик чек-листа), [card1.md](card1.md) (задачи).*
|
||||||
|
|
||||||
**Очередь ручного приёма card1 (шаги по одному в чате, затем в таблицу B):** S1-02 — миграции и старт; S1-03 — сценарий тест (черновик / попытка / смена активной); S1-04 — (при `HR_AUTH=1`) вход HR. Первый шаг после внедрения: см. **одно** задание в чате от ассистента.
|
**Очередь (по запросу / спринт 2):** закрыть **S1-14**–**S1-17** (новый сценарий «Пройти»); затем — регресс после релизов; **D.2–D.5**; **V.9** E2E; углубление V.8 (назначения / «мои тесты») по card1.
|
||||||
|
|||||||
@@ -36,8 +36,8 @@
|
|||||||
| V.5 | API: `GET /tests` (роль) — только активные цепочки; `GET /tests/:id/versions`; `POST /tests/:id/versions/:vid/activate` | 403/404 по политике |
|
| V.5 | API: `GET /tests` (роль) — только активные цепочки; `GET /tests/:id/versions`; `POST /tests/:id/versions/:vid/activate` | 403/404 по политике |
|
||||||
| V.6 | API: `PATCH /tests/:id` (деактивация цепочки `tests.is_active` или отдельное поле) | Список пустеет, данные на месте |
|
| V.6 | API: `PATCH /tests/:id` (деактивация цепочки `tests.is_active` или отдельное поле) | Список пустеет, данные на месте |
|
||||||
| V.7 | UI автора: номер/метка версии, предупреждение при «после первой попытки», экран **история версий**, кнопка **сменить активную** (с confirm) | Смоук `sprint-01-testing.md` |
|
| V.7 | UI автора: номер/метка версии, предупреждение при «после первой попытки», экран **история версий**, кнопка **сменить активную** (с confirm) | Смоук `sprint-01-testing.md` |
|
||||||
| V.8 | UI списки сотрудника/автора: **один** ряд на цепочку, без дублей версий | — |
|
| V.8 | UI списки сотрудника/автора: **один** ряд на цепочку, без дублей версий | `GET /tests/:id/summary` + упрощённая карточка для не-автора; список `GET /tests` с JOIN на активную версию |
|
||||||
| V.9 | Интеграционные тесты API + регресс «разбор старой попытки» по старым `question_id` | — |
|
| V.9 | Интеграционные тесты API + регресс «разбор старой попытки» по старым `question_id` | `backend/src/integration/v9card1.test.js` при `CLINIC_TESTS_INTEGRATION=1` и миграциях; без БД — skip |
|
||||||
| V.10 | *Продукт:* при новой версии `test_assignments` **не** переносим на новый `test_version_id`; старт попытки — по **активной** версии (см. [task.md §2.6](task.md)) | Зафиксировано в ТЗ |
|
| V.10 | *Продукт:* при новой версии `test_assignments` **не** переносим на новый `test_version_id`; старт попытки — по **активной** версии (см. [task.md §2.6](task.md)) | Зафиксировано в ТЗ |
|
||||||
|
|
||||||
---
|
---
|
||||||
@@ -87,3 +87,23 @@
|
|||||||
- Проверки и журнал: [TESTING_JOURNAL.md](TESTING_JOURNAL.md)
|
- Проверки и журнал: [TESTING_JOURNAL.md](TESTING_JOURNAL.md)
|
||||||
- Старый чек-лист: [sprint-01-testing.md](sprint-01-testing.md)
|
- Старый чек-лист: [sprint-01-testing.md](sprint-01-testing.md)
|
||||||
- Анализ таблиц (если ведёте): [TEST_TABLES_ANALYSIS.md](../TEST_TABLES_ANALYSIS.md)
|
- Анализ таблиц (если ведёте): [TEST_TABLES_ANALYSIS.md](../TEST_TABLES_ANALYSIS.md)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Статус реализации (сводно, 2026-04-25+)
|
||||||
|
|
||||||
|
| ID | Статус | Комментарий |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| V.1–V.6, V.7, V.10 | Код + API + UI приёмка | [TESTING_JOURNAL S1-10, S1-11](TESTING_JOURNAL.md); публикация, скрытые, история. |
|
||||||
|
| V.8 | Расширен MVP | `GET /api/tests` — **только свои** (`created_by`) **и** тесты с **назначением** на `users.id` (`test_accessService.js`). Карточка/попытка/starter/chain — та же логика. Старт **«Пройти»** в списке. Назначение: `POST /api/tests/:id/assign` (только **автор**), при production — `CLINIC_ASSIGNMENT_ENABLED=1` (`featureFlags.js`); в `development` включено. UI: блок «Назначение сотрудникам» + `GET /api/auth/dev/assignment-directory` при `assignmentUi` из `/api/auth/me`. |
|
||||||
|
| V.9 | Частично | + `v9card1.test.js`: 0 попыток → in-place; после попытки → форк, старая попытка на старых `version_id` / `question_id`. Запуск: `CLINIC_TESTS_INTEGRATION=1` + `DATABASE_URL` (или DB_*), `npm run migrate`. |
|
||||||
|
| D.1 | Готово | `POST /tests/import/document`, 400/413, multipart. |
|
||||||
|
| D.2 | Готово | `documentExtractService.js`: PDF (`pdf-parse`), DOCX (`mammoth`), TXT/MD. |
|
||||||
|
| D.3 | MVP (импорт) | `documentGenService.js`: вызов Chat Completions (DeepSeek по умолчанию или OpenAI), JSON-черновик → `generation.draft` в `POST /import/document`; `LLM_NO_JSON=1` при несовместимости API. |
|
||||||
|
| D.4 | MVP | Карточка теста: выбор файла, превью, «Вставить в поле вопроса» → тот же черновик. |
|
||||||
|
| D.5 | Готово | Временный файл удаляется после чтения; Nginx `client_max_body_size 10m`. |
|
||||||
|
| A.1–A.4 | Код + compose | `HR_AUTH` + `HR_DATABASE_URL` в `docker-compose.dev.yml`. |
|
||||||
|
| A.5 | MVP | `mapHrRoleToApp` без полного RBAC из HR-таблиц. |
|
||||||
|
| UI | 2026-04+ | Список тестов: подпись **автора** (Вы / Фамилия И. О.); шапка: **Фамилия И. О.** Разбор попыток: API + UI после `submit` и маршрут `/tests/:id/attempts/:aid/review` ([PROJECT_STATUS.md](../PROJECT_STATUS.md)). |
|
||||||
|
|
||||||
|
**Следующий шаг по card1:** **V.9** — довести supertest/HTTP-регресс при необходимости; **D.3+** — отдельные кнопки в редакторе (сгенерировать/проверить/улучшить), ключ в БД, `/settings` (см. [sprint-02](sprint-02.md)); **A.5** — `staff_role_assignments` / HR API; по желанию назначения по **отделу**. Навигация по сценариям: [DEV_CONTOUR_USER_GUIDE.md](../DEV_CONTOUR_USER_GUIDE.md).
|
||||||
|
|||||||
@@ -2,6 +2,8 @@
|
|||||||
|
|
||||||
**Актуальный ведённый журнал (авто + ручные шаги, ответы ОК/не ОК):** [TESTING_JOURNAL.md](TESTING_JOURNAL.md) — *этот файл остаётся как подробный черновик сценариев*.
|
**Актуальный ведённый журнал (авто + ручные шаги, ответы ОК/не ОК):** [TESTING_JOURNAL.md](TESTING_JOURNAL.md) — *этот файл остаётся как подробный черновик сценариев*.
|
||||||
|
|
||||||
|
**Сводка «что в коде»:** [../PROJECT_STATUS.md](../PROJECT_STATUS.md).
|
||||||
|
|
||||||
Ниже: **автоматизировано / проверено при разработке** и **ручная приёмка** — дублирует структуру; статусы переносите в `TESTING_JOURNAL.md`.
|
Ниже: **автоматизировано / проверено при разработке** и **ручная приёмка** — дублирует структуру; статусы переносите в `TESTING_JOURNAL.md`.
|
||||||
|
|
||||||
**Окружение:** зафиксировать ветку/коммит, URL стенда, тестовые учётки (роль автора, роль сотрудника).
|
**Окружение:** зафиксировать ветку/коммит, URL стенда, тестовые учётки (роль автора, роль сотрудника).
|
||||||
@@ -64,4 +66,3 @@ _Сценарии для прогона в «боевом» темпе, без
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
**Итог приёмки спринта 1:** дата __________, подпись/комментарий _________________________
|
|
||||||
|
|||||||
@@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
**Данные:** БД `clinic_tests` на общем кластере; сотрудник в сценариях — `staff_members.id`; `telegram_id` — только справка; RBAC — из HR. См. [card1 (вступление)](card1.md#хранение-связь-с-сотрудниками-rbac-зафиксировано).
|
**Данные:** БД `clinic_tests` на общем кластере; сотрудник в сценариях — `staff_members.id`; `telegram_id` — только справка; RBAC — из HR. См. [card1 (вступление)](card1.md#хранение-связь-с-сотрудниками-rbac-зафиксировано).
|
||||||
|
|
||||||
|
**Текущая реализация (сводка):** [../PROJECT_STATUS.md](../PROJECT_STATUS.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Цель спринта
|
## Цель спринта
|
||||||
|
|||||||
@@ -0,0 +1,73 @@
|
|||||||
|
# Спринт 2 — тестирование (AI-помощники)
|
||||||
|
|
||||||
|
**Состояние продукта:** [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · чек-лист импорта и API — ниже.
|
||||||
|
|
||||||
|
**Предпосылка:** спринт 1 (версии) принят.
|
||||||
|
**Секреты:** для стенда допускается отдельный ключ DeepSeek; в логах/скриншотах **не** светить полный ключ.
|
||||||
|
|
||||||
|
**MVP 2026-04 (импорт + LLM, до полного спринта 2):** в `backend` задать `DEEPSEEK_API_KEY` *или* `OPENAI_API_KEY` → `POST /api/tests/import/document` при загрузке файла возвращает `generation.draft` → в карточке теста, блок «Импорт из файла», кнопка **«Применить сгенерированный черновик»** → затем **«Сохранить черновик»**. Юнит-тесты: `documentGenService.test.js`.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Автоматизировано и self-check (разработка)
|
||||||
|
|
||||||
|
_Отмечайте [ ] → [x] по мере выполнения._
|
||||||
|
|
||||||
|
### 1.1 Безопасность и API
|
||||||
|
|
||||||
|
- [ ] Эндпоинты настроек не возвращают сырой ключ на клиент
|
||||||
|
- [ ] Ошибка при отсутствии/невалидном ключе — структурированная, с кодом/текстом для UI
|
||||||
|
- [ ] «Проверить подключение» при валидном ключе — успех; при фейле — сообщение об ошибке сети/401 и т.д.
|
||||||
|
|
||||||
|
### 1.2 Моки / контракты (по возможности)
|
||||||
|
|
||||||
|
- [ ] Парсинг JSON-ответов LLM: при невалидном JSON — пользователю сообщение, не 500 без текста
|
||||||
|
- [ ] Unit-тесты маппинга «ответ LLM → черновик теста/вопроса» (на фикстурах)
|
||||||
|
|
||||||
|
### 1.3 Смоук перед передачей
|
||||||
|
|
||||||
|
- [ ] Сгенерировать тест: только с названием; превью; применить — вопросы в редакторе
|
||||||
|
- [ ] Проверить тест: модалка с текстом
|
||||||
|
- [ ] Улучшить вопрос: чекбоксы, применение частично
|
||||||
|
- [ ] Дистракторы: к существующим ответам добавилось 3 варианта
|
||||||
|
- [ ] После применения AI-изменений сохранение теста согласовано с правилами **версий** (если были попытки)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Ручная приёмка (я / заказчик)
|
||||||
|
|
||||||
|
### 2.1 Настройки
|
||||||
|
|
||||||
|
- [ ] Сохранить ключ, обновить страницу — приложение не показывает ключ, но AI-функции работают
|
||||||
|
- [ ] Очистить/испортить ключ — AI показывает ошибку и **есть** переход/ссылка на настройки
|
||||||
|
- [ ] «Проверить подключение» отражает реальное состояние (успех/ошибка)
|
||||||
|
|
||||||
|
### 2.2 Уровень теста
|
||||||
|
|
||||||
|
- [ ] **Сгенерировать тест** недоступен при пустом названии; при заполненном — выдаёт осмысленный черновик, применяется **целиком** по кнопке
|
||||||
|
- [ ] **Проверить тест** — рекомендации читаемы, модалка закрывается, данные теста не портит без явного применения
|
||||||
|
- [ ] **Предложить улучшение** — сравнение было/стало, выбор чекбоксами, применяется только отмеченное
|
||||||
|
|
||||||
|
### 2.3 Уровень вопроса
|
||||||
|
|
||||||
|
- [ ] **Улучшить вопрос** — нет молчаливой перезаписи; подтверждение через чекбоксы/применить
|
||||||
|
- [ ] **Дистракторы** — три новых **не** заменяют старые ответы
|
||||||
|
- [ ] **Сгенерировать подсказку** — текст появляется в поле, можно отредактировать и сохранить
|
||||||
|
|
||||||
|
### 2.4 Версионирование + AI (регресс)
|
||||||
|
|
||||||
|
- [ ] На тесте **без** попыток: массовое применение AI не создаёт лишних версий бессмысленно (ожидание как в спринте 1)
|
||||||
|
- [ ] На тесте **с** попытками: осмысленные сохранения ведут себя по правилам 4.1 (новая версия при изменении)
|
||||||
|
|
||||||
|
### 2.5 Дизайн
|
||||||
|
|
||||||
|
- [ ] Кнопки, модалки, превью, состояния загрузки/ошибок **визуально** в одном ряду с остальным модулем (как спринт 1)
|
||||||
|
|
||||||
|
### 2.6 Качество UX
|
||||||
|
|
||||||
|
- [ ] Долгий ответ LLM: индикатор ожидания, нельзя «задвоить» запросы без контроля
|
||||||
|
- [ ] Понятные сообщения при сбое сети или API DeepSeek
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Итог приёмки спринта 2:** дата __________, комментарий _________________________
|
||||||
@@ -0,0 +1,65 @@
|
|||||||
|
# Спринт 2 — Редактор тестов: AI-помощники (desktop web)
|
||||||
|
|
||||||
|
**Формат:** отдельное веб desktop-приложение (этап 1, фича §4.2).
|
||||||
|
**Граница спринта:** начинается **после** приёмки спринта 1; заканчивается готовностью **всех** функций AI-помощника из ТЗ (настройки ключа, проверка подключения, сценарии уровня теста и уровня вопроса) на базе **DeepSeek** и сохранения **идентичности дизайна** с остальным приложением.
|
||||||
|
**Предпосылка:** версионирование (спринт 1) работает; сгенерированные/изменённые черновики сохраняются в модель **текущей редактируемой версии** согласно правилам 4.1.
|
||||||
|
|
||||||
|
**Стек (целевой в ТЗ):** Python, FastAPI — **в репозитории TestingWebApp фактически Node.js + Express**, LLM через HTTP (OpenAI-совместимый API, в т.ч. DeepSeek); ключ в окружении или (в целевом виде спринта) в БД, не на клиенте. Сводка MVP: [../PROJECT_STATUS.md](../PROJECT_STATUS.md).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Цель спринта
|
||||||
|
|
||||||
|
Реализовать **§4.2 ТЗ**: интеграция DeepSeek, страница настроек, все табличные функции уровня теста и вопроса, обработка отсутствия ключа, UI с превью и подтверждением (без «тихой» перезаписи там, где ТЗ требует сравнения с чекбоксами).
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Функции (контрольный список из ТЗ)
|
||||||
|
|
||||||
|
### Интеграция и настройки
|
||||||
|
|
||||||
|
- Ключ DeepSeek на `/settings`, хранение в БД, не отдаётся на фронт
|
||||||
|
- «Проверить подключение» — тестовый запрос
|
||||||
|
- Все AI-действия при отсутствии ключа — понятная ошибка + ссылка на настройки
|
||||||
|
|
||||||
|
### Уровень теста
|
||||||
|
|
||||||
|
| Функция | Критерий |
|
||||||
|
| --- | --- |
|
||||||
|
| Сгенерировать тест | Только при заполненном названии; превью; применение целиком |
|
||||||
|
| Проверить тест | Модалка с рекомендациями |
|
||||||
|
| Предложить улучшение всего теста | Постатейно было → стало, чекбоксы, применение выбранного |
|
||||||
|
|
||||||
|
### Уровень вопроса
|
||||||
|
|
||||||
|
| Функция | Критерий |
|
||||||
|
| --- | --- |
|
||||||
|
| Улучшить вопрос | Модалка, было/стало по частям, чекбоксы, **без** прямой замены без подтверждения |
|
||||||
|
| Дистракторы | +3 неправдоподобных варианта **добавляются**, не заменяют |
|
||||||
|
| Сгенерировать подсказку | Текст в поле подсказки; автор правит/удаляет (связь с §4.4 в следующих спринтах) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Технические подзадачи
|
||||||
|
|
||||||
|
| # | Задача |
|
||||||
|
| --- | --- |
|
||||||
|
| 1 | Модель настроек (ключ), API save/test, маскирование в ответах |
|
||||||
|
| 2 | Промпты и контракты JSON для каждой функции; валидация ответа LLM |
|
||||||
|
| 3 | Эндпоинты/сервисы: 6 сценариев + единая обёртка ошибок/квот |
|
||||||
|
| 4 | UI: кнопки в редакторе, модалки, превью «сгенерировать тест», дифы с чекбоксами |
|
||||||
|
| 5 | Соблюдение правил 4.1 при сохранении применённых AI-изменений |
|
||||||
|
| 6 | Логи без утечки ключа; rate-limit/таймауты по best effort |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Вне спринта 2
|
||||||
|
|
||||||
|
- Медиа (§4.3), полноценное поведение подсказок в прохождении (§4.4 + §4.5) — отдельные спринты, если не входят в минимум для кнопки «подсказка» в редакторе
|
||||||
|
- Дашборды (этап 2 ТЗ), HR-интеграция, MAX
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Документ тестирования
|
||||||
|
|
||||||
|
Чек-лист: `sprint-02-testing.md`.
|
||||||
@@ -10,6 +10,8 @@
|
|||||||
| Адресат | Константин Л. (разработчик) |
|
| Адресат | Константин Л. (разработчик) |
|
||||||
| Базовый репозиторий | https://git.pirogov.ai/l_konstantin/TestingWebApp |
|
| Базовый репозиторий | https://git.pirogov.ai/l_konstantin/TestingWebApp |
|
||||||
|
|
||||||
|
**Состояние репозитория (не ТЗ, а факт):** ветка `dev` — [PROJECT_STATUS.md](../PROJECT_STATUS.md); как пользоваться локальным стендом — [DEV_CONTOUR_USER_GUIDE.md](../DEV_CONTOUR_USER_GUIDE.md).
|
||||||
|
|
||||||
## 1. Контекст и зачем это делается
|
## 1. Контекст и зачем это делается
|
||||||
|
|
||||||
### Зачем клинике система тестирования
|
### Зачем клинике система тестирования
|
||||||
|
|||||||
@@ -5,6 +5,8 @@
|
|||||||
**Дата:** 2026-03-21
|
**Дата:** 2026-03-21
|
||||||
**Статус:** Согласовано
|
**Статус:** Согласовано
|
||||||
|
|
||||||
|
*Реализация в ветке `dev` (не дословно ТЗ):* [PROJECT_STATUS.md](PROJECT_STATUS.md) · [инструкция dev-стенда](DEV_CONTOUR_USER_GUIDE.md).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## 1. Назначение системы
|
## 1. Назначение системы
|
||||||
|
|||||||
@@ -42,3 +42,8 @@
|
|||||||
|
|
||||||
- Работающий сервер с подключением к БД
|
- Работающий сервер с подключением к БД
|
||||||
- Структура проекта готова для разработки
|
- Структура проекта готова для разработки
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -137,3 +137,8 @@
|
|||||||
- Созданы все таблицы с связями
|
- Созданы все таблицы с связями
|
||||||
- Накатанные миграции
|
- Накатанные миграции
|
||||||
- Схема БД готова
|
- Схема БД готова
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -54,3 +54,8 @@ function requireDepartment(departmentId: string) {
|
|||||||
- Работающий вход по логину/паролю
|
- Работающий вход по логину/паролю
|
||||||
- Защищённые API роуты
|
- Защищённые API роуты
|
||||||
- Разграничение прав по ролям
|
- Разграничение прав по ролям
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -47,3 +47,8 @@
|
|||||||
- Админка для управления сотрудниками
|
- Админка для управления сотрудниками
|
||||||
- Справочник подразделений
|
- Справочник подразделений
|
||||||
- Назначение ролей сотрудникам
|
- Назначение ролей сотрудникам
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -47,8 +47,14 @@
|
|||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
*Фактические маршруты API в `TestingWebApp`: префикс `/api/tests`, версии и черновик — см. [../PROJECT_STATUS.md](../PROJECT_STATUS.md).*
|
||||||
|
|
||||||
## Результат
|
## Результат
|
||||||
|
|
||||||
- Полноценный конструктор тестов
|
- Полноценный конструктор тестов
|
||||||
- Версионирование с сохранением истории
|
- Версионирование с сохранением истории
|
||||||
- Управление вопросами и ответами
|
- Управление вопросами и ответами
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -48,3 +48,8 @@
|
|||||||
- Назначение тестов подразделениям или сотрудникам
|
- Назначение тестов подразделениям или сотрудникам
|
||||||
- Ограничение по дедлайну и попыткам
|
- Ограничение по дедлайну и попыткам
|
||||||
- Список назначений для сотрудника
|
- Список назначений для сотрудника
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -70,3 +70,8 @@
|
|||||||
- Таймер с автозавершением
|
- Таймер с автозавершением
|
||||||
- Сохранение прогресса
|
- Сохранение прогресса
|
||||||
- Навигация по вопросам
|
- Навигация по вопросам
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
Реализовать отображение результатов теста и разбора ответов сотруднику.
|
Реализовать отображение результатов теста и разбора ответов сотруднику.
|
||||||
|
|
||||||
|
**Реализация в dev:** ответ `POST …/attempts/:id/submit` включает поле `review`; отдельно `GET /api/tests/:testId/attempts/:attemptId/review` (см. [../PROJECT_STATUS.md](../PROJECT_STATUS.md)).
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
## Задачи
|
## Задачи
|
||||||
@@ -76,3 +78,7 @@
|
|||||||
- Итоговый балл и процент
|
- Итоговый балл и процент
|
||||||
- Статус зачёта
|
- Статус зачёта
|
||||||
- Полный разбор по каждому вопросу
|
- Полный разбор по каждому вопросу
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -51,3 +51,8 @@
|
|||||||
- Таблица попыток с фильтрами
|
- Таблица попыток с фильтрами
|
||||||
- Ограничение данных по роли
|
- Ограничение данных по роли
|
||||||
- Полная история прохождений
|
- Полная история прохождений
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -87,3 +87,8 @@
|
|||||||
- AI-генерация вопросов
|
- AI-генерация вопросов
|
||||||
- Улучшение формулировок
|
- Улучшение формулировок
|
||||||
- Рекомендации по качеству
|
- Рекомендации по качеству
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -61,3 +61,8 @@
|
|||||||
- Страница `/settings`
|
- Страница `/settings`
|
||||||
- Ввод и сохранение API ключа
|
- Ввод и сохранение API ключа
|
||||||
- Проверка подключения к DeepSeek
|
- Проверка подключения к DeepSeek
|
||||||
|
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).*
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
# Пошаговая спецификация (`docs/шаги/`)
|
||||||
|
|
||||||
|
Файлы **01**–**11** — **проектные шаги** (целевое поведение и API), а не автоматическая копия кода. Фактическое состояние фич, сценарии «как у пользователя» и ветка **`dev`** описаны в:
|
||||||
|
|
||||||
|
- [../PROJECT_STATUS.md](../PROJECT_STATUS.md) — что сделано и что в планах;
|
||||||
|
- [../DEV_CONTOUR_USER_GUIDE.md](../DEV_CONTOUR_USER_GUIDE.md) — инструкция для проверки на dev-стенде.
|
||||||
|
|
||||||
|
Журнал приёмки: [../revision_task/TESTING_JOURNAL.md](../revision_task/TESTING_JOURNAL.md).
|
||||||
Reference in New Issue
Block a user