4b0d56ff0e
Этап 1 миграции TestingWebApp на целевой стек (Python/Flask/Jinja),
БД остаётся clinic_tests.
E1.0 — База Flask-приложения: SQLAlchemy/psycopg2 пул, Flask sessions,
фабрика create_app, blueprint main с / и /health, base.html в стиле
кабинета HR (Tailwind CDN + Manrope + Material Symbols), 404/500.
E1.1 — Auth + /api/me: Flask sessions (signed cookie) вместо JWT,
bcrypt + Werkzeug, опц. HR_AUTH=1 с UPSERT в clinic_tests.users по
staff_id. UI /login, JSON /api/auth/{login,logout,me}, декораторы
@login_required / @require_role.
E1.2 — Тесты: список + редактор. 10 эндпоинтов, сервисы test_draft,
test_access, test_chain, ai_editor, llm_client, draft_validator,
editor_content. UI /tests (каталог + создание) и /tests/<id>/edit
(редактор с AI). Полный мобильный UX (аккордеоны/drag-n-drop) — в E1.7.
E1.3 — Импорт документов: pypdf + python-docx, эндпоинт
POST /api/tests/import/document, кнопка «Импорт документа» в
AI-панели редактора, лимит 16 МБ.
E1.8 — AI v2: страница /settings (статус ENV-ключа + ping),
ai/generate-by-title (без сетки), ai/check (рецензия), ai/improve
(массовое было→стало с чекбоксами). Унифицированный ответ AI-ошибок:
{ error, code, settingsUrl }.
Docker:
- docker-compose.dev.yml: добавлены DATABASE_URL, HR_AUTH/HR_DATABASE_URL,
DEEPSEEK_API_KEY/OPENAI_API_KEY/LLM_BASE_URL/LLM_MODEL и сеть postgres
для testing-flask.
Документация:
- docs/migration-final.md — двух-этапный план (Этап 1: унификация
стека внутри TestingWebApp; Этап 2: слияние с tgFlaskForm).
- docs/migration-final-inventory.md — карта 22 эндпоинтов Express.
Made-with: Cursor
203 lines
18 KiB
Markdown
203 lines
18 KiB
Markdown
# Система тестирования сотрудников клиники
|
||
|
||
Веб-приложение для проведения внутреннего тестирования сотрудников клиники. Руководители подразделений и 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 (отдельный спринт)
|