# Система тестирования сотрудников клиники Веб-приложение для проведения внутреннего тестирования сотрудников клиники. Руководители подразделений и HR-менеджеры создают тесты и назначают их сотрудникам. Система фиксирует все попытки и результаты. **Версия ТЗ:** 1.2 **Дата:** 2026-03-21 **Статус:** Согласовано **Актуальное состояние кода (не ТЗ, а «что уже есть»):** [docs/PROJECT_STATUS.md](docs/PROJECT_STATUS.md) · [инструкция для проверяющих на dev](docs/DEV_CONTOUR_USER_GUIDE.md) · [кабинет: коротко для врачей/кураторов](docs/РУКОВОДСТВО_КАБИНЕТ_ТЕСТОВ.md). **Спринты мобильного UI (чек-лист для разработки):** [docs/СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md](docs/СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md). **Унификация стека (текущий этап) и слияние с HR-кабинетом (на будущее):** план и журнал — [docs/migration-final.md](docs/migration-final.md). Этап 1 — Express → Flask + React → Jinja **внутри TestingWebApp** (БД остаётся `clinic_tests`). Этап 2 (на будущее) — слияние с `HR_TG_Bot/tgFlaskForm`: [docs/migration-to-tgflaskform.md](docs/migration-to-tgflaskform.md) · [простым языком](docs/migration-to-tgflaskform-plain.md). Карта Express-функционала и справочный gap-analysis с уже существующим модулем HR-кабинета: [docs/migration-final-inventory.md](docs/migration-final-inventory.md). **Заготовка `flask_app/`** (отдельный Flask) больше **не развивается** — выбран сценарий «модуль внутри `tgFlaskForm`». --- ## Стек технологий ### Этот репозиторий (TestingWebApp) | Слой | Технологии | |------|------------| | **Backend** | Node.js (ESM), **Express** 4, **pg**, миграции SQL; аутентификация — cookie + **JWT** (**jsonwebtoken**), пароли **bcryptjs**; опционально вход через HR (`HR_AUTH`, отдельное подключение к БД HR). | | **Frontend** | **React** 18, **React Router** 6, сборка **Vite** 5; статика в проде через Nginx (см. `docker-compose.dev.yml`). | | **Данные** | **PostgreSQL**, отдельная БД **`clinic_tests`**: UUID-ключи, таблицы `tests`, `test_versions`, `questions`, `answer_options`, назначения, попытки (см. `backend/src/db/migrations/`). | | **Прочее** | Извлечение текста из PDF/DOCX (**pdf-parse**, **mammoth**), опционально LLM для черновиков тестов; **dotenv**, **cors**, **multer**. | ### Целевой стек (Flask, как в кабинете / мини-приложении) Тот же класс технологий, что в **`HR_TG_Bot/tgFlaskForm`**: Python, Flask, шаблоны, Postgres. Сейчас допускается **отдельный деплой** нового контура из каталога [`flask_app/`](flask_app/README.md); позже — слияние с полным кабинетом при необходимости. Эталон реализации модуля в монорепозитории HR — общий веб-кабинет **`HR_TG_Bot/tgFlaskForm`**: | Слой | Технологии | |------|------------| | **Приложение** | **Python 3**, **Flask** 3, шаблоны **Jinja2** + **PyPug**, blueprint `/cabinet/testing`; прод-сервер типично **waitress**. | | **Данные** | **SQLAlchemy** 2, **psycopg2**, БД **`hr_bot_test`**: таблицы `testing_*`, связи с **`staff_members`**. | | **Клиент** | HTML-шаблоны кабинета, JS в `webApp/templates/static/js/cabinet/` (без отдельного SPA в этом репозитории). | | **Инфра** | Тот же кластер Postgres, что и у Postgres_TG_Bots / HR (см. раздел установки ниже). | Подробности переноса и миграции данных: [docs/migration-to-tgflaskform.md](docs/migration-to-tgflaskform.md). Скрипт ETL в монорепозитории HR: [`../HR_TG_Bot/tgFlaskForm/tools/migrate_clinic_tests_to_hr.py`](../HR_TG_Bot/tgFlaskForm/tools/migrate_clinic_tests_to_hr.py) (`--dry-run` / `--apply`, переменные `CLINIC_TESTS_URL` и `HR_BOT_URL`). --- ## Содержание - [Стек технологий](#стек-технологий) · [flask_app/ — новый контур](flask_app/README.md) - [Состояние реализации (сводка)](#состояние-реализации-сводка) - [Функциональные возможности](#функциональные-возможности) - [Роли и права доступа](#роли-и-права-доступа) - [Установка и запуск](#установка-и-запуск) - [Данные, сотрудники, интеграция с HR](#данные-сотрудники-интеграция-с-hr) - [Нефункциональные требования](#нефункциональные-требования) - [Вне scope](#вне-scope-не-реализуется-в-данной-версии) --- ## Состояние реализации (сводка) Коротко и по-человечески: [docs/PROJECT_STATUS.md](docs/PROJECT_STATUS.md) (черновики и версии, разбор попыток, список тестов, dev-стенд). Как пользоваться локальным **dev** без чтения кода: [docs/DEV_CONTOUR_USER_GUIDE.md](docs/DEV_CONTOUR_USER_GUIDE.md). --- ## Функциональные возможности ### Управление пользователями и подразделениями - Создание/редактирование/деактивация учётных записей сотрудников - Каждый сотрудник принадлежит одному подразделению - Создание/редактирование справочника подразделений - Назначение роли сотруднику: HR-менеджер / Руководитель подразделения / Сотрудник ### Создание и редактирование тестов **Тест содержит:** - Название теста - Описание (опционально) - Список вопросов (минимум 7) - Порог зачёта — минимальный % правильных ответов - Таймер прохождения — лимит в минутах (опционально) **Вопрос содержит:** - Текст вопроса - Минимум 3 варианта ответа - Один или несколько правильных ответов **Настройки теста:** - Разрешить возврат к предыдущему вопросу: да / нет **Версионирование:** - Автор может редактировать тест пока никто его не проходил - Если тест уже проходили — создаётся новая версия (`version + 1`), старая сохраняется - Все версии теста хранятся; результаты привязаны к конкретной версии - Активная версия — та, которую видят сотрудники; автор может вручную переключить активную версию - Тест можно деактивировать (скрыть из списка, не удалять) ### Назначение теста - Список получателей (отдел или конкретные сотрудники) - Срок сдачи — дата дедлайна - Допустимое количество попыток (1 или более) ### Прохождение теста - На главной странице сотрудник видит список назначенных тестов со статусами: - `Не начат` — ещё не открывал - `В процессе` — начал, не завершил - `Завершён` — сдал/не сдал - `Просрочен` — дедлайн прошёл, не сдан - Если задан таймер — отображается обратный отсчёт, по истечении тест завершается автоматически - Порядок вопросов **случайный** при каждом прохождении - Возможность вернуться к предыдущему вопросу — определяется настройкой теста ### Результаты после завершения теста - Итоговый балл и процент правильных ответов - Факт зачёта: **сдал / не сдал** - Разбор ошибок: по каждому вопросу — его ответ и правильный ответ ### Трекер попыток Единый интерфейс просмотра всех попыток прохождения тестов: - Фильтрация по подразделению, сотруднику, тесту, статусу, результату - Пагинация и сортировка ### AI-помощник Интеграция с LLM для помощи при создании тестов: | Функция | Описание | |---------|----------| | Генерация теста | AI генерирует готовый набор вопросов с вариантами ответов по теме | | Улучшение формулировки | AI переформулирует выбранный вопрос более чётко | | Добавление дистракторов | AI генерирует правдоподобные неправильные варианты ответов | | Проверка качества | AI анализирует весь тест и выдаёт рекомендации | --- ## Роли и права доступа | Роль | Кто | Создаёт тесты | Назначает тесты | Видит результаты | |------|-----|:---:|:---:|:---:| | **HR-менеджер** | Руководитель службы HR, Директор клиники | ✅ | Всем сотрудникам клиники | Всех сотрудников | | **Руководитель подразделения** | Главный врач, рук. службы администраторов | ✅ | Только своему подразделению | Только своего подразделения | | **Сотрудник** | Все остальные работники | ❌ | ❌ | Только свои | --- ## Установка и запуск ### База данных (как в HR_TG_Bot / Postgres_TG_Bots) Используется **тот же** экземпляр PostgreSQL, что и в [Postgres_TG_Bots](../Postgres_TG_Bots) (`docker-compose.dev.yml`, контейнер `hr_postgres_dev`, учётка `hr_bot_user` / сеть `hr_postgres_dev_net` — см. [HR_TG_Bot docker-compose](../HR_TG_Bot/docker-compose.dev.yml)). Схема приложения (таблицы `users`, `tests`, `departments`, …) **не** совмещается с БД `hr_bot_test` — для TestingWebApp заведена отдельная база **`clinic_tests`**. 1. Поднять Postgres из `Postgres_TG_Bots` (и при необходимости внешнюю сеть: `docker network create hr_postgres_dev_net` — как в compose этих репозиториев). 2. Один раз создать базу: `psql "postgresql://hr_bot_user:hrbot123@localhost:5432/postgres" -c "CREATE DATABASE clinic_tests;"` 3. Скопировать `backend/.env.example` в `backend/.env`, при необходимости поправить `DATABASE_URL` (внутри Docker кластера — хост `hr_postgres_dev`, порт `5432`). 4. Миграции: из каталога `backend/`: `npm run migrate`, затем `npm start` (и фронт из `frontend/` — `npm run dev`). **Docker (UI + API + общий Postgres):** поднять `Postgres_TG_Bots` (сеть `hr_postgres_dev_net`), создать БД `clinic_tests`, затем из корня `TestingWebApp`: `docker compose -f docker-compose.dev.yml up --build` — интерфейс **http://localhost:3107** (Nginx проксирует `/api` в backend), API с хоста **http://localhost:3001** (см. [docker-compose.dev.yml](docker-compose.dev.yml), миграции в entrypoint). **Новый Flask-контур** (тот же стек, что кабинет HR): **http://localhost:3108** — сервис `testing-flask`, см. [flask_app/README.md](flask_app/README.md). Локальный `npm run dev` фронта (Vite) — тоже **:3107**, прокси `/api` на **:3001**. В БД `clinic_tests` для локального логина нужен активный `users` с bcrypt-паролем, либо включите `HR_AUTH=1` + `HR_DATABASE_URL` в compose/`.env` (см. `backend/.env.example`). В `backend/.env` задайте `PORT=3001`, если поднимаете API отдельно от compose. `docker compose -f docker-compose.dev.yml down` — остановка. **Без общего кластера** (только отладка): `docker compose --profile standalone up -d` в TestingWebApp — Postgres на **5433**, тогда в `.env` укажите `DATABASE_URL=...localhost:5433/clinic_tests` или `DB_PORT=5433` с `DB_NAME`/`DB_USER` из compose. **Если `npm run migrate` пишет `ECONNREFUSED ...:5433`:** в `backend/.env` нет (или кривой) `DATABASE_URL` на **5432**, и сработал старый `DB_PORT=5433`. Задайте `DATABASE_URL` как в `backend/.env.example` для общего Postgres. ### Данные, сотрудники, интеграция с HR - **Две роли кластера Postgres:** в **`clinic_tests`** — только сущности модуля тестирования (тесты, версии, назначения, попытки, локальные технические учётки при необходимости). В **`hr_bot_test`** (Postgres_TG_Bots / hr_web_viewer) — штат, справочники, существующий **RBAC** и веб-логины. Так мы не смешиваем схемы и не дублируем «источник правды» по людям. - **Сотрудник в процессах** (назначения, дашборды, доступ к результатам) — везде по **`staff_members.id`**. Ссылки в `clinic_tests` храним как **тот же идентификатор** (логическая связь с `staff_members` в `hr_bot_test`); **ФИО, отдел, роли** подтягиваем из HR при отображении или кэшируем по согласованной политике, а не ведём второй кадровый учёт. - **`telegram_id`** в данных сотрудника **не участвует** в бизнес-логике модуля: ни вход, ни проверка прав, ни выбор сотрудника в сценариях, ни фильтрация — только **справочная** информация при необходимости (отображение, история). - **RBAC в перспективе:** единая система разрешений — та, что уже в HR (роли, `staff_role_assignments`, permissions). Модуль тестирования **не** развивает отдельную полную копию матрицы; проверка действий в целевом виде — через **HR** (внутренний API / токен / согласованные запросы к БД). Пока договор и API не готовы — допустимы временные флаги в `clinic_tests`, явно помечаемые как MVP. Детализация задач и варианты A.x: [docs/revision_task/card1.md](docs/revision_task/card1.md). --- ## Нефункциональные требования | Параметр | Значение | |----------|----------| | Количество пользователей | 50–200 человек | | Платформа | Веб-приложение, браузер (desktop-first) | | Доступность | Внутренняя сеть клиники | | Язык интерфейса | Русский | | Время отклика | < 2 секунды | --- ## Вне scope (не реализуется в данной версии) - Интеграция с AD/LDAP - Мобильное приложение - Вопросы с вложениями (изображения, видео) - Экспорт отчётов в Excel / PDF - Уведомления в MAX (отдельный спринт)