You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

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-сервис на порту 3108 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):

  1. Перенести код Flask-приложения как blueprint в tgFlaskForm.
  2. Адаптировать модели под существующие Testing* таблицы (hr_bot_test.testing_*).
  3. Перевести авторизацию на сессии общего HR-кабинета.
  4. Прогнать 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).
  5. 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. Бизнес-логика не добавлялась.