Files
TestingWebApp/docs/TEST_TABLES_ANALYSIS.md
T
Константин Лебединский a68331c86b docs: статус проекта, инструкция dev, обновление всех .md
- PROJECT_STATUS: что сделано (черновики, версии, разбор, каталог) и планы
- DEV_CONTOUR_USER_GUIDE: сценарии для проверяющих на dev-стенде
- README, ТЗ, card1, журнал, бэклоги, шаги 01–11+README, спринты, TEST_TABLES: ссылки и примечания
- backend/PROGRESS: ссылка на PROJECT_STATUS

Made-with: Cursor
2026-04-24 22:12:06 +05:00

381 lines
16 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.
# Анализ таблиц для тестирования сотрудников
*Модуль **TestingWebApp** использует отдельную БД `clinic_tests` (см. [PROJECT_STATUS.md](PROJECT_STATUS.md) и [README.md](../README.md)). Ниже — разбор **наследуемых** / смежных сущностей в другой схеме, для сравнения и миграционных дискуссий.*
## Обзор существующих таблиц
В базе данных существуют следующие таблицы, связанные с тестированием:
### 1. [`training_questions`](hr_web_viewer/models.py) - Вопросы обучения
| Колонка | Тип | Описание |
|---------|-----|----------|
| `id` | integer | Первичный ключ |
| `position` | text | Должность/категория |
| `test_type` | text | Тип темы/теста |
| `question` | text | Текст вопроса |
| `answer_1` - `answer_12` | text | Варианты ответов |
| `answer_count` | smallint | Количество правильных ответов |
**Проблемы:**
- Отсутствует явное указание правильного ответа
- Нет типа вопроса (одиночный/множественный выбор, текстовый, сопоставление)
- Нет баллов за вопрос
- Нет порядка вопросов
- Поле `position` используется как категория, но не связано с должностями
### 2. [`training_results`](hr_web_viewer/models.py) - Результаты обучения
| Колонка | Тип | Описание |
|---------|-----|----------|
| `id` | integer | Первичный ключ |
| `telegram_id` | bigint | ID сотрудника |
| `correct_answers` | integer | Правильные ответы |
| `total_questions` | integer | Всего вопросов |
| `score` | integer | Балл |
| `completed_at` | timestamp | Дата завершения |
| `passed` | boolean | Пройден/не пройден |
**Индексы:**
- `idx_training_results_telegram_id` - по telegram_id
**Проблемы:**
- Нет связи с конкретным тестом (test_type)
- Нет количества попыток
- Нет детализации по ответам
### 3. [`training_settings`](hr_web_viewer/models.py) - Настройки обучения
| Колонка | Тип | Описание |
|---------|-----|----------|
| `id` | integer | Первичный ключ |
| `position` | varchar(100) | Должность |
| `question_count` | integer | Количество вопросов (по умолчанию 10) |
| `passing_score` | integer | Проходной балл (по умолчанию 70) |
| `time_limit` | integer | Ограничение времени в минутах (по умолчанию 30) |
| `active` | boolean | Активен/неактивен |
**Индексы:**
- `idx_training_settings_position` - по position
**Проблемы:**
- Нет связи с категорией теста
- Нет ограничения количества попыток
- Нет настройки случайного порядка вопросов
### 4. [`test_assignments`](hr_web_viewer/models.py) - Назначения тестов
| Колонка | Тип | Описание |
|---------|-----|----------|
| `id` | integer | Первичный ключ |
| `la_name` | text | Название адаптации |
| `intern_fio` | text | ФИО стажера |
| `user_credentials` | text | Учетные данные |
| `test_theme` | text | Тема теста |
| `test_subtheme` | text | Подтема теста |
| `attempts_allowed` | integer | Количество попыток |
| `passing_score` | integer | Проходной балл |
| `la_id` | integer | Ссылка на адаптацию |
| `intern_id` | bigint | ID сотрудника (staff_members.id) |
| `deadline` | timestamp | Срок сдачи |
**Внешние ключи:**
- `intern_id` -> `staff_members(id)`
- `la_id` -> `learning_adaptations(id)`
**Проблемы:**
- Назначения привязаны к конкретным сотрудникам, а не к должностям
- Нет статуса прохождения
- Нет связи с результатами
### 5. [`test_table`](hr_web_viewer/models.py) - Таблица тестов
| Колонка | Тип | Описание |
|---------|-----|----------|
| `id` | integer | Первичный ключ |
| `name` | varchar(100) | Название теста |
**Проблемы:**
- Минимальная структура, практически не используется
### 6. [`corp_groups_tester`](hr_web_viewer/models.py) - Тестировщики (корпоративные группы)
| Колонка | Тип | Описание |
|---------|-----|----------|
| `id` | integer | Первичный ключ |
| `fio` | text | ФИО |
| `telegram_id` | varchar(20) | Telegram ID |
| `position` | varchar(200) | Должность |
| `department` | varchar(200) | Отдел |
| `phone` | varchar(20) | Телефон |
| `email` | varchar(100) | Email |
| `hire_date` | date | Дата приема |
**Проблемы:**
- Дублирует данные staff_members
- Не используется в текущей системе
---
## Рекомендуемая расширенная схема для ClinicTestingApp
### Новые таблицы
#### 1. `tests` - Основные тесты
```sql
CREATE TABLE tests (
id SERIAL PRIMARY KEY,
name VARCHAR(255) NOT NULL, -- Название теста
description TEXT, -- Описание
category VARCHAR(100), -- Категория (тема)
position VARCHAR(100), -- Должность (nullable - для всех)
question_count INTEGER DEFAULT 10, -- Количество вопросов в тесте
time_limit_minutes INTEGER, -- Ограничение времени (null = без ограничений)
attempts_allowed INTEGER DEFAULT 3, -- Количество попыток
passing_score_percent INTEGER DEFAULT 70, -- Проходной процент
random_questions BOOLEAN DEFAULT FALSE, -- Случайный порядок вопросов
is_active BOOLEAN DEFAULT TRUE, -- Активен
created_by INTEGER, -- ID администратора
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_tests_category ON tests(category);
CREATE INDEX idx_tests_position ON tests(position);
```
#### 2. `test_questions` - Вопросы тестов
```sql
CREATE TABLE test_questions (
id SERIAL PRIMARY KEY,
test_id INTEGER NOT NULL REFERENCES tests(id) ON DELETE CASCADE,
question_text TEXT NOT NULL, -- Текст вопроса
question_type VARCHAR(50) NOT NULL, -- single_choice, multiple_choice, text, matching, ordering
points INTEGER DEFAULT 1, -- Баллы за вопрос
question_order INTEGER, -- Порядок вопроса
explanation TEXT, -- Пояснение к ответу
is_active BOOLEAN DEFAULT TRUE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_test_questions_test_id ON test_questions(test_id);
```
#### 3. `test_answers` - Ответы на вопросы
```sql
CREATE TABLE test_answers (
id SERIAL PRIMARY KEY,
question_id INTEGER NOT NULL REFERENCES test_questions(id) ON DELETE CASCADE,
answer_text TEXT NOT NULL, -- Текст ответа
is_correct BOOLEAN DEFAULT FALSE, -- Правильный ответ
answer_order INTEGER, -- Порядок (для сопоставления/порядка)
points_if_correct INTEGER DEFAULT 1 -- Баллы (если отличаются от question.points)
);
CREATE INDEX idx_test_answers_question_id ON test_answers(question_id);
```
#### 4. `test_assignments_extended` - Расширенные назначения тестов
```sql
CREATE TABLE test_assignments_extended (
id SERIAL PRIMARY KEY,
test_id INTEGER NOT NULL REFERENCES tests(id) ON DELETE CASCADE,
staff_id INTEGER NOT NULL REFERENCES staff_members(id) ON DELETE CASCADE,
assigned_by INTEGER, -- ID администратора
assigned_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
deadline TIMESTAMP, -- Срок сдачи
attempts_allowed INTEGER, -- Переопределение количества попыток (null = из теста)
status VARCHAR(50) DEFAULT 'pending', -- pending, in_progress, completed, expired
UNIQUE(test_id, staff_id)
);
CREATE INDEX idx_test_assignments_test_id ON test_assignments_extended(test_id);
CREATE INDEX idx_test_assignments_staff_id ON test_assignments_extended(staff_id);
```
#### 5. `test_attempts` - Попытки прохождения
```sql
CREATE TABLE test_attempts (
id SERIAL PRIMARY KEY,
assignment_id INTEGER NOT NULL REFERENCES test_assignments_extended(id) ON DELETE CASCADE,
attempt_number INTEGER NOT NULL, -- Номер попытки
started_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
completed_at TIMESTAMP, -- Завершена
score_points INTEGER, -- Набрано баллов
score_percent NUMERIC(5,2), -- Процент
passed BOOLEAN, -- Пройден/не пройден
time_spent_seconds INTEGER -- Потраченное время
);
CREATE INDEX idx_test_attempts_assignment_id ON test_attempts(assignment_id);
```
#### 6. `test_answers_given` - Ответы пользователя
```sql
CREATE TABLE test_answers_given (
id SERIAL PRIMARY KEY,
attempt_id INTEGER NOT NULL REFERENCES test_attempts(id) ON DELETE CASCADE,
question_id INTEGER NOT NULL REFERENCES test_questions(id) ON DELETE CASCADE,
given_answer_ids INTEGER[], -- ID выбранных ответов (для choice)
given_text TEXT, -- Текстовый ответ
is_correct BOOLEAN, -- Правильный/неправильный
points_earned INTEGER, -- Полученные баллы
answered_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
CREATE INDEX idx_test_answers_given_attempt_id ON test_answers_given(attempt_id);
```
#### 7. `test_categories` - Категории тестов
```sql
CREATE TABLE test_categories (
id SERIAL PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
description TEXT,
parent_id INTEGER REFERENCES test_categories(id),
is_active BOOLEAN DEFAULT TRUE
);
```
#### 8. `test_reports` - Сформированные отчеты
```sql
CREATE TABLE test_reports (
id SERIAL PRIMARY KEY,
report_type VARCHAR(50) NOT NULL, -- department, employee, category
parameters JSONB, -- Параметры отчета
file_path VARCHAR(500), -- Путь к файлу
format VARCHAR(10), -- pdf, xlsx
generated_by INTEGER, -- ID администратора
generated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
```
---
## Расширение существующих таблиц (миграции)
### training_questions
```sql
-- Добавить тип вопроса
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS question_type VARCHAR(50) DEFAULT 'single_choice';
-- Добавить баллы
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS points INTEGER DEFAULT 1;
-- Добавить правильный ответ (индекс)
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS correct_answer_index INTEGER;
-- Добавить порядок
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS sort_order INTEGER;
-- Добавить пояснение
ALTER TABLE training_questions ADD COLUMN IF NOT EXISTS explanation TEXT;
```
### training_results
```sql
-- Добавить связь с тестом
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS test_id INTEGER REFERENCES tests(id);
-- Добавить номер попытки
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS attempt_number INTEGER DEFAULT 1;
-- Добавить время прохождения
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS time_spent_seconds INTEGER;
-- Добавить детализацию ответов (JSON)
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS answers_detail JSONB;
```
### training_settings
```sql
-- Добавить связь с тестом
ALTER TABLE training_settings ADD COLUMN IF NOT EXISTS test_id INTEGER REFERENCES tests(id);
-- Добавить категорию
ALTER TABLE training_settings ADD COLUMN IF NOT EXISTS category VARCHAR(100);
-- Добавить случайный порядок
ALTER TABLE training_settings ADD COLUMN IF NOT EXISTS random_order BOOLEAN DEFAULT FALSE;
```
---
## Связь с staff_members
Текущая проблема: используется `telegram_id` для связи с сотрудниками.
Решение: перейти на использование `staff_members.id` как универсального идентификатора:
```sql
-- Добавить staff_id в training_results
ALTER TABLE training_results ADD COLUMN IF NOT EXISTS staff_id INTEGER REFERENCES staff_members(id);
-- Миграция данных
UPDATE training_results tr
SET staff_id = sm.id
FROM staff_members sm
WHERE tr.telegram_id = sm.telegram_id;
-- Создать внешний ключ после миграции
ALTER TABLE training_results
ADD CONSTRAINT training_results_staff_id_fkey
FOREIGN KEY (staff_id) REFERENCES staff_members(id);
```
---
## Типы вопросов
| Тип | Код | Описание |
|-----|-----|----------|
| Одиночный выбор | `single_choice` | Один правильный ответ из нескольких |
| Множественный выбор | `multiple_choice` | Несколько правильных ответов |
| Текстовый ответ | `text` | Свободный текст |
| Сопоставление | `matching` | Сопоставление пар |
| Порядок элементов | `ordering` | Расстановка в правильном порядке |
---
## API Endpoints (рекомендуемые)
### Тесты
- `GET /api/tests` - Список тестов
- `POST /api/tests` - Создать тест
- `GET /api/tests/{id}` - Получить тест с вопросами
- `PUT /api/tests/{id}` - Обновить тест
- `DELETE /api/tests/{id}` - Удалить тест
### Вопросы
- `GET /api/tests/{test_id}/questions` - Список вопросов
- `POST /api/tests/{test_id}/questions` - Добавить вопрос
- `PUT /api/questions/{id}` - Обновить вопрос
- `DELETE /api/questions/{id}` - Удалить вопрос
### Назначения
- `GET /api/assignments` - Список назначений
- `POST /api/assignments` - Назначить тест
- `GET /api/employees/{id}/assignments` - Назначения сотрудника
### Прохождение
- `POST /api/tests/{id}/start` - Начать тест
- `POST /api/attempts/{id}/answer` - Ответить на вопрос
- `POST /api/attempts/{id}/complete` - Завершить тест
### Отчеты
- `GET /api/reports/department` - Отчет по отделениям
- `GET /api/reports/employee/{id}` - Отчет по сотруднику
- `GET /api/reports/category/{id}` - Отчет по категории
- `GET /api/reports/export` - Экспорт отчета (PDF/Excel)