diff --git a/README.md b/README.md index cb1e22d..c431690 100644 --- a/README.md +++ b/README.md @@ -6,10 +6,13 @@ **Дата:** 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). + +--- + ## Функциональные возможности ### Управление пользователями и подразделениями diff --git a/backend/PROGRESS.md b/backend/PROGRESS.md new file mode 100644 index 0000000..cda3ba8 --- /dev/null +++ b/backend/PROGRESS.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 diff --git a/docs/DEV_CONTOUR_USER_GUIDE.md b/docs/DEV_CONTOUR_USER_GUIDE.md new file mode 100644 index 0000000..6035667 --- /dev/null +++ b/docs/DEV_CONTOUR_USER_GUIDE.md @@ -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, БД, переменные окружения. diff --git a/docs/PROJECT_STATUS.md b/docs/PROJECT_STATUS.md new file mode 100644 index 0000000..f7d4a68 --- /dev/null +++ b/docs/PROJECT_STATUS.md @@ -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) diff --git a/docs/TEST_TABLES_ANALYSIS.md b/docs/TEST_TABLES_ANALYSIS.md new file mode 100644 index 0000000..9858769 --- /dev/null +++ b/docs/TEST_TABLES_ANALYSIS.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) diff --git a/docs/revision_task/BACKLOG.md b/docs/revision_task/BACKLOG.md index d8dd84b..3212022 100644 --- a/docs/revision_task/BACKLOG.md +++ b/docs/revision_task/BACKLOG.md @@ -4,7 +4,8 @@ **Карта больших кусков работ:** [card1.md](card1.md) (версии **V**, документ **D**, авторизация **HR A**). **Идеи и пожелания (простой язык):** [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 (части можно параллелить). **Этап 2 (ТЗ §5)** — дашборды. diff --git a/docs/revision_task/BACKLOG_IDEAS.md b/docs/revision_task/BACKLOG_IDEAS.md index edc4efd..218cc78 100644 --- a/docs/revision_task/BACKLOG_IDEAS.md +++ b/docs/revision_task/BACKLOG_IDEAS.md @@ -2,7 +2,8 @@ *Язык простой, без жаргона разработки. Сюда попадает всё, что всплыло в обсуждениях и ещё не вошло в жёсткое ТЗ.* -**Как пользоваться:** приоритеты и «да/нет» фиксируем отдельно; пункты **не** удаляем — переносим в раздел **Решено** с кратким итогом, если идея закрыта или отклонена. +**Как пользоваться:** приоритеты и «да/нет» фиксируем отдельно; пункты **не** удаляем — переносим в раздел **Решено** с кратким итогом, если идея закрыта или отклонена. +**Что уже в продукте (кратко):** [../PROJECT_STATUS.md](../PROJECT_STATUS.md). --- diff --git a/docs/revision_task/TESTING_JOURNAL.md b/docs/revision_task/TESTING_JOURNAL.md index 3280b48..d070d78 100644 --- a/docs/revision_task/TESTING_JOURNAL.md +++ b/docs/revision_task/TESTING_JOURNAL.md @@ -8,9 +8,13 @@ Ниже в разделе 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 | | 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 | | 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-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… сведём к той же таблице, когда появятся экраны; формулировки шагов вы получите **только** в чате, по одному.* -**Итог спринта 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 | |--------|------------------------|----------|----------| -| 1 | Версии, история прогонов | приём (код в dev) | 2 + очередь S1-02+ | +| 1 | Версии, история прогонов | приём (код в dev) | S1-02…S1-13 ОК; **регресс UI:** S1-14… *(в процессе)* | | 2 | *(по мере появления)* | | | --- *Связанные файлы: [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. diff --git a/docs/revision_task/card1.md b/docs/revision_task/card1.md index 13d0a45..c7ac54d 100644 --- a/docs/revision_task/card1.md +++ b/docs/revision_task/card1.md @@ -36,8 +36,8 @@ | 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.7 | UI автора: номер/метка версии, предупреждение при «после первой попытки», экран **история версий**, кнопка **сменить активную** (с confirm) | Смоук `sprint-01-testing.md` | -| V.8 | UI списки сотрудника/автора: **один** ряд на цепочку, без дублей версий | — | -| V.9 | Интеграционные тесты API + регресс «разбор старой попытки» по старым `question_id` | — | +| V.8 | UI списки сотрудника/автора: **один** ряд на цепочку, без дублей версий | `GET /tests/:id/summary` + упрощённая карточка для не-автора; список `GET /tests` с JOIN на активную версию | +| 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)) | Зафиксировано в ТЗ | --- @@ -87,3 +87,23 @@ - Проверки и журнал: [TESTING_JOURNAL.md](TESTING_JOURNAL.md) - Старый чек-лист: [sprint-01-testing.md](sprint-01-testing.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). diff --git a/docs/revision_task/sprint-01-testing.md b/docs/revision_task/sprint-01-testing.md index bd88e74..d28541a 100644 --- a/docs/revision_task/sprint-01-testing.md +++ b/docs/revision_task/sprint-01-testing.md @@ -2,6 +2,8 @@ **Актуальный ведённый журнал (авто + ручные шаги, ответы ОК/не ОК):** [TESTING_JOURNAL.md](TESTING_JOURNAL.md) — *этот файл остаётся как подробный черновик сценариев*. +**Сводка «что в коде»:** [../PROJECT_STATUS.md](../PROJECT_STATUS.md). + Ниже: **автоматизировано / проверено при разработке** и **ручная приёмка** — дублирует структуру; статусы переносите в `TESTING_JOURNAL.md`. **Окружение:** зафиксировать ветку/коммит, URL стенда, тестовые учётки (роль автора, роль сотрудника). @@ -64,4 +66,3 @@ _Сценарии для прогона в «боевом» темпе, без --- -**Итог приёмки спринта 1:** дата __________, подпись/комментарий _________________________ diff --git a/docs/revision_task/sprint-01.md b/docs/revision_task/sprint-01.md index 0fa2dc8..c487a4e 100644 --- a/docs/revision_task/sprint-01.md +++ b/docs/revision_task/sprint-01.md @@ -10,6 +10,8 @@ **Данные:** БД `clinic_tests` на общем кластере; сотрудник в сценариях — `staff_members.id`; `telegram_id` — только справка; RBAC — из HR. См. [card1 (вступление)](card1.md#хранение-связь-с-сотрудниками-rbac-зафиксировано). +**Текущая реализация (сводка):** [../PROJECT_STATUS.md](../PROJECT_STATUS.md). + --- ## Цель спринта diff --git a/docs/revision_task/sprint-02-testing.md b/docs/revision_task/sprint-02-testing.md new file mode 100644 index 0000000..8d2aa36 --- /dev/null +++ b/docs/revision_task/sprint-02-testing.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:** дата __________, комментарий _________________________ diff --git a/docs/revision_task/sprint-02.md b/docs/revision_task/sprint-02.md new file mode 100644 index 0000000..1a2bcd9 --- /dev/null +++ b/docs/revision_task/sprint-02.md @@ -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`. diff --git a/docs/revision_task/task.md b/docs/revision_task/task.md index 5d1483b..3cd2b1e 100644 --- a/docs/revision_task/task.md +++ b/docs/revision_task/task.md @@ -10,6 +10,8 @@ | Адресат | Константин Л. (разработчик) | | Базовый репозиторий | 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. Контекст и зачем это делается ### Зачем клинике система тестирования diff --git a/docs/ТЗ.md b/docs/ТЗ.md index 7084550..f33ca6f 100644 --- a/docs/ТЗ.md +++ b/docs/ТЗ.md @@ -5,6 +5,8 @@ **Дата:** 2026-03-21 **Статус:** Согласовано +*Реализация в ветке `dev` (не дословно ТЗ):* [PROJECT_STATUS.md](PROJECT_STATUS.md) · [инструкция dev-стенда](DEV_CONTOUR_USER_GUIDE.md). + --- ## 1. Назначение системы diff --git a/docs/шаги/01-project-setup.md b/docs/шаги/01-project-setup.md index a95227e..3740606 100644 --- a/docs/шаги/01-project-setup.md +++ b/docs/шаги/01-project-setup.md @@ -42,3 +42,8 @@ - Работающий сервер с подключением к БД - Структура проекта готова для разработки + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/02-database-design.md b/docs/шаги/02-database-design.md index 5d94596..405a6f8 100644 --- a/docs/шаги/02-database-design.md +++ b/docs/шаги/02-database-design.md @@ -137,3 +137,8 @@ - Созданы все таблицы с связями - Накатанные миграции - Схема БД готова + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/03-auth.md b/docs/шаги/03-auth.md index 11b0116..e7865e5 100644 --- a/docs/шаги/03-auth.md +++ b/docs/шаги/03-auth.md @@ -54,3 +54,8 @@ function requireDepartment(departmentId: string) { - Работающий вход по логину/паролю - Защищённые API роуты - Разграничение прав по ролям + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/04-users-departments.md b/docs/шаги/04-users-departments.md index 3fb5621..446da8e 100644 --- a/docs/шаги/04-users-departments.md +++ b/docs/шаги/04-users-departments.md @@ -47,3 +47,8 @@ - Админка для управления сотрудниками - Справочник подразделений - Назначение ролей сотрудникам + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/05-test-management.md b/docs/шаги/05-test-management.md index 4bb5e99..938925d 100644 --- a/docs/шаги/05-test-management.md +++ b/docs/шаги/05-test-management.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).* diff --git a/docs/шаги/06-test-assignment.md b/docs/шаги/06-test-assignment.md index f3a0390..453eb6c 100644 --- a/docs/шаги/06-test-assignment.md +++ b/docs/шаги/06-test-assignment.md @@ -48,3 +48,8 @@ - Назначение тестов подразделениям или сотрудникам - Ограничение по дедлайну и попыткам - Список назначений для сотрудника + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/07-test-taking.md b/docs/шаги/07-test-taking.md index 9e6d839..b0c0baf 100644 --- a/docs/шаги/07-test-taking.md +++ b/docs/шаги/07-test-taking.md @@ -70,3 +70,8 @@ - Таймер с автозавершением - Сохранение прогресса - Навигация по вопросам + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/08-results-review.md b/docs/шаги/08-results-review.md index d732cd0..dfbf8e1 100644 --- a/docs/шаги/08-results-review.md +++ b/docs/шаги/08-results-review.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).* diff --git a/docs/шаги/09-attempt-tracking.md b/docs/шаги/09-attempt-tracking.md index 37323a8..05c170b 100644 --- a/docs/шаги/09-attempt-tracking.md +++ b/docs/шаги/09-attempt-tracking.md @@ -51,3 +51,8 @@ - Таблица попыток с фильтрами - Ограничение данных по роли - Полная история прохождений + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/10-ai-assistant.md b/docs/шаги/10-ai-assistant.md index ca4d0f1..247177e 100644 --- a/docs/шаги/10-ai-assistant.md +++ b/docs/шаги/10-ai-assistant.md @@ -87,3 +87,8 @@ - AI-генерация вопросов - Улучшение формулировок - Рекомендации по качеству + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/11-settings.md b/docs/шаги/11-settings.md index 871cdfc..fabb385 100644 --- a/docs/шаги/11-settings.md +++ b/docs/шаги/11-settings.md @@ -61,3 +61,8 @@ - Страница `/settings` - Ввод и сохранение API ключа - Проверка подключения к DeepSeek + + +--- + +*Актуальная привязка к коду: [../PROJECT_STATUS.md](../PROJECT_STATUS.md) · индекс шагов: [README.md](README.md).* diff --git a/docs/шаги/README.md b/docs/шаги/README.md new file mode 100644 index 0000000..ea26852 --- /dev/null +++ b/docs/шаги/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).