18 KiB
Унификация стека TestingWebApp с tgFlaskForm — план и журнал
Корректировка курса от 2026-04-27. Ранее в этом документе фигурировал «полный переезд в HR-кабинет (
tgFlaskForm) с cutover'ом и удалением React/Express». Это было забеганием вперёд. Текущая фаза — только унификация стека, без слияния репозиториев и без миграции данных.
Назначение документа
Зафиксировать разделение работы на два этапа и текущий статус каждого. Этот файл — трекер решения и журнал, основной план Этапа 1 — здесь же; план Этапа 2 (на будущее) — в migration-to-tgflaskform.md.
Связано: migration-final-inventory.md (карта 22 эндпоинтов Express, БД, env, зависимости, плюс справочный gap-analysis с уже существующим модулем в tgFlaskForm — пригодится в Этапе 2), PROJECT_STATUS.md, README, flask_app/README.md, СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md.
Этап 1 (текущий) — единый стек: Express → Flask, React → Jinja, внутри TestingWebApp
Цель
Привести TestingWebApp к тому же стеку, что у HR_TG_Bot/tgFlaskForm:
- Бэкенд: Python 3 + Flask, точечный SQL/SQLAlchemy в стиле
tgFlaskForm. Развивается в каталогеflask_app/этого репозитория (сейчас — минимальный каркас). - Фронтенд: Jinja-шаблоны в
flask_app/app/templates/, мобильный UX из Спринта 3 переносится один в один. React (frontend/) уходит после того, как Jinja-версия закроет все экраны. - БД: остаётся
clinic_tests(со своими UUID-ключами). Никаких изменений схемы. - Авторизация: JWT/bcrypt + опциональный
HR_AUTH=1(как в Express) — переносим как есть.
Что не делаем в Этапе 1
- Не трогаем
HR_TG_Bot/tgFlaskForm/. Его модульcabinet/testingживёт своей жизнью. - Не мигрируем данные
clinic_tests → hr_bot_test. ETL-скриптmigrate_clinic_tests_to_hr.pyесть, но он — для Этапа 2. - Не удаляем
backend/иfrontend/сразу. Они работают параллельно сflask_app/до полного паритета. Удаление — последним PR этапа.
Стартовая точка flask_app/
| Что есть | Файл / артефакт |
|---|---|
Flask-приложение (create_app) |
flask_app/app/__init__.py |
| Точка входа (dev / waitress) | flask_app/run.py |
/health |
flask_app/app/__init__.py |
Пустой index.html |
flask_app/app/templates/index.html |
| Зависимости | flask_app/requirements.txt (Flask, python-dotenv, waitress) |
| Docker Flask (порт 3107) | flask_app/Dockerfile, docker-compose.dev.yml (сервис testing-flask) |
Всё остальное — писать.
План Этапа 1 (по спринтам)
| Спринт | Цель | Артефакты |
|---|---|---|
| E1.0 — База Flask-приложения ✅ | БД-пул (SQLAlchemy + psycopg2), Flask sessions через SECRET_KEY, конфиг через .env, структура blueprint'ов, шаблон base.html в стиле кабинета, обработчики 404/500, /health с проверкой БД. Без бизнес-логики. |
flask_app/app/db.py, flask_app/app/__init__.py, flask_app/app/blueprints/main.py, flask_app/app/templates/{base,index,404,500}.html, flask_app/app/static/css/app.css, обновлённые requirements.txt и .env.example |
E1.1 — Auth и /api/me ✅ |
Flask sessions (signed cookie), bcrypt + Werkzeug (werkzeug.security.check_password_hash), опц. HR_AUTH=1 с UPSERT в clinic_tests.users по staff_id. UI-страница /login, JSON-API /api/auth/{login,logout,me}, декораторы login_required/require_role, current_user доступен в шаблонах. |
flask_app/app/auth/{routes,services,decorators,hr_role}.py, flask_app/app/{config,messages}.py, flask_app/app/templates/auth/login.html, обновлены base.html, __init__.py, requirements.txt (+bcrypt) |
| E1.2 — Тесты: список и редактор ✅ | Перенесены 10 эндпоинтов из Express: GET/POST /api/tests, GET /api/tests/:id/{summary,versions,editor}, POST /api/tests/:id/draft, POST /api/tests/:id/versions/:vid/activate, PATCH /api/tests/:id, POST /api/tests/:id/ai/{generate-test,generate-question}. UI: /tests (каталог + создание), /tests/:id/edit (рабочий редактор с AI). Полная мобильная отполировка UX (4 аккордеона + fixed footer + drag-n-drop) — в E1.7. |
flask_app/app/services/{llm_client,draft_validator,ai_editor,test_access,test_chain,test_draft,editor_content}.py, flask_app/app/tests/{__init__,routes}.py, flask_app/app/templates/tests/{list,editor}.html, flask_app/app/static/js/editor.js, обновлены base.html, index.html, __init__.py |
| E1.3 — Импорт документов ✅ | POST /api/tests/import/document (PDF/DOCX/TXT/MD извлечение текста через pypdf и python-docx), интеграция с AI-генерацией черновика (generation_for_import_document), кнопка «Импорт документа» в AI-панели редактора, лимит 16 МБ. |
flask_app/app/services/{document_extract,document_gen}.py, эндпоинт в flask_app/app/tests/routes.py, кнопка в editor.html + editor.js, requirements.txt (+pypdf, python-docx) |
| E1.4 — Назначение и прохождение | Эндпоинты assign, attempts/start, attempts/:id/play, attempts/:id/submit, attempts/:id/review. Шаблоны: assign.html, take_test.html, test_result.html. |
flask_app/app/assignments/, flask_app/app/attempts/, шаблоны |
| E1.5 — Трекер и настройки | Трекер прохождений, настройки модуля (ключи AI и т.д.), цепочки тестов. Шаблоны tracker.html, settings.html. |
flask_app/app/tracker/, flask_app/app/settings/ |
| E1.6 — Cutover внутри репозитория | docker-compose.dev.yml указывает на flask_app/ как основной сервис; Nginx маршрутизирует /api и UI на новый Flask. Удаление backend/ и frontend/ отдельным PR. README → актуальные команды. |
docker-compose.dev.yml, корневой README.md, frontend/ и backend/ удаляются |
| E1.7 — UX-полировка редактора | Перевод базового редактора (E1.2) на мобильный UX из Спринта 3: 4 аккордеона (Шапка / AI-помощник / Вопросы / Действия), sticky footer, drag-n-drop вопросов, импорт документа в подразделе AI-блока (после E1.3). | flask_app/app/templates/tests/editor.html, flask_app/app/static/js/editor.js, новый static/css/testing.css |
| E1.8 — AI-функции v2 ✅ | /settings (статус ключа из ENV + ping), POST /api/llm/ping, на тесте — ai/generate-by-title (без сетки), ai/check (рецензия), ai/improve (массовое «было → стало» с чекбоксами). На уровне вопроса — уже есть ai/generate-question из E1.2 (создаёт вопрос или переформулирует). Все AI-эндпоинты унифицированы: при отсутствии ключа — { error, code, settingsUrl: '/settings' }. |
flask_app/app/services/{ai_editor,llm_client}.py, flask_app/app/blueprints/settings.py, flask_app/app/templates/settings.html, ссылка «Настройки» в base.html, обновлены tests/routes.py, editor.html, editor.js |
Критерии готовности Этапа 1
- Все 22 эндпоинта Express (см. migration-final-inventory.md) реализованы в
flask_app/и проходят smoke-тесты. - Все экраны мобильного UX из Спринта 3 воспроизведены в Jinja.
- В
docker-compose.dev.ymlостался один сервис приложения (Flask).backend/иfrontend/удалены или перенесены в веткуlegacy/clinic-tests-node. - БД — по-прежнему
clinic_tests, схема не менялась.
Этап 2 (на будущее, без сроков) — слияние с tgFlaskForm
Когда заказчик решит «вот теперь объединяем» — вся разработанная Flask-логика и шаблоны легко переносятся в HR_TG_Bot/tgFlaskForm/webApp/interfaces/testing/ и templates/cabinet/testing/, потому что стек уже совпадает. Это и есть смысл Этапа 1.
Что нужно сделать в Этапе 2 (план — в migration-to-tgflaskform.md):
- Перенести код Flask-приложения как blueprint в
tgFlaskForm. - Адаптировать модели под существующие
Testing*таблицы (hr_bot_test.testing_*). - Перевести авторизацию на сессии общего HR-кабинета.
- Прогнать ETL
clinic_tests → hr_bot_test(скриптHR_TG_Bot/tgFlaskForm/tools/migrate_clinic_tests_to_hr.pyуже готов: 437 строк, режимы--dry-run/--apply, идемпотентность через_clinic_tests_migration_map). - Cutover (если к тому моменту появятся реальные пользователи; сейчас TestingWebApp — песочница для тестировщиков).
Решения, которые относятся к Этапу 2 (зафиксированы заранее, чтобы потом не переоткрывать):
test_assignments: переносим 1:1, дописывая отдельный блок в ETL (сейчас скрипт переносит только пары через попытки).- Пользователи без
staff_id: игнорируем с WARN; по договорённости настоящие пользователи всегда привязаны кstaff_members. - Cutover / окно простоя: не нужны, пока TestingWebApp остаётся песочницей.
Журнал
| Дата | Что сделано |
|---|---|
| 2026-04-27 | Спринт 0 («инвентаризация» в старой нумерации) закрыт: артефакт migration-final-inventory.md — карта 22 эндпоинтов Express, БД, env, зависимости. |
| 2026-04-27 | Принято решение: сценарий B + b1 (полный переезд в HR-кабинет). |
| 2026-04-27 | Курс скорректирован: Этап 1 = унификация стека внутри TestingWebApp (Express → Flask + React → Jinja, БД остаётся clinic_tests). Этап 2 = слияние с tgFlaskForm — на будущее. ETL и удаление React переходят в Этап 2. Документы переписаны под двух-этапную картину. Эксперимент с правкой tgFlaskForm/cabinet/testing/test_editor.html (ветка feat/testing-editor-jinja-redesign) откачен и не оставил следов в HR-репо. |
| 2026-04-27 | E1.8 закрыт. AI v2: страница /settings (статус ключа из ENV, Проверить подключение → POST /api/llm/ping). Три новых эндпоинта на тесте: POST /api/tests/<id>/ai/generate-by-title (генерация только по названию + опции «сколько вопросов / сколько вариантов»), POST /api/tests/<id>/ai/check (рецензия: вердикт + разделы рекомендаций), POST /api/tests/<id>/ai/improve (массовое «было → стало» с проверкой неизменности сетки). UI редактора: кнопки «Сгенерировать по названию», «Проверить тест», «Улучшить тест»; общий <dialog> для модалок check/improve; чекбоксы в improve позволяют применять изменения по выбранным вопросам. Все AI-эндпоинты унифицированы: при отсутствии ключа возвращают { error, code, settingsUrl: '/settings' } 502 — фронт предлагает открыть Настройки. |
| 2026-04-27 | E1.3 закрыт. Импорт документов: app/services/document_extract.py (PDF через pypdf, DOCX через python-docx, TXT/MD), app/services/document_gen.py (generation_for_import_document — извлекает текст, при наличии LLM-ключа просит модель собрать draft через validate_and_normalize_draft), эндпоинт POST /api/tests/import/document под @login_required с лимитом 16 МБ. UI редактора: кнопка «Импорт документа» в AI-панели, после загрузки — confirm с предложением применить черновик; если ключа нет — алерт с превью текста. В requirements.txt добавлены pypdf>=4 и python-docx>=1.1. |
| 2026-04-27 | E1.2 закрыт. Перенесены backend/src/routes/tests.js (только E1.2-эндпоинты — без import/document/assign/attempts/chain-info, они уйдут в E1.3-E1.5) + сервисы testDraftService.js, testAccessService.js, testChainService.js, aiEditorService.js, documentGenService.js (только парсер JSON и валидатор draft), llmClient.js, getEditorContent из testAttemptService.js. Эндпоинты регистрируются в blueprint tests. AI-генерация: parseAndValidateShape 1:1, ошибки LLM (llm_*-коды) пробрасываются как 502 с кодом в JSON. UI: каталог тестов с кнопкой создания (модалка <dialog>) и рабочий редактор (inline-поля, AI-кнопки «весь тест» / «один вопрос», добавление/удаление/перемещение вопросов и вариантов, сохранение черновика, переключатель «Цепочка активна»). Полный мобильный UX редактора (аккордеоны+fixed footer+drag-n-drop из Спринта 3) вынесен в новый спринт E1.7 — этот PR закрывает функциональность, не дизайн. |
| 2026-04-27 | E1.1 закрыт. Перенесены backend/src/routes/auth.js + middleware/auth.js + utils/{auth,werkzeugPassword,hrRoleMap}.js в flask_app/app/auth/. Решение: Flask sessions (signed cookie) вместо JWT, как договорились (вариант A). Поддерживаются bcrypt-хеши ($2*) и Werkzeug-хеши (scrypt:/pbkdf2:). Эндпоинты — те же пути, что в Express: POST /api/auth/login, POST /api/auth/logout, GET /api/auth/me (отдаёт user, devUi, assignmentUi). Дополнительно — HTML-страница /login (форма) и POST /logout. Декораторы: @login_required, @require_role(...). В шаблонах доступны current_user, hr_auth_enabled, dev_ui, assignment_ui. Защита от open-redirect в параметре ?next=. Главная (/) теперь требует логин. |
| 2026-04-27 | E1.0 закрыт. В flask_app/: SQLAlchemy/psycopg2-пул в стиле tgFlaskForm/db/session.py (app/db.py, основная БД clinic_tests + опциональная HR-БД при HR_AUTH=1), фабрика create_app с регистрацией blueprint'ов, обработчиками 404/500 и Flask sessions, главный blueprint main с / и /health (smoke-проверка БД), base.html в стиле кабинета HR (Tailwind CDN + Manrope + Material Symbols, без зависимостей от HR-репо), шаблоны index/404/500, минимальный static/css/app.css. Бизнес-логика не добавлялась. |