diff --git a/README.md b/README.md index 7e995e7..509d864 100644 --- a/README.md +++ b/README.md @@ -14,11 +14,12 @@ **Целевой и единственный рабочий стек** — Python 3.11 + Flask 3 + Jinja2 + Tailwind CDN + SQLAlchemy / psycopg2, код в -[`flask_app/`](flask_app/). На нём работает и прод, и dev (`:3108`). +[`flask_app/`](flask_app/). На нём работает и прод, и dev (кабинетный UI, +порт **:3107** в Docker, см. ниже). Старые каталоги `backend/` (Node.js / Express) и `frontend/` (React + Vite) — **архив**: не разворачиваются, в `docker-compose.dev.yml` -поднимается только сервис `testing-flask`, удаление папок запланировано +поднимается сервис **`testing-flask`**, удаление папок запланировано в спринте **E1.6**. Использовать их не надо, миграции и SQL-схема сохранены в `backend/src/db/migrations/` исключительно как источник структуры БД. @@ -33,6 +34,20 @@ Jinja2 + Tailwind CDN + SQLAlchemy / psycopg2, код в --- +## Интерфейс (кабинет) + +Единственный вариант UI — **как у основного HR-веба**: в +[`base.html`](flask_app/app/templates/base.html) корень +`cabinet-app` → шапка `cabinet-header` → контент `cabinet-main`; на +`` всегда класс **`ui-legacy`**, стили в [`app.css`](flask_app/app/static/css/app.css) +с префиксом **`body.ui-legacy`** (primary/teal, `.btn`, `.surface-card`, +`legacy-list-shell`, `test-detail-page` и т.д.). + +В [`docker-compose.dev.yml`](docker-compose.dev.yml) один сервис +**`testing-flask`** (`container_name: testing_webapp_flask`), порт **3107**. + +--- + ## Что уже работает на новом (Flask) контуре E1.0–E1.3 и E1.8 закрыты. Чек-лист и журнал — @@ -69,7 +84,7 @@ E1.0–E1.3 и E1.8 закрыты. Чек-лист и журнал — |---|---| | **E1.4** — Назначение и прохождение | Назначить тест сотруднику, экран прохождения, экран результата с разбором ошибок. | | **E1.5** — Трекер и настройки модуля | Единый список попыток с фильтрами, страница настроек цепочки. | -| **E1.6** — Cutover внутри репозитория | Удаление `backend/` и `frontend/`, чистка `docker-compose.dev.yml` от legacy-сервисов. | +| **E1.6** — Cutover внутри репозитория | Удаление `backend/` и `frontend/`, чистка `docker-compose.dev.yml` от архивных Node/React-сервисов. | | **E1.7** — UX-полировка редактора | 4 аккордеона (Шапка / AI / Вопросы / Действия) и drag-n-drop из [Спринта 3](docs/СПРИНТЫ_МОБИЛЬНЫЙ_ДИЗАЙН.md). | --- @@ -93,26 +108,16 @@ docker network create hr_postgres_dev_net || true ### Dev-стенд -Выбор интерфейса задаётся через env-переменную `COMPOSE_PROFILES`: - -- `modern` — основной интерфейс на Flask/Jinja; -- `legacy` — legacy-раскладка интерфейса на том же Flask-стеке. - ```bash -# Новый стек (рекомендуется) -COMPOSE_PROFILES=modern docker compose -f docker-compose.dev.yml up -d --build - -# Legacy-раскладка (тот же Flask) -COMPOSE_PROFILES=legacy docker compose -f docker-compose.dev.yml up -d --build +docker compose -f docker-compose.dev.yml up -d --build ``` | Что | URL | |---|---| -| Приложение (Flask modern) | | -| Health-check | | -| Приложение (Flask legacy) | | +| Приложение (Flask) | | +| Health-check | | -`docker-compose.dev.yml` пробрасывает в `testing-flask`: +`docker-compose.dev.yml` пробрасывает в контейнер **`testing-flask`**: - `DATABASE_URL` (по умолчанию на контейнерный Postgres `clinic_tests`); - `HR_AUTH=1` / `HR_DATABASE_URL` по умолчанию — вход через HR-кабинет; - `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` / `LLM_BASE_URL` / `LLM_MODEL` — diff --git a/docker-compose.dev.yml b/docker-compose.dev.yml index 7b17369..a904bbf 100644 --- a/docker-compose.dev.yml +++ b/docker-compose.dev.yml @@ -4,53 +4,14 @@ # База clinic_tests: один раз # psql "postgresql://hr_bot_user:hrbot123@localhost:5432/postgres" -c "CREATE DATABASE clinic_tests;" # -# Flask-only режим. Выбор варианта интерфейса через profile: -# COMPOSE_PROFILES=modern docker compose -f docker-compose.dev.yml up -d --build -# COMPOSE_PROFILES=legacy docker compose -f docker-compose.dev.yml up -d --build -# Оба варианта работают на одном Flask-стеке, отличаются только UI-раскладкой. -# UI (Flask modern): http://localhost:3108 -# UI (Flask legacy): http://localhost:3107 +# Flask UI (кабинетный стиль): http://localhost:3107 services: - # Flask modern UI testing-flask: - profiles: ["modern"] build: context: ./flask_app dockerfile: Dockerfile container_name: testing_webapp_flask - environment: - PORT: "3108" - WEB_USE_WAITRESS: "1" - FLASK_DEBUG: "0" - SECRET_KEY: ${FLASK_SECRET_KEY:-testing_flask_dev_change_me} - # БД (clinic_tests) в общей сети hr_postgres_dev_net. - # По умолчанию используем те же dev-учётки, что и в backend-сервисе. - DATABASE_URL: ${DATABASE_URL:-postgresql+psycopg2://hr_bot_user:hrbot123@hr_postgres_dev:5432/clinic_tests} - # HR-аутентификация включена по умолчанию: - # пароль проверяется в hr_bot_test.users + staff по web_login. - HR_AUTH: ${HR_AUTH:-1} - HR_DATABASE_URL: ${HR_DATABASE_URL:-postgresql://hr_bot_user:hrbot123@hr_postgres_dev:5432/hr_bot_test} - UI_VARIANT: ${UI_VARIANT_MODERN:-modern} - DEV_FIO_PASSWORD: ${DEV_FIO_PASSWORD:-} - # LLM (E1.2/E1.3/E1.8): один общий ключ, читается из .env проекта. - DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-} - OPENAI_API_KEY: ${OPENAI_API_KEY:-} - LLM_BASE_URL: ${LLM_BASE_URL:-} - LLM_MODEL: ${LLM_MODEL:-} - ports: - - "3108:3108" - networks: - - app - - postgres - - # Flask legacy UI (старое расположение элементов на новом стеке) - testing-flask-legacy: - profiles: ["legacy"] - build: - context: ./flask_app - dockerfile: Dockerfile - container_name: testing_webapp_flask_legacy environment: PORT: "3107" WEB_USE_WAITRESS: "1" @@ -59,7 +20,6 @@ services: DATABASE_URL: ${DATABASE_URL:-postgresql+psycopg2://hr_bot_user:hrbot123@hr_postgres_dev:5432/clinic_tests} HR_AUTH: ${HR_AUTH:-1} HR_DATABASE_URL: ${HR_DATABASE_URL:-postgresql://hr_bot_user:hrbot123@hr_postgres_dev:5432/hr_bot_test} - UI_VARIANT: ${UI_VARIANT_LEGACY:-legacy} DEV_FIO_PASSWORD: ${DEV_FIO_PASSWORD:-} DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY:-} OPENAI_API_KEY: ${OPENAI_API_KEY:-} diff --git a/docs/migration-final-inventory.md b/docs/migration-final-inventory.md index 4bf01cd..5c487f4 100644 --- a/docs/migration-final-inventory.md +++ b/docs/migration-final-inventory.md @@ -151,7 +151,7 @@ Используется один тонкий клиент `frontend/src/api.js`: `fetch` с `credentials: 'include'`, базовый путь — пустой (т.е. **`/api/...` относительно текущего origin**, что в dev резолвит Vite-proxy, а в prod — Nginx). Это значит: - **Менять фронтенд при смене бэкенда не нужно**, если новый сервис отвечает по тем же путям. -- В dev сейчас `vite.config.js` проксирует `/api` на Express (`localhost:3001`). После переноса — заменить адрес/порт на Flask (см. `flask_app/run.py`, по умолчанию `3108`). +- В dev сейчас `vite.config.js` проксирует `/api` на Express (`localhost:3001`). После переноса — заменить адрес/порт на Flask (см. `flask_app/run.py`, по умолчанию `3107`). Список путей (отсортирован по убыванию использований): diff --git a/docs/migration-final.md b/docs/migration-final.md index e460560..51378d2 100644 --- a/docs/migration-final.md +++ b/docs/migration-final.md @@ -39,7 +39,7 @@ | `/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`) | +| Docker Flask (порт 3107) | `flask_app/Dockerfile`, `docker-compose.dev.yml` (сервис `testing-flask`) | Всё остальное — **писать**. diff --git a/docs/performance-flask-mini-app.md b/docs/performance-flask-mini-app.md index 5c927b4..63e8a84 100644 --- a/docs/performance-flask-mini-app.md +++ b/docs/performance-flask-mini-app.md @@ -19,7 +19,7 @@ | Контур | Путь в репозитории | Назначение | |--------|-------------------|------------| | **Основной веб-кабинет HR** | `HR_TG_Bot/tgFlaskForm/` | Flask-приложение: авторизация, кабинет, разделы в т.ч. **тестирование сотрудников**. Именно сюда чаще всего относят жалобы «мини-приложение / кабинет на Flask». | -| **Отдельный Flask-скелет под тестирование** | `TestingWebApp/flask_app/` | Упрощённое приложение того же стека (переходный контур, Docker-сервис `testing-flask`, порт **3108** в `TestingWebApp/docker-compose.dev.yml`). Может быть медленным по тем же причинам (БД, шаблоны, отсутствие кэша статики), но **это не обязательно тот же инстанс**, что видят пользователи в проде. | +| **Отдельный Flask-скелет под тестирование** | `TestingWebApp/flask_app/` | Упрощённое приложение того же стека (переходный контур, Docker `testing-flask`, порт **3107**). Может быть медленным по тем же причинам (БД, шаблоны, отсутствие кэша статики), но **это не обязательно тот же инстанс**, что видят пользователи в проде. | Связанные по смыслу документы (миграция данных, две БД): @@ -30,7 +30,7 @@ 1. **Веб-кабинет в браузере** (`tgFlaskForm`, типичный порт локально **3104** в `web_run.py`). 2. **Встроенный WebView в мини-приложении** (Telegram MAX и т.п.) — тот же HTML с того же хоста, но **другая сеть, кэш, DNS, TLS**; воспроизведение обязательно на целевом клиенте. -3. **Переходный контур** `TestingWebApp` на **3108** — проверять отдельно, если пользователи реально ходят туда. +3. **Переходный контур** `TestingWebApp` на **3107** — проверять отдельно, если пользователи реально ходят туда. Перед оптимизацией **уточнить URL/контур** у тех, кто жалуется. @@ -114,7 +114,7 @@ ### Фаза 0 — уточнение (полдня максимум) -- [ ] Точный **URL/продукт** (кабинет HR vs TestingWebApp:3108 vs мини-app WebView). +- [ ] Точный **URL/продукт** (кабинет HR vs TestingWebApp:3107 vs мини-app WebView). - [ ] **Роль пользователя** и сценарий (первый заход, каждый клик, только раздел тестирования). - [ ] Есть ли **nginx / CDN** перед приложением. @@ -175,7 +175,7 @@ | Авторизация и gate | `HR_TG_Bot/tgFlaskForm/webApp/auth.py` | | Шаблоны | `HR_TG_Bot/tgFlaskForm/webApp/templates/` | | Переходный Flask + waitress | `TestingWebApp/flask_app/run.py`, `TestingWebApp/flask_app/app/` | -| Docker dev (пример порта 3108) | `TestingWebApp/docker-compose.dev.yml` | +| Docker dev (по умолчанию 3107 legacy) | `TestingWebApp/docker-compose.dev.yml` | | Docker dev кабинета | `HR_TG_Bot/docker-compose.dev.yml` | --- diff --git a/flask_app/.env.example b/flask_app/.env.example index 636715e..fd36787 100644 --- a/flask_app/.env.example +++ b/flask_app/.env.example @@ -1,6 +1,6 @@ # ─── HTTP сервер ───────────────────────────────────────────────── -# Порт (не пересекать с :3107 текущего docker-compose.dev.yml) -PORT=3108 +# Порт как в docker-compose (:3107). +PORT=3107 FLASK_DEBUG=1 # В Docker задайте WEB_USE_WAITRESS=1 (см. docker-compose.dev.yml) # WEB_USE_WAITRESS=1 diff --git a/flask_app/Dockerfile b/flask_app/Dockerfile index 1666e9e..8d0ce06 100644 --- a/flask_app/Dockerfile +++ b/flask_app/Dockerfile @@ -4,7 +4,7 @@ FROM python:3.11-slim ENV PYTHONDONTWRITEBYTECODE=1 \ PYTHONUNBUFFERED=1 \ PIP_DISABLE_PIP_VERSION_CHECK=1 \ - PORT=3108 \ + PORT=3107 \ WEB_USE_WAITRESS=1 \ FLASK_DEBUG=0 @@ -18,6 +18,6 @@ COPY app ./app COPY alembic.ini . COPY alembic ./alembic -EXPOSE 3108 +EXPOSE 3107 CMD ["python", "run.py"] diff --git a/flask_app/README.md b/flask_app/README.md index 1b85514..ddc82ca 100644 --- a/flask_app/README.md +++ b/flask_app/README.md @@ -15,20 +15,12 @@ `DATABASE_URL`, `HR_AUTH`, `HR_DATABASE_URL`, `DEEPSEEK_API_KEY` / `OPENAI_API_KEY` / `LLM_BASE_URL` / `LLM_MODEL` из корневого `.env`. -```bash -docker compose -f docker-compose.dev.yml up -d --build testing-flask -``` - -- **URL:** http://localhost:3108 -- **Проверка:** http://localhost:3108/health - -Вместе со старым UI и API: - ```bash docker compose -f docker-compose.dev.yml up -d --build ``` -Тогда Node-интерфейс остаётся на **http://localhost:3107**, Flask — на **3108**. +- **URL:** http://localhost:3107 +- **Проверка:** http://localhost:3107/health ## Запуск локально (без Docker) @@ -41,7 +33,7 @@ cp .env.example .env # при необходимости поправьте python run.py ``` -По умолчанию: **http://127.0.0.1:3108** , проверка: **http://127.0.0.1:3108/health** +По умолчанию (без `PORT` в `.env`): **http://127.0.0.1:3107** , проверка: **http://127.0.0.1:3107/health** Для режима как в Docker (waitress): в `.env` задайте `WEB_USE_WAITRESS=1`. ## Зачем отдельная папка diff --git a/flask_app/app/__init__.py b/flask_app/app/__init__.py index c26aaa6..5eb2f3d 100644 --- a/flask_app/app/__init__.py +++ b/flask_app/app/__init__.py @@ -80,13 +80,11 @@ def create_app() -> Flask: @app.context_processor def _inject_globals(): - ui_variant = (os.environ.get('UI_VARIANT') or 'modern').strip().lower() or 'modern' return { 'current_user': _current_user(), 'hr_auth_enabled': is_hr_auth_enabled(), 'dev_ui': is_dev_ui(), 'assignment_ui': is_assignment_feature_enabled(), - 'ui_variant': ui_variant, 'format_name_short': _format_surname_with_initials, 'format_role': _format_role, } diff --git a/flask_app/app/static/css/app.css b/flask_app/app/static/css/app.css index bb2c21d..c929164 100644 --- a/flask_app/app/static/css/app.css +++ b/flask_app/app/static/css/app.css @@ -78,19 +78,9 @@ h3 { } /* ------------------------------------------------------------------ */ -/* UI variants (оба режима на Flask, отличие только в компоновке UI). */ +/* Кабинетный UI (класс body.ui-legacy на корне). */ /* ------------------------------------------------------------------ */ -/* Modern: плотная колонка и акцент на карточный контент. */ -body.ui-modern .max-w-2xl { - max-width: 42rem !important; -} - -body.ui-modern main { - padding-top: 1.25rem; -} - -/* Legacy: идентичный cabinet layout. */ body.ui-legacy .max-w-2xl { max-width: 42rem !important; } @@ -1133,17 +1123,6 @@ body.ui-legacy .attempts-card-list { border-color: color-mix(in srgb, var(--primary, #007168) 25%, transparent); } -body.ui-modern .version-item { - background: #fff; - border-color: rgba(15, 23, 42, 0.08); -} -body.ui-modern .version-item[data-active="1"] { - background: color-mix(in srgb, var(--brand-600, #6366f1) 6%, #fff); - border-color: color-mix(in srgb, var(--brand-600, #6366f1) 28%, transparent); -} -body.ui-modern .version-item__badge { - background: var(--brand-600, #6366f1); -} body.ui-legacy #versions-section { padding: 0.75rem 1rem; } @@ -1176,7 +1155,7 @@ body.ui-legacy .attempts-card-list__action { .attempt-review-page { max-width: 42rem; margin: 0 auto; - padding: 0 0.25rem 2rem; + padding: 0 0.25rem max(2rem, env(safe-area-inset-bottom, 0px)); width: 100%; } @@ -1442,14 +1421,6 @@ body.ui-legacy .attempts-card-list__action { color: #14532d; } -body.ui-modern .attempt-review-page { - padding-bottom: max(2rem, env(safe-area-inset-bottom, 0px)); -} - -body.ui-modern .attempt-review-card { - border-color: rgba(15, 23, 42, 0.08); -} - body.ui-legacy .attempt-review-page { padding-left: 0; padding-right: 0; @@ -1934,21 +1905,6 @@ body.ui-legacy .attempt-review-page { color: #b42318; } -body.ui-modern .attempt-result-card { - --attempt-result-fail-score: var(--brand-600, #6366f1); -} -body.ui-modern .attempt-result-verdict[data-passed="1"] .attempt-result-verdict__label { - color: var(--attempt-result-pass); -} - -body.ui-modern .attempt-flow { - min-height: min(75dvh, 880px); -} - -body.ui-modern .attempt-title { - font-size: 1.25rem; -} - body.ui-legacy .attempt-flow { min-height: min(72dvh, 820px); } diff --git a/flask_app/app/templates/auth/login.html b/flask_app/app/templates/auth/login.html index 3dbca52..5c2f952 100644 --- a/flask_app/app/templates/auth/login.html +++ b/flask_app/app/templates/auth/login.html @@ -3,117 +3,55 @@ {% block content %} {% set fio_as_login = dev_fio_enabled or hr_auth_enabled %} -{% if ui_variant == 'legacy' %} - {% block scripts %}{% endblock %} diff --git a/flask_app/app/templates/settings.html b/flask_app/app/templates/settings.html index 43444cf..f970889 100644 --- a/flask_app/app/templates/settings.html +++ b/flask_app/app/templates/settings.html @@ -2,13 +2,13 @@ {% block title %}Настройки — LLM{% endblock %} {% block content %} -
+
settings

