# Анализ таблиц для тестирования сотрудников *Модуль **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)