# Этап 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). Это вспомогательный документ, не часть этапов миграции.