16 KiB
Карта задач: Card 1 — история прохождений, документ, авторизация HR
Связь со спринтами: основная масса пунктов V.x — Спринт 1 (версионирование + инфра); D.x — загрузка документа (может пересекаться со Спринтом 2 / AI, если без LLM черновик не делается); A.x — авторизация базой Postgres_TG_Bots (выполнять после/параллельно с V.1, чтобы не плодить дубли пользователей на долгий срок).
Фактический стек репозитория TestingWebApp: Node.js (backend), PostgreSQL, Docker; фронт — SPA в frontend/. План доработок не привязывать к FastAPI, если в коде API на Express/Node.
Хранение, связь с сотрудниками, RBAC (зафиксировано)
- Один кластер PostgreSQL (как в Postgres_TG_Bots): отдельные базы —
clinic_tests(тесты, назначения, попытки, миграции модуля) иhr_bot_test(штат, справочники, уже реализованный RBAC). Таблицы модуля тестов не вешаем вhr_bot_test, чтобы не конфликтовать по именам (users,departmentsи т.д. уже заняты смыслами HR). - Сотрудник в бизнес-процессах модуля тестирования идентифицируется по
staff_members.id(БДhr_bot_test/ экосистема hr_web_viewer). Вclinic_testsхраним суррогатные ссылки на сотрудника (напримерstaff_id/ UUID той же сущности), без дублирования ФИО и кадровых данных в долгую. telegram_idв данных сотрудника — только справка (в т.ч. для исторических выгрузок, отображения). Ни назначения, ни проверок прав, ни выбор сотрудника в сценариях модуля отtelegram_idне зависят и не должны зависеть.- RBAC: единая цель — опираться на уже существующую в клинике модель (роли, привязки к сотруднику, permissions). Проверка «можно ли действие» в конечном варианте — через HR API / общий auth-контур и/или согласованное чтение RBAC-таблиц; в
clinic_testsне строим параллельную полную матрицу ролей. На переходных этапах допустимы упрощения (MVP-флаги), пока в контракте не зафиксировано иное.
Часть V — Версионирование (цель: корректная история при правках автора)
Правила (приёмка):
- Пока по цепочке теста (
tests.id) не было ни одной попытки вtest_attempts(через любуюtest_version_idэтой цепочки) — автор редактирует на месте текущую единственную строкуtest_versionsи дочерние вопросы/ответы; полеversionне увеличивается. - Как только появилась хотя бы одна попытка — каждое сохранение с изменениями контента создаёт новую строку
test_versions:version = max+1,parent_id→ id предыдущей версии, прежняя версияis_active = false, новаяis_active = true; старые вопросы/ответы копируются в новую версию. - Все версии одной цепочки — общий
test_id; цепочка линейна поparent_id(и/или по монотонномуversionпри одномtest_id). test_attempts.test_version_idNOT NULL — попытка всегда на снимок версии, разбор старых результатов не «плывёт» при новых правках.- Списки тестов для сотрудников и авторов: только активная версия цепочки (
test_versions.is_activeиtestsне скрыт деактивацией цепочки). - История версий в UI: просмотр, ручной выбор активной версии; при выборе в транзакции: у всех версий данного
test_idis_active = false, у выбраннойis_active = true. Новые старты попыток — по текущей активной версии. - Деактивация теста целиком — флаг на уровне
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 списки сотрудника/автора: один ряд на цепочку, без дублей версий | GET /tests/:id/summary + упрощённая карточка для не-автора; список GET /tests с JOIN на активную версию |
| V.9 | Интеграционные тесты API + регресс «разбор старой попытки» по старым question_id |
backend/src/integration/v9card1.test.js при CLINIC_TESTS_INTEGRATION=1 и миграциях; без БД — skip |
| V.10 | Продукт: при новой версии test_assignments не переносим на новый test_version_id; старт попытки — по активной версии (см. task.md §2.6) |
Зафиксировано в ТЗ |
Часть 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/hr_web_viewer учёт users с полями username, password_hash (Werkzeug pbkdf2:sha256 / scrypt через werkzeug.security — см. hr_web_viewer/models/user_models.py). Идентичность сотрудника для сценариев тестов — по staff_members.id (см. блок «Хранение…» выше); telegram_id не используем в логике входа, назначений и прав.
| ID | Задача | Критерий |
|---|---|---|
| A.1 | Вторичное соединение: HR_DATABASE_URL (или DSN) к hr_bot_test, read-only или отдельный пользователь с SELECT на users и staff_members (для связки логин → staff_id, подразделение и т.д.) отдельно от DATABASE_URL в clinic_tests |
Пример .env в TestingWebApp |
| A.2 | Реализация verifyPassword в Node, совместимой с check_password_hash (использовать пакет, понимающий префиксы pbkdf2:sha256: и scrypt:) |
Тест: тот же хеш, что сгенерировал Werkzeug |
| A.3 | Логин: по username → users в hr_bot_test; при успехе — токен TestingWebApp с привязкой к staff_members.id (и при необходимости к локальному users в clinic_tests только как технический mirror без отдельного жизненного цикла пароля). Пароли только в HR-таблице users |
Нет дублирования паролей в долгую |
| A.4 | Отключить/не использовать регистрацию с локальным password_hash в прод-режиме, если включён HR_AUTH=1 |
Флаг в .env |
| A.5 | Маппинг ролей: взять из существующей RBAC-модели HR (см. staff_role_assignments / roles & permissions) или согласованный вызов HR API; MVP — ограниченный набор, без опоры на telegram_id |
README — данные и интеграция |
Порядок работ (рекомендация)
- V.1 → V.2 → V.3 (ядро версий).
- A.1–A.3 параллельно или сразу после V.1 (нужен стабильный логин для стенда).
- V.4–V.9 и UI V.7–V.8.
- D.* после появления клиента LLM (или D.1–D.2 + ручной встав текста без LLM).
- V.10 — решение по назначениям до V.5 если назначения уже в проде.
Ссылки
- ТЗ: task.md §4.1, §4.2
- Спринт 1: sprint-01.md
- Проверки и журнал: TESTING_JOURNAL.md
- Старый чек-лист: sprint-01-testing.md
- Анализ таблиц (если ведёте): TEST_TABLES_ANALYSIS.md
Статус реализации (сводно, 2026-04-25+)
| ID | Статус | Комментарий |
|---|---|---|
| V.1–V.6, V.7, V.10 | Код + API + UI приёмка | TESTING_JOURNAL S1-10, S1-11; публикация, скрытые, история. |
| V.8 | Расширен MVP | GET /api/tests — только свои (created_by) и тесты с назначением на users.id (test_accessService.js). Карточка/попытка/starter/chain — та же логика. Старт «Пройти» в списке. Назначение: POST /api/tests/:id/assign (только автор), при production — CLINIC_ASSIGNMENT_ENABLED=1 (featureFlags.js); в development включено. UI: блок «Назначение сотрудникам» + GET /api/auth/dev/assignment-directory при assignmentUi из /api/auth/me. |
| V.9 | Частично | + v9card1.test.js: 0 попыток → in-place; после попытки → форк, старая попытка на старых version_id / question_id. Запуск: CLINIC_TESTS_INTEGRATION=1 + DATABASE_URL (или DB_*), npm run migrate. |
| D.1 | Готово | POST /tests/import/document, 400/413, multipart. |
| D.2 | Готово | documentExtractService.js: PDF (pdf-parse), DOCX (mammoth), TXT/MD. |
| D.3 | MVP (импорт) | documentGenService.js: вызов Chat Completions (DeepSeek по умолчанию или OpenAI), JSON-черновик → generation.draft в POST /import/document; LLM_NO_JSON=1 при несовместимости API. |
| D.4 | MVP | Карточка теста: выбор файла, превью, «Вставить в поле вопроса» → тот же черновик. |
| D.5 | Готово | Временный файл удаляется после чтения; Nginx client_max_body_size 10m. |
| A.1–A.4 | Код + compose | HR_AUTH + HR_DATABASE_URL в docker-compose.dev.yml. |
| A.5 | MVP | mapHrRoleToApp без полного RBAC из HR-таблиц. |
| UI | 2026-04+ | Список тестов: подпись автора (Вы / Фамилия И. О.); шапка: Фамилия И. О. Разбор попыток: API + UI после submit и маршрут /tests/:id/attempts/:aid/review (PROJECT_STATUS.md). |
Следующий шаг по card1: V.9 — довести supertest/HTTP-регресс при необходимости; D.3+ — отдельные кнопки в редакторе (сгенерировать/проверить/улучшить), ключ в БД, /settings (см. sprint-02); A.5 — staff_role_assignments / HR API; по желанию назначения по отделу. Навигация по сценариям: DEV_CONTOUR_USER_GUIDE.md.