Files
TestingWebApp/docs/revision_task/card1.md
T

82 lines
9.8 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Карта задач: Card 1 — история прохождений, документ, авторизация HR
**Связь со спринтами:** основная масса пунктов **V.x****Спринт 1** (версионирование + инфра); **D.x** — загрузка документа (может пересекаться со **Спринтом 2** / AI, если без LLM черновик не делается); **A.x** — авторизация **базой** `Postgres_TG_Bots` (выполнять после/параллельно с V.1, чтобы не плодить дубли пользователей на долгий срок).
**Фактический стек репозитория [TestingWebApp](../../README.md):** Node.js (backend), PostgreSQL, Docker; фронт — SPA в `frontend/`. План доработок **не** привязывать к FastAPI, если в коде API на Express/Node.
---
## Часть V — Версионирование (цель: корректная история при правках автора)
**Правила (приёмка):**
1. Пока по **цепочке теста** (`tests.id`) **не было ни одной** попытки в `test_attempts` (через любую `test_version_id` этой цепочки) — автор **редактирует на месте** текущую единственную строку `test_versions` и дочерние вопросы/ответы; поле `version` **не** увеличивается.
2. Как только появилась **хотя бы одна** попытка — **каждое** сохранение с изменениями контента создаёт **новую** строку `test_versions`: `version = max+1`, `parent_id` → id предыдущей версии, прежняя версия `is_active = false`, новая `is_active = true`; старые вопросы/ответы **копируются** в новую версию.
3. Все версии одной цепочки — общий `test_id`; цепочка линейна по `parent_id` (и/или по монотонному `version` при одном `test_id`).
4. `test_attempts.test_version_id` **NOT NULL** — попытка всегда на снимок версии, разбор старых результатов не «плывёт» при новых правках.
5. Списки тестов для **сотрудников** и **авторов**: только **активная** версия цепочки (`test_versions.is_active` и `tests` не скрыт деактивацией цепочки).
6. **История версий** в UI: просмотр, **ручной** выбор активной версии; при выборе в транзакции: у всех версий данного `test_id` `is_active = false`, у выбранной `is_active = true`. Новые старты попыток — по **текущей** активной версии.
7. **Деактивация теста целиком** — флаг на уровне `tests` (скрыть цепочку из списков, **без** удаления строк).
**Задачи (детализация):**
| ID | Задача | Критерий |
| --- | --- | --- |
| V.1 | Миграция БД: `test_versions.parent_id` (FK на `test_versions.id`, ON DELETE NO ACTION/RESTRICT), частичный уникальный индекс: не более **одной** `is_active = true` на `test_id` (если СУБД поддерживает; иначе — уникальный индекс + проверка в сервисе) | `migrate` отрабатывает пусто на повтор |
| V.2 | Сервис `hasAnyAttemptForTest(testId)` + unit-тесты | Покрыты кейсы: 0 попыток / 1+ по любой версии цепочки |
| V.3 | `saveTestDraft(author, testId, payload)`: ветвление in-place **vs** `forkNewVersion` (копия вопросов/опций) | Соответствие правилам 1–2 |
| V.4 | Старт попытки: в `test_attempts` писать `test_version_id` **той** версии, что была **активна** в момент старта | Нет перезаписи `version_id` позже |
| V.5 | API: `GET /tests` (роль) — только активные цепочки; `GET /tests/:id/versions`; `POST /tests/:id/versions/:vid/activate` | 403/404 по политике |
| V.6 | API: `PATCH /tests/:id` (деактивация цепочки `tests.is_active` или отдельное поле) | Список пустеет, данные на месте |
| V.7 | UI автора: номер/метка версии, предупреждение при «после первой попытки», экран **история версий**, кнопка **сменить активную** (с confirm) | Смоук `sprint-01-testing.md` |
| V.8 | UI списки сотрудника/автора: **один** ряд на цепочку, без дублей версий | — |
| V.9 | Интеграционные тесты API + регресс «разбор старой попытки» по старым `question_id` | — |
| V.10 | *Продукт (зафиксировать в коде/доке):* при **новой** версии, что делаем с `test_assignments` — остаются на старом `test_version_id` / подтягиваем на новый / оба сценария | Решение в `task.md` или ADR one-liner |
---
## Часть D — Загрузка документа → черновик теста
**Цель:** загрузить файл (PDF, DOCX — перечислить лимиты), извлечь текст, **сгенерировать** структуру вопросов (логично тянуть **LLM** из ТЗ §4.2) или дать мастер «вставьте текст».
| ID | Задача | Критерий |
| --- | --- | --- |
| D.1 | Эндпоинт `POST /tests/import/document` (multipart), валидация размера/типа, сохранение во временное хранилище | 413/400 при нарушении |
| D.2 | Извлечение текста: PDF (библиотека на выбор), DOCX (zip/xml) | Юнит на фикстуре |
| D.3 | Вызов слоя **генерации** (если **есть** ключ DeepSeek — → промпт; иначе — заглушка «только вручную») с ответом JSON по схеме вопросов/ответов | Согласовано с §4.2 |
| D.4 | UI: кнопка «Из документа», превью, применение → дальше **тот же** поток сохранения, что и ручной редактор (в т.ч. V.1–V.3) | — |
| D.5 | Удаление временных файлов после обработки / TTL | — |
---
## Часть A — Авторизация по паролю, БД [Postgres_TG_Bots](../../../Postgres_TG_Bots)
**Контекст:** в `Postgres_TG_Bots`/`hr_web_viewer` учёт `users` с полями `username`, `password_hash` (Werkzeug `pbkdf2:sha256` / `scrypt` через `werkzeug.security` — см. `hr_web_viewer/models/user_models.py`).
| ID | Задача | Критерий |
| --- | --- | --- |
| A.1 | Режим `DATABASE_URL` (или `HR_DATABASE_URL`) на **ту же** БД, что и `Postgres_TG_Bots`, read-only **или** отдельный пользователь с `SELECT` на `users` (+ при необходимости `staff_members` для `department_id`) | Пример `.env` в `TestingWebApp` |
| A.2 | Реализация `verifyPassword` в Node, **совместимой** с `check_password_hash` (использовать пакет, понимающий префиксы `pbkdf2:sha256:` и `scrypt:`) | Тест: тот же хеш, что сгенерировал Werkzeug |
| A.3 | Логин: по `username` → взять `id`, `password_hash`; при успехе — JWT/сессия **TestingWebApp** с `user_id` (и маппинг на локальный UUID, если в приложении внутри свои `users`**документировать** стратегию: single source = HR DB **или** sync) | Нет дублирования паролей в две таблицы в долгую |
| A.4 | Отключить/не использовать регистрацию с локальным `password_hash` в прод-режиме, если включён `HR_AUTH=1` | Флаг в `.env` |
| A.5 | Маппинг ролей: `users` не содержит `hr`/`manager` — связать `staff_members.web_login` = `users.username` и взять роль из существующей HR-модели **или** MVP: одна роль + админ-лист | Зафиксировать в README Card 1 |
---
## Порядок работ (рекомендация)
1. **V.1****V.2****V.3** (ядро версий).
2. **A.1**–**A.3** параллельно или сразу после **V.1** (нужен стабильный логин для стенда).
3. **V.4****V.9** и UI **V.7****V.8**.
4. **D.*** после появления клиента LLM (или D.1–D.2 + ручной встав текста без LLM).
5. **V.10** — решение по назначениям до **V.5** если назначения уже в проде.
---
## Ссылки
- ТЗ: [task.md](task.md) §4.1, §4.2
- Спринт 1: [sprint-01.md](sprint-01.md)
- Тестирование: [sprint-01-testing.md](sprint-01-testing.md)
- Анализ таблиц (если ведёте): [TEST_TABLES_ANALYSIS.md](../TEST_TABLES_ANALYSIS.md)