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
103 lines
8.6 KiB
Markdown
103 lines
8.6 KiB
Markdown
# Этап 2 (на будущее) — слияние TestingWebApp с `HR_TG_Bot/tgFlaskForm`
|
||
|
||
> **Не текущая фаза.** Текущая работа — **Этап 1**: унификация стека внутри TestingWebApp (Express → Flask + React → Jinja). См. [migration-final.md](migration-final.md). Этот документ — план **последующего** слияния, когда заказчик решит объединять.
|
||
|
||
**Простое объяснение тех же шагов:** [migration-to-tgflaskform-plain.md](migration-to-tgflaskform-plain.md).
|
||
|
||
**Связано:** [migration-final.md](migration-final.md) (главный трекер двух этапов), [migration-final-inventory.md](migration-final-inventory.md) (карта Express + gap-analysis с уже существующим модулем `tgFlaskForm`), [PROJECT_STATUS.md](PROJECT_STATUS.md), [README](../README.md), код HR-кабинета: `HR_TG_Bot/tgFlaskForm/webApp/interfaces/testing/`, модели: `HR_TG_Bot/tgFlaskForm/db/models.py`.
|
||
|
||
---
|
||
|
||
## 0. Предусловие — Этап 1 закрыт
|
||
|
||
К моменту, когда этот документ берётся в работу, в TestingWebApp **уже** должно быть:
|
||
|
||
- Бэкенд переписан с Express на Flask внутри [`flask_app/`](../flask_app/), все 22 эндпоинта работают.
|
||
- Фронтенд переписан с React на Jinja-шаблоны в `flask_app/app/templates/`.
|
||
- БД — по-прежнему `clinic_tests`, схема не менялась.
|
||
- В репозитории остался один сервис приложения.
|
||
|
||
Если что-то из этого ещё не готово — Этап 2 не начинается.
|
||
|
||
---
|
||
|
||
## 1. Что меняется при слиянии
|
||
|
||
| Аспект | После Этапа 1 (отдельный сервис) | После Этапа 2 (часть HR-кабинета) |
|
||
|---|---|---|
|
||
| Репозиторий | `TestingWebApp/flask_app/` | `HR_TG_Bot/tgFlaskForm/webApp/interfaces/testing/` |
|
||
| Деплой | Свой Docker-сервис, свой URL/порт | Часть основного `tgFlaskForm`, общий URL `/cabinet/testing/...` |
|
||
| БД | `clinic_tests`, UUID | `hr_bot_test`, integer ID, схема `testing_*` |
|
||
| Авторизация | JWT/bcrypt + опциональный `HR_AUTH` | Сессии общего HR-кабинета, привязка к `staff_members` |
|
||
| Модели | Свои (как в Express, но на Python) | Существующие `TestingTest`, `TestingTestVersion`, `TestingQuestion`, `TestingAnswer`, `TestingAssignment`, `TestingAttempt`, `TestingAttemptAnswer`, `TestingSetting`, `TestingHeadPosition` в `db/models.py` |
|
||
| UI | Jinja-шаблоны в `flask_app/app/templates/` | Jinja-шаблоны в `tgFlaskForm/webApp/templates/cabinet/testing/` |
|
||
|
||
---
|
||
|
||
## 2. План Этапа 2 (по спринтам)
|
||
|
||
### E2.0 — Сверка кода и моделей
|
||
|
||
- Сравнить структуру `flask_app/` (после Этапа 1) с уже существующим модулем `tgFlaskForm/webApp/interfaces/testing/`. Где функции называются иначе — выбрать одно имя.
|
||
- Сверить модели: `clinic_tests` UUID-схема vs `hr_bot_test` `testing_*` integer-схема. Зафиксировать поле-в-поле соответствия (большая часть уже сделана в [migration-final-inventory.md §10](migration-final-inventory.md#10-gap-analysis-tgflaskformcabinettesting-vs-express)).
|
||
- **Критерий выхода:** документ соответствий + решение по спорным точкам (например, кто прав — `is_active` на цепочке или на версии).
|
||
|
||
### E2.1 — Перенос кода как blueprint
|
||
|
||
- Скопировать роуты, сервисы, шаблоны из `flask_app/` в `tgFlaskForm`. **Адаптировать**:
|
||
- Импорты — на `db/models.py` и `db/queries/testing_queries.py` HR-кабинета.
|
||
- Авторизация — сменить с JWT/bcrypt на сессии и `werkzeug.security.check_password_hash`.
|
||
- URL-prefix с корневого на `/cabinet/testing/`.
|
||
- Шаблоны — наследование от `cabinet/base.html` (хедер, нижний нав-бар).
|
||
- **Критерий выхода:** все экраны открываются через HR-кабинет, локальный smoke-тест зелёный.
|
||
|
||
### E2.2 — Миграция данных (ETL)
|
||
|
||
Скрипт уже готов: [`HR_TG_Bot/tgFlaskForm/tools/migrate_clinic_tests_to_hr.py`](../../HR_TG_Bot/tgFlaskForm/tools/migrate_clinic_tests_to_hr.py) (437 строк, режимы `--dry-run`/`--apply`, идемпотентность через `public._clinic_tests_migration_map`).
|
||
|
||
Перед прогоном **на актуальных данных** дописать:
|
||
|
||
- **Перенос `test_assignments` 1:1** — сейчас скрипт переносит только пары «тест-сотрудник» через попытки; нужны и «висящие» назначения без попыток. (Решение Этапа 2.)
|
||
- **Логирование пользователей без `staff_id`:** автор → WARN, попытка → WARN; никаких хардовых ошибок. (Решение Этапа 2.)
|
||
|
||
**Порядок:**
|
||
|
||
1. Бэкап `clinic_tests` и `hr_bot_test`.
|
||
2. `--dry-run` на копии прод-БД, разбор лога.
|
||
3. `--apply` на той же копии, ручная сверка через UI HR-кабинета.
|
||
4. После приёмки — `--dry-run` + `--apply` на боевой БД.
|
||
|
||
### E2.3 — Cutover
|
||
|
||
Если к этому моменту у TestingWebApp всё ещё «песочница для тестировщиков» (как сейчас) — простое переключение, без окна простоя и баннеров. Если появятся реальные пользователи — добавить пункт E2.3.1: коммуникация и redirect.
|
||
|
||
- Заморозка записи в `flask_app/` старой инсталляции (read-only).
|
||
- Прогон ETL на боевом.
|
||
- Маршрутизация: внешние ссылки `clinic-tests.example.com/*` → `hr-cabinet.example.com/cabinet/testing/*`.
|
||
- В корневом репозитории TestingWebApp — ветка `legacy/clinic-tests-flask`, в README — ссылка на этот документ и дату EOL.
|
||
|
||
**Критерий выхода:** мониторинг ошибок (например Sentry, уже подключён в `webApp/__init__.py`), отсутствие P1 в первую неделю.
|
||
|
||
---
|
||
|
||
## 3. Что трогаем в HR-кабинете до Этапа 2
|
||
|
||
**Ничего.** Существующий модуль `tgFlaskForm/webApp/interfaces/testing/` развивается своим темпом командой HR-кабинета и **не** должен подстраиваться под TestingWebApp до момента слияния. Если в нём появляются полезные правки — переносим в `flask_app/` обратным потоком.
|
||
|
||
---
|
||
|
||
## 4. Риски Этапа 2 и как их снимать
|
||
|
||
| Риск | Мера |
|
||
|------|------|
|
||
| Несовпадение `users.staff_id` ↔ `staff_members.id` | Проверка перед `--apply`; пользователей без `staff_id` пропускаем по решению. |
|
||
| Расхождение моделей (UUID vs integer, поля «на цепочке» vs «на версии») | Закрыть в E2.0; подкрепить unit-тестами на конвертацию. |
|
||
| Назначения «отдел → N сотрудников» | Логировать развёртку с пометкой `created_from_department=...`. |
|
||
| Двойное развитие модуля HR-кабинета | До Этапа 2 — не править `tgFlaskForm/cabinet/testing` под нужды TestingWebApp. |
|
||
|
||
---
|
||
|
||
## 5. Производительность кабинета (общее)
|
||
|
||
Если после переноса страницы кабинета или мини-приложения работают медленно — пошаговый план измерений и правок: [performance-flask-mini-app.md](performance-flask-mini-app.md). Это вспомогательный документ, не часть этапов миграции.
|