feat(flask): E1.0–E1.3, E1.8 — миграция на Python/Flask + AI v2
Этап 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
This commit is contained in:
@@ -1,175 +1,102 @@
|
||||
# Перенос TestingWebApp на стек HR_TG_Bot / tgFlaskForm
|
||||
# Этап 2 (на будущее) — слияние TestingWebApp с `HR_TG_Bot/tgFlaskForm`
|
||||
|
||||
**Тот же план простым языком (две базы, люди, этапы):** [migration-to-tgflaskform-plain.md](migration-to-tgflaskform-plain.md).
|
||||
> **Не текущая фаза.** Текущая работа — **Этап 1**: унификация стека внутри TestingWebApp (Express → Flask + React → Jinja). См. [migration-final.md](migration-final.md). Этот документ — план **последующего** слияния, когда заказчик решит объединять.
|
||||
|
||||
**Назначение документа:** зафиксировать целевую архитектуру, **спринтовый план** доведения функциональности до паритета и **порядок миграции данных** из отдельного приложения (`Express` + `React` + БД `clinic_tests`) в кабинет **`tgFlaskForm`** (Flask, шаблоны, общая БД `hr_bot_test`, таблицы `testing_*`).
|
||||
**Простое объяснение тех же шагов:** [migration-to-tgflaskform-plain.md](migration-to-tgflaskform-plain.md).
|
||||
|
||||
**Связанные материалы:** [PROJECT_STATUS.md](PROJECT_STATUS.md), [СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md](СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md) (актуальный UI React-кабинета), [README.md](../README.md), [TEST_TABLES_ANALYSIS.md](TEST_TABLES_ANALYSIS.md), код модуля в репозитории HR: `HR_TG_Bot/tgFlaskForm/webApp/interfaces/testing/`, модели: `HR_TG_Bot/tgFlaskForm/db/models.py`.
|
||||
**Каркас нового контура в этом репозитории:** [../flask_app/README.md](../flask_app/README.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. Стратегия переходного периода (отдельное приложение, тот же стек)
|
||||
## 0. Предусловие — Этап 1 закрыт
|
||||
|
||||
**Решение:** переписывание с Node/React на **тот же стек, что у мини-приложения и кабинета HR** — Python 3, **Flask**, шаблоны (Jinja2), статический JS, работа с PostgreSQL в духе `tgFlaskForm`. При этом сервис **пока живёт отдельно**: свой процесс, свой URL/порт, **не** обязан совпадать с деплоем полного `HR_TG_Bot/tgFlaskForm`.
|
||||
К моменту, когда этот документ берётся в работу, в TestingWebApp **уже** должно быть:
|
||||
|
||||
**Зачем так:** быстрее выйти на паритет по UX и данным, **без** риска «большого взрыва» в едином кабинете; позже либо встраиваете модуль в кабинет (общий `webApp`), либо оставляете отдельный вход — стек уже совпадает.
|
||||
- Бэкенд переписан с Express на Flask внутри [`flask_app/`](../flask_app/), все 22 эндпоинта работают.
|
||||
- Фронтенд переписан с React на Jinja-шаблоны в `flask_app/app/templates/`.
|
||||
- БД — по-прежнему `clinic_tests`, схема не менялась.
|
||||
- В репозитории остался один сервис приложения.
|
||||
|
||||
**Обязательно зафиксировать продуктово:**
|
||||
|
||||
| Вопрос | Рекомендация |
|
||||
|--------|----------------|
|
||||
| Где **пишут** тесты и попытки, пока два контура? | Один «канонический» контур на запись; второй read-only или только пилот — иначе разъедутся данные. |
|
||||
| База | Либо по-прежнему **`clinic_tests`** в новом Flask до ETL, либо сразу **`hr_bot_test`** + `testing_*` (как в кабинете) — одно из двух, не смешивать без миграции. |
|
||||
| ETL | Скрипт `HR_TG_Bot/tgFlaskForm/tools/migrate_clinic_tests_to_hr.py`: бэкап → `--dry-run` → проверка на копии → короткое окно → `--apply`. |
|
||||
|
||||
**Технически:** в репозитории TestingWebApp заведён каталог **`flask_app/`** — минимальное приложение-заготовка; развитие переноса идёт там (или копированием готовых модулей из `HR_TG_Bot/tgFlaskForm`).
|
||||
Если что-то из этого ещё не готово — Этап 2 не начинается.
|
||||
|
||||
---
|
||||
|
||||
## 1. Зачем переносить
|
||||
## 1. Что меняется при слиянии
|
||||
|
||||
| Аспект | Сейчас (TestingWebApp) | Цель (tgFlaskForm) |
|
||||
|--------|------------------------|---------------------|
|
||||
| Стек | Node.js (Express), React (Vite), отдельный деплой | Python 3, Flask, Jinja/PyPug, статический JS в шаблонах — **единый кабинет** с остальным HR |
|
||||
| База | PostgreSQL, схема `clinic_tests`, UUID-ключи, локальные `users` | Та же инфраструктура Postgres, БД **`hr_bot_test`**, целочисленные `id`, связь с **`staff_members`** |
|
||||
| Авторизация | Собственные логин/JWT + опция `HR_AUTH` | Сессии кабинета, RBAC через HR (`testing_head_positions`, флаги HR и т.д.) |
|
||||
| Модуль тестирования | Полный цикл в одном репозитории | В **`tgFlaskForm` уже есть** blueprint `/cabinet/testing`, запросы в `db/queries/testing_queries.py` — задача переноса = **паритет фич + данные + вывод из эксплуатации** старого UI/API |
|
||||
|
||||
Итог после **полной** консолидации: один вход для сотрудника, одна БД «истины» по людям, меньше дублирования интеграций с HR. На переходном этапе допустим **отдельный** Flask-инстанс с тем же стеком (см. §0).
|
||||
| Аспект | После Этапа 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. План Этапа 2 (по спринтам)
|
||||
|
||||
**Исходный (TestingWebApp):**
|
||||
### E2.0 — Сверка кода и моделей
|
||||
|
||||
- Backend: `express`, `pg`, миграции SQL в `backend/src/db/migrations/`.
|
||||
- Frontend: `react`, `react-router-dom`, `vite`.
|
||||
- Данные: цепочки `tests` → `test_versions` → `questions` → `answer_options`; назначения с `test_assignment_targets` (отдел/пользователь); попытки `test_attempts`, ответы `user_answers` (массив UUID вариантов).
|
||||
- Сравнить структуру `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` на цепочке или на версии).
|
||||
|
||||
**Целевой (`HR_TG_Bot/tgFlaskForm`) и отдельный контур в этом репозитории (`flask_app/`):**
|
||||
### E2.1 — Перенос кода как blueprint
|
||||
|
||||
- Приложение: `Flask`, точка входа `web_run.py`, фабрика/приложение `webApp/__init__.py`.
|
||||
- Шаблоны: `webApp/templates/cabinet/testing/*.html`, клиентский JS в `templates/static/js/cabinet/testing_*`.
|
||||
- ORM/запросы: SQLAlchemy-модели `TestingTest`, `TestingTestVersion`, `TestingQuestion`, `TestingAnswer`, `TestingAssignment`, `TestingAttempt`, `TestingAttemptAnswer`, `TestingSetting`, `TestingHeadPosition` в `db/models.py`; бизнес-запросы — `db/queries/testing_queries.py`.
|
||||
- Сервер: dev `flask run`, prod типично `waitress` (см. `web_run.py`).
|
||||
- **Отдельный деплой в TestingWebApp:** каталог `flask_app/` — `run.py`, шаблоны в `flask_app/app/templates/` (см. §0).
|
||||
- Скопировать роуты, сервисы, шаблоны из `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. Спринтовый план (переписывание = паритет + миграция + снятие стенда)
|
||||
## 3. Что трогаем в HR-кабинете до Этапа 2
|
||||
|
||||
Длительность спринта ориентировочно **2 календарные недели**; границы можно сжимать/растягивать под состав команды. Нумерация условная: **Спринт 0** — подготовка, далее функциональные слои.
|
||||
|
||||
### Спринт 0 — Инвентаризация и критерии готовности
|
||||
|
||||
**Цель:** зафиксировать разрыв «TestingWebApp ↔ tgFlaskForm» и правила миграции.
|
||||
|
||||
- Составить **матрицу сценариев** по [ТЗ.md](ТЗ.md) и [PROJECT_STATUS.md](PROJECT_STATUS.md): редактор теста, версии, назначения, прохождение, разбор, трекер, настройки модуля, AI.
|
||||
- Зафиксировать отличия схемы: UUID vs integer, модель назначений (цель: каждая строка `TestingAssignment` = один `staff_id`).
|
||||
- Решение по **импорту из PDF/DOCX** (в Node-версии есть извлечение текста для черновика): либо перенос в Python (`tgFlaskForm`), либо явный scope «после миграции».
|
||||
- **Критерий выхода:** подписанный чек-лист паритета + утверждённый порядок миграции (раздел 4 этого документа).
|
||||
|
||||
### Спринт 1 — Данные и идентификаторы
|
||||
|
||||
**Цель:** подготовить перенос без потери смысла связей.
|
||||
|
||||
- Убедиться, что у всех значимых пользователей `clinic_tests.users` есть сопоставление с **`staff_members.id`** (колонка `staff_id` и/или правила сопоставления по логину из HR).
|
||||
- Спроектировать **таблицы соответствия** для одноразового ETL (например временные таблицы или JSON-маппинги: `old_test_uuid → testing_tests.id`, `old_version_uuid → testing_test_versions.id`, и т.д.).
|
||||
- Реализовать **скрипт миграции** — в репозитории HR: [`HR_TG_Bot/tgFlaskForm/tools/migrate_clinic_tests_to_hr.py`](../../HR_TG_Bot/tgFlaskForm/tools/migrate_clinic_tests_to_hr.py) (Python, `psycopg2`, два URL). Режимы: `--dry-run` (только отчёт) и `--apply` (одна транзакция `COMMIT` в `hr_bot_test`). Переменные или флаги: `CLINIC_TESTS_URL`, `HR_BOT_URL`; опция `--skip-missing-staff` пропускает цепочки, у автора нет `users.staff_id`.
|
||||
- **Критерий выхода:** dry-run на копии прод-дампа `clinic_tests` + smoke-проверки количества строк (тесты, версии, вопросы, попытки).
|
||||
|
||||
### Спринт 2 — Паритет бизнес-логики в Flask
|
||||
|
||||
**Цель:** закрыть расхождения поведения, а не только UI.
|
||||
|
||||
- Версионирование: правила «первая правка без попыток / новая версия после попыток», активная версия — согласовать с уже реализованным в `testing_queries.py` и довести до полного соответствия ТЗ при необходимости.
|
||||
- Назначения: если в `clinic_tests` остались назначения **на отдел**, описать стратегию **разворачивания** в N строк `TestingAssignment` (по списку `staff_id` отдела на дату миграции) или доработать модель в HR (отдельное решение продукт-оунера).
|
||||
- Прохождение: таймер, лимит попыток, дедлайн, случайный порядок вопросов (`question_seed`) — сверка с ТЗ и доработка в Python при расхождении.
|
||||
- **Критерий выхода:** автоматические тесты на критичные запросы (где их ещё нет) + ручной прогон чек-листа из спринта 0.
|
||||
|
||||
### Спринт 3 — UI/UX кабинета и интеграция в меню
|
||||
|
||||
**Цель:** пользователь не возвращается к старому хосту.
|
||||
|
||||
- Пункты меню кабинета, бейджи «назначенные тесты», единый стиль с `cabinet/base.html`.
|
||||
- Довести страницы: список «мои тесты», редактор, назначение, прохождение, результат/разбор, трекер, настройки — по чек-листу.
|
||||
- Импорт документов (если включён в scope спринта 0): эндпоинт + UI в шаблоне, ключи API только на сервере (`TestingSetting` / env).
|
||||
- **Критерий выхода:** UX-приёмка на стенде, совпадающий с ТЗ сценарий для HR / руководителя / сотрудника.
|
||||
|
||||
### Спринт 4 — Миграция prod, cutover, архив TestingWebApp
|
||||
|
||||
**Цель:** переключить реальных пользователей и зафиксировать артефакты.
|
||||
|
||||
- Заморозка записи в TestingWebApp (режим только чтение или техническое окно).
|
||||
- Прогон ETL на прод-копии → валидация → прогон на боевой БД в согласованное окно.
|
||||
- Обновление ссылок (внутренние порталы, документация, docker-compose): вместо `:3107` / отдельного сервиса — URL кабинета HR с `/cabinet/testing/...`.
|
||||
- Репозиторий TestingWebApp: ветка **`legacy/clinic-tests-node`**, в README — ссылка на этот документ и дата end-of-life API/UI.
|
||||
- **Критерий выхода:** мониторинг ошибок (например Sentry уже в `webApp/__init__.py`), отсутствие P1 по тестам в первую неделю после cutover.
|
||||
**Ничего.** Существующий модуль `tgFlaskForm/webApp/interfaces/testing/` развивается своим темпом командой HR-кабинета и **не** должен подстраиваться под TestingWebApp до момента слияния. Если в нём появляются полезные правки — переносим в `flask_app/` обратным потоком.
|
||||
|
||||
---
|
||||
|
||||
## 4. Как происходит миграция данных (пошагово)
|
||||
|
||||
### 4.1 Предпосылки
|
||||
|
||||
1. Доступ к **двум** базам с одной машины (или логическое копирование дампа): `clinic_tests` и `hr_bot_test`.
|
||||
2. Маппинг **пользователь → сотрудник:** для каждой строки `users` в `clinic_tests` должен быть известен **`staff_members.id`**. Если `staff_id` пустой — заранее ручной/полуавтоматический справочник соответствий (логин, email, ФИО).
|
||||
3. Зафиксированная **версия кода** `tgFlaskForm`, в которой пройдены регрессионные тесты модуля тестирования.
|
||||
|
||||
### 4.2 Порядок загрузки сущностей (чтобы не нарушить FK)
|
||||
|
||||
Рекомендуемый порядок транзакций/батчей:
|
||||
|
||||
1. **`testing_tests`** — из цепочек `tests`: `title`, `description`, `created_by` ← `users.staff_id`, `is_active`, `created_at` (по политике: локальное время vs UTC).
|
||||
2. **`testing_test_versions`** — из `test_versions`: связь `test_id` через маппинг; `version_number` ← `version`; `passing_score_percent` ← порог из версии/цепочки (в старой схеме часть полей была на `tests` — нормализовать в версию как в SQLAlchemy-модели); `time_limit_minutes`, `allow_back_navigation`, `is_active_version`, флаг единственной активной версии на цепочку.
|
||||
3. **`testing_questions`** — из `questions`: текст, тип (`single`/`multiple` из `has_multiple_answers`), `sort_order` ← `question_order`.
|
||||
4. **`testing_answers`** — из `answer_options`: текст, `is_correct`, порядок.
|
||||
5. **`testing_assignments`** — из `test_assignments` + `test_assignment_targets`:
|
||||
- для целей типа **пользователь:** одна строка на пару (тест, `staff_id`);
|
||||
- для целей **отдел:** развернуть в множество строк по сотрудникам отдела на момент миграции (с явным логом «создано из department_id=…»);
|
||||
- `assigned_by` ← `staff_id` постановщика; `deadline`, `max_attempts`, `assigned_at`.
|
||||
6. **`testing_attempts`** — из `test_attempts`: связь с новым `assignment_id` (если в старой модели попытка шла от `user_id` без отдельного assignment — потребуется **восстановление** или создание синтетических назначений; зафиксировать правило в спринте 0).
|
||||
7. **`testing_attempt_answers`** — из `user_answers`: каждый выбранный UUID варианта → строка с новым `answer_id` (через маппинг `answer_options.id` → `testing_answers.id`).
|
||||
|
||||
Везде, где в старой БД использовались **UUID**, скрипт хранит таблицу **`public._clinic_tests_migration_map`** (`entity`, `old_uuid` → `new_id`) в `hr_bot_test` для идемпотентного повторного прогона.
|
||||
|
||||
**Замечание по назначениям:** в текущей версии скрипта строки `clinic_tests.test_assignments` / `test_assignment_targets` **не** переносятся пакетно; для каждой пары (тест HR, сотрудник) при переносе **попыток** создаётся или находится строка `testing_assignments` (синтетическое назначение, `max_attempts = 99`). Полный импорт истории назначений из clinic — отдельная доработка при необходимости.
|
||||
|
||||
### 4.3 Валидация после ETL
|
||||
|
||||
- Сравнение **агрегатов:** число тестов, версий, вопросов, назначений, завершённых попыток, строк ответов.
|
||||
- Выборочная сверка: 5–10 последних попыток — ручной разбор «вопрос / выбранные варианты / балл» в старом и новом UI.
|
||||
- Проверка уникальности «одна активная версия на тест» и отсутствия «висячих» FK.
|
||||
|
||||
### 4.4 Cutover (переключение)
|
||||
|
||||
1. Объявить **окно**: остановка записи в TestingWebApp.
|
||||
2. Инкрементальный дамп изменений с последней реплики (если делали пробный перенос ранее) или финальный полный перенос.
|
||||
3. Прогон ETL в транзакции (или по крупным батчам с чекпоинтами) → `VACUUM ANALYZE` при необходимости.
|
||||
4. Включить пользователям ссылку на **кабинет**; проверить права `can_create_tests` / HR.
|
||||
5. Сохранить **бэкап** `clinic_tests` и лог миграции минимум на срок, определённый политикой клиники (типично 30–90 дней).
|
||||
|
||||
### 4.5 Откат
|
||||
|
||||
- Если после cutover обнаружен блокирующий дефект: вернуть пользователей на временный старый стенд **только для чтения** при наличии бэкапа; новые данные в `hr_bot_test` после cutover при откате не синхронизируются автоматически — риск фиксируется заранее (короткое окно, «freeze» повторных действий).
|
||||
|
||||
---
|
||||
|
||||
## 5. Риски и как их снимать
|
||||
## 4. Риски Этапа 2 и как их снимать
|
||||
|
||||
| Риск | Мера |
|
||||
|------|------|
|
||||
| Неполное сопоставление `users` ↔ `staff_members` | Закрыть в спринте 1; не начинать ETL без процента покрытия, согласованного с заказчиком |
|
||||
| Разная семантика назначений (отдел, версия) | Явные правила в спринте 0 + лог развёртки отделов |
|
||||
| Потеря истории попыток из-за смены модели assignment | Моделирование на копии БД в спринте 1–2 |
|
||||
| Дублирование разработки UI | Опираться на уже существующий модуль в `tgFlaskForm`, не переписывать с нуля параллельный SPA |
|
||||
| Несовпадение `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. |
|
||||
|
||||
---
|
||||
|
||||
## 6. Итог
|
||||
## 5. Производительность кабинета (общее)
|
||||
|
||||
Переписывание в данном контексте — это не «ещё один greenfield на Flask», а **консолидация** уже начатого модуля в `tgFlaskForm` с **одноразовой миграцией** из `clinic_tests` и выводом из эксплуатации связки React + Express. Спринты 0–4 дают сквозной маршрут от анализа до cutover; детали ETL должны быть закреплены в коде скрипта и журнале прогона к концу **спринта 1**.
|
||||
|
||||
**См. также:** если пользователи жалуются на медленную загрузку страниц кабинета/Flask — пошаговый план измерений и правок: [performance-flask-mini-app.md](performance-flask-mini-app.md).
|
||||
Если после переноса страницы кабинета или мини-приложения работают медленно — пошаговый план измерений и правок: [performance-flask-mini-app.md](performance-flask-mini-app.md). Это вспомогательный документ, не часть этапов миграции.
|
||||
|
||||
Reference in New Issue
Block a user