Настройки

-

Подключение к LLM

+

Подключение к LLM

Ключ задаётся в .env сервера (общий, не на пользователя). Поддерживаются DeepSeek и OpenAI-совместимые API. @@ -53,7 +53,7 @@ OPENAI_API_KEY=sk-...

diff --git a/flask_app/app/templates/tests/editor.html b/flask_app/app/templates/tests/editor.html index cf8a13b..9cf2f2a 100644 --- a/flask_app/app/templates/tests/editor.html +++ b/flask_app/app/templates/tests/editor.html @@ -3,7 +3,7 @@ {% block content %}
@@ -277,7 +277,7 @@ {# ── Sticky-footer: «Цепочка активна» + «Сохранить» ────────────── #}
-
-

+

{# ── Шаблон вопроса ─────────────────────────────────────────────── #} diff --git a/flask_app/app/templates/tests/list.html b/flask_app/app/templates/tests/list.html index 990d8ad..f910b97 100644 --- a/flask_app/app/templates/tests/list.html +++ b/flask_app/app/templates/tests/list.html @@ -17,7 +17,6 @@ {%- endmacro %} {% block content %} -{% if ui_variant == 'legacy' %}

Тесты

@@ -77,71 +76,6 @@ {% endif %}
-{% else %} -
-
-
-

Каталог тестов

-

Все активные тесты.

-
- -
- - {% if visible %} - - {% else %} -

Доступных тестов пока нет.

- {% endif %} - - {% if hidden %} -
- - Скрытые из каталога ({{ hidden|length }}) - -
    - {% for t in hidden %} -
  • - {{ t.title }} · v{{ t.version }} - {{ catalog_test_params_line(t) }} - - Открыть -
  • - {% endfor %} -
-
- {% endif %} -
-{% endif %} bool: if __name__ == '__main__': - port = int(os.environ.get('PORT', '3108')) + port = int(os.environ.get('PORT', '3107')) if _use_waitress(): from waitress import serve