# Система тестирования сотрудников клиники
Веб-приложение для проведения внутреннего тестирования сотрудников клиники.
Руководители подразделений и HR-менеджеры создают тесты и назначают их
сотрудникам. Все попытки и результаты сохраняются.
- **Прод:** **[https://edullm.pirogov.ai/](https://edullm.pirogov.ai/)**
- **Ветка разработки:** `dev`
- **ТЗ:** v1.2 от 2026-03-21 (статус — согласовано)
---
## Стек и состояние
**Целевой и единственный рабочий стек** — Python 3.11 + Flask 3 +
Jinja2 + Tailwind CDN + SQLAlchemy / psycopg2, код в
[`flask_app/`](flask_app/). На нём работает и прод, и dev (кабинетный UI,
порт **:3107** в Docker, см. ниже).
Старые каталоги `backend/` (Node.js / Express) и `frontend/`
(React + Vite) — **архив**: не разворачиваются, в `docker-compose.dev.yml`
поднимается сервис **`testing-flask`**, удаление папок запланировано
в спринте **E1.6**. Использовать их не надо, миграции и SQL-схема
сохранены в `backend/src/db/migrations/` исключительно как источник
структуры БД.
БД — **`clinic_tests`** (PostgreSQL, UUID-ключи). В Этапе 1 схема
не меняется.
**Этап 2** — слияние с общим HR-кабинетом `HR_TG_Bot/tgFlaskForm` —
запланирован на будущее, сейчас не делается. План:
[`docs/migration-to-tgflaskform.md`](docs/migration-to-tgflaskform.md)
([простыми словами](docs/migration-to-tgflaskform-plain.md)).
---
## Интерфейс (кабинет)
Единственный вариант UI — **как у основного HR-веба**: в
[`base.html`](flask_app/app/templates/base.html) корень
`cabinet-app` → шапка `cabinet-header` → контент `cabinet-main`; на
`
` всегда класс **`ui-legacy`**, стили в [`app.css`](flask_app/app/static/css/app.css)
с префиксом **`body.ui-legacy`** (primary/teal, `.btn`, `.surface-card`,
`legacy-list-shell`, `test-detail-page` и т.д.).
В [`docker-compose.dev.yml`](docker-compose.dev.yml) один сервис
**`testing-flask`** (`container_name: testing_webapp_flask`), порт **3107**.
---
## Что уже работает на новом (Flask) контуре
E1.0–E1.3 и E1.8 закрыты. Чек-лист и журнал —
[`docs/migration-final.md`](docs/migration-final.md).
- **Авторизация** через куки-сессию Flask: bcrypt (локальные пользователи
`clinic_tests.users`) или Werkzeug-хеши при включённом `HR_AUTH=1`
(UPSERT в `clinic_tests.users` по `staff_id` из `hr_bot_test`).
UI: `/login`, JSON: `/api/auth/{login,logout,me}`.
- **Каталог тестов** `/tests` (видны активные + блок «Скрытые вами»),
создание теста через модалку.
- **Редактор** `/tests//edit`: правка названия/описания/проходного
балла, добавление/удаление/перемещение вопросов и вариантов,
переключатель «Цепочка активна», авто-форк новой версии при правке
после первой попытки.
- **AI-помощник** в редакторе:
- «По названию» — генерация всего теста по теме (количество вопросов
и вариантов задаёт автор);
- «По текущей сетке» — генерация по уже расставленным карточкам;
- «Проверить» — рецензия теста с вердиктом и разделами рекомендаций;
- «Улучшить» — массовое «было → стало» с чекбоксами;
- «AI: вопрос/переформулировать» — на отдельной карточке вопроса.
- **Импорт документа** в редакторе: PDF / DOCX / TXT / MD до 16 МБ,
через `pypdf` и `python-docx` → AI-черновик.
- **Настройки** `/settings` — статус общего LLM-ключа из ENV (DeepSeek
или OpenAI-совместимый), кнопка «Проверить подключение».
Подробная инструкция для тестировщика (только UI, без консоли) —
[`docs/QA-versioning-and-ai.md`](docs/QA-versioning-and-ai.md).
## Что ещё не реализовано
| Спринт | Что включает |
|---|---|
| **E1.4** — Назначение и прохождение | Назначить тест сотруднику, экран прохождения, экран результата с разбором ошибок. |
| **E1.5** — Трекер и настройки модуля | Единый список попыток с фильтрами, страница настроек цепочки. |
| **E1.6** — Cutover внутри репозитория | Удаление `backend/` и `frontend/`, чистка `docker-compose.dev.yml` от архивных Node/React-сервисов. |
| **E1.7** — UX-полировка редактора | 4 аккордеона (Шапка / AI / Вопросы / Действия) и drag-n-drop из [Спринта 3](docs/СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md). |
---
## Установка и запуск
### Предпосылка: общий Postgres
Используется **тот же** PostgreSQL, что и в
[Postgres_TG_Bots](../Postgres_TG_Bots) (контейнер `hr_postgres_dev`,
сеть `hr_postgres_dev_net`, учётка `hr_bot_user`).
```bash
# (один раз) создать базу
psql "postgresql://hr_bot_user:hrbot123@localhost:5432/postgres" \
-c "CREATE DATABASE clinic_tests;"
# (один раз) внешняя сеть, если ещё не создана соседом
docker network create hr_postgres_dev_net || true
```
### Dev-стенд
```bash
docker compose -f docker-compose.dev.yml up -d --build
```
| Что | URL |
|---|---|
| Приложение (Flask) | |
| Health-check | |
`docker-compose.dev.yml` пробрасывает в контейнер **`testing-flask`**:
- `DATABASE_URL` (по умолчанию на контейнерный Postgres `clinic_tests`);
- `HR_AUTH=1` / `HR_DATABASE_URL` по умолчанию — вход через HR-кабинет;
- `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` / `LLM_BASE_URL` / `LLM_MODEL` —
для AI-функций. Достаточно положить ключ в корневой `.env` репозитория.
### Локально без Docker
См. [`flask_app/README.md`](flask_app/README.md) — `venv` +
`pip install -r requirements.txt` + `python run.py`.
---
## Данные и интеграция с HR
- **Две роли кластера Postgres.** В **`clinic_tests`** — только сущности
модуля тестирования (тесты, версии, назначения, попытки, локальные
технические учётки). В **`hr_bot_test`** (Postgres_TG_Bots /
hr_web_viewer) — штат, справочники, RBAC и веб-логины. Схемы не
смешиваем, второй кадровый учёт в `clinic_tests` не ведём.
- **Сотрудник** во всех бизнес-процессах — по
**`staff_members.id`** из `hr_bot_test`. В `clinic_tests` храним тот же
идентификатор; ФИО / отдел / роли подтягиваем из HR при отображении.
- **`telegram_id` сотрудника** в бизнес-логике модуля **не участвует**
(ни вход, ни проверка прав, ни выбор сотрудника, ни фильтрация).
- **Целевой RBAC** — единая система разрешений HR
(`staff_role_assignments`, `permissions`). Модуль тестирования
не дублирует матрицу; пока единый API не готов — в `clinic_tests`
допустимы временные флаги, явно помеченные как MVP.
- **`HR_AUTH=1`**: в Flask-контуре включает вход через `hr_bot_test.users`
(Werkzeug-хеши) с UPSERT в `clinic_tests.users`. См.
[`flask_app/.env.example`](flask_app/.env.example).
---
## Роли и права (по ТЗ)
| Роль | Кто | Создаёт тесты | Назначает | Видит результаты |
|---|---|:---:|:---:|:---:|
| **HR-менеджер** | Руководитель HR, директор | ✅ | Всем | Всех |
| **Руководитель подразделения** | Главврач, рук. отделения | ✅ | Только своему подразделению | Только своего подразделения |
| **Сотрудник** | Все остальные | ❌ | ❌ | Только свои |
> На текущем Flask-контуре (E1.0–E1.3, E1.8) проверяется только
> `@login_required`; разделение по ролям задействуется на E1.4–E1.5.
---
## Нефункциональные требования
| Параметр | Значение |
|---|---|
| Количество пользователей | 50–200 человек |
| Платформа | Веб, браузер; mobile-friendly |
| Доступность | Внутренняя сеть клиники |
| Язык интерфейса | Русский |
| Время отклика | < 2 секунды |
## Вне scope (в текущей версии не делаем)
- Интеграция с AD / LDAP.
- Нативное мобильное приложение.
- Вопросы с вложениями (картинки, видео).
- Экспорт отчётов в Excel / PDF.
- Уведомления в MAX (отдельный спринт).
---
## Документация
| Файл | О чём |
|---|---|
| [`docs/PROJECT_STATUS.md`](docs/PROJECT_STATUS.md) | Что работает «прямо сейчас», что в работе, что в бэклоге. |
| [`docs/migration-final.md`](docs/migration-final.md) | Главный трекер миграции: спринты Этапа 1, журнал, критерии готовности. |
| [`docs/migration-final-inventory.md`](docs/migration-final-inventory.md) | Карта 22 эндпоинтов Express + gap-analysis с `tgFlaskForm`. |
| [`docs/migration-to-tgflaskform.md`](docs/migration-to-tgflaskform.md) | План Этапа 2 (слияние с HR-кабинетом). |
| [`docs/QA-versioning-and-ai.md`](docs/QA-versioning-and-ai.md) | Инструкция для тестировщика — только через сайт. |
| [`docs/СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md`](docs/СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md) | Целевой мобильный UX редактора (база для E1.7). |
| [`docs/РУКОВОДСТВО_КАБИНЕТ_ТЕСТОВ.md`](docs/РУКОВОДСТВО_КАБИНЕТ_ТЕСТОВ.md) | Кратко для врачей-кураторов. |
| [`flask_app/README.md`](flask_app/README.md) | Конкретные команды для нового контура. |
| [`docs/ТЗ.md`](docs/ТЗ.md) | Исходное ТЗ заказчика. |