Прохождение тестов
Студенческий поток прохождения теста: начать попытку, прочитать текущее состояние, сохранить ответы (merge по question_id) и отправить попытку на автооценку. Все эндпоинты требуют авторизации; дополнительного гейта прав нет.
/api/v1/t/attemptsНачать попытку
Создаёт новую попытку прохождения теста. Идентификатор пользователя и его роль берутся из JWT, а не из тела запроса — клиент не может подменить личность. Роль на момент старта сохраняется как actor_role для последующей аналитики.
Стартовать попытку могут student/curator/admin одной школы. При исчерпании лимита попыток возвращается 409 max_attempts_reached. Если урок с этим тестом ограничен по дате (расписание курса), новая попытка после дедлайна возвращает 403 schedule_window_closed — КРОМЕ студентов, начавших хотя бы одну попытку до дедлайна (правило «первой попытки»). Админ/куратор это ограничение обходят.
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
quiz_idобяз. | uuid | Идентификатор теста. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/attempts \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{ "quiz_id": "9c1f2e3a-..." }'Пример ответа
{
"success": true,
"data": {
"id": "11112222-...",
"quiz_id": "9c1f2e3a-...",
"user_id": "33334444-...",
"status": "in_progress",
"actor_role": "student",
"score": "0.00",
"max_score": "0.00",
"passed": null,
"started_at": "2026-05-28T09:00:00Z",
"deadline_at": "2026-05-28T09:30:00Z",
"submitted_at": null,
"auto_finalized": false
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_request_body | Тело запроса не прошло валидацию (отсутствует quiz_id). |
| 400 | invalid_actor_role | Роль в JWT некорректна — попытка отклонена. |
| 401 | unauthorized | Токен отсутствует или недействителен. |
| 403 | schedule_window_closed | Урок теста вне окна доступа по расписанию, и правило «первой попытки» не применимо (студент не начинал попытку до дедлайна). |
| 404 | quiz_not_found | Тест не найден или принадлежит другой школе. |
| 409 | max_attempts_reached | Исчерпан лимит попыток для этого теста. |
/api/v1/t/attempts/:idПолучить попытку
Возвращает текущее состояние попытки. Владелец (ученик-автор попытки) видит её всегда. Куратор/админ могут просматривать попытку в рамках своих прав (менеджер-куратор или куратор, у которого ученик в его группе); прочим возвращается 404, чтобы не раскрывать существование чужих попыток. Дополнительно в ответе возвращается saved_answers — массив сохранённых ответов студента для восстановления выбранных вариантов при возобновлении/обновлении; поле опускается, когда ещё ничего не сохранено. Каждый элемент несёт также awarded_points и is_correct — вердикт по вопросу (0 / false, пока попытка in_progress; после submit — фактически начисленные баллы и правильность).
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор попытки. |
Пример запроса
curl https://api.edumentor.kz/api/v1/t/attempts/11112222-... \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "11112222-...",
"quiz_id": "9c1f2e3a-...",
"user_id": "33334444-...",
"status": "submitted",
"actor_role": "student",
"score": "2.00",
"max_score": "3.00",
"passed": true,
"started_at": "2026-05-28T09:00:00Z",
"deadline_at": "2026-05-28T09:30:00Z",
"submitted_at": "2026-05-28T09:12:00Z",
"auto_finalized": false,
"saved_answers": [
{
"question_id": "a1b2c3d4-...",
"payload": { "answer_ids": ["..."] },
"awarded_points": "2.00",
"is_correct": true
}
]
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | id не является корректным UUID. |
| 401 | unauthorized | Токен отсутствует или недействителен. |
| 404 | attempt_not_found | Попытка не найдена, принадлежит другой школе или недоступна вызывающему. |
/api/v1/t/attempts/:id/answersСохранить ответы
Сохраняет ответы по вопросам. Каждая запись upsert-ится по паре (attempt_id, question_id), поэтому повторные вызовы безопасно сливаются (PATCH-семантика). Возвращает число сохранённых ответов.
Форма payload зависит от типа вопроса, причём принимается ЛЮБОЙ из поддерживаемых ключей (это устранило баг, когда ответы с ключом answer_ids не засчитывались и итоговый результат был 0). Для single_choice — answer_ids (массив, первый элемент берётся как выбранный), selected_answer_id или selected_option_id. Для multiple_choice — answer_ids, selected_answer_ids или selected_option_ids. Для free_text — { text }. Для matching — массив пар { left_id, right_id }, где left_id берётся из match_lefts[].id, а right_id — из match_rights[].id студенческого представления вопроса (это токен, НЕ сырой match.id). Подробно про формулы баллов и формат ответов — в разделе Начисление баллов и ответы на вопросы. Сервер декодирует payload по типу вопроса. Сохранение в уже отправленную попытку даёт 409 attempt_already_submitted.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор попытки. |
Тело запроса
| Параметр | Тип | Описание |
|---|---|---|
answersобяз. | object | Массив { question_id (uuid), payload (object — зависит от типа вопроса) }; минимум 1. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/attempts/11112222-.../answers \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme" \
-H "Content-Type: application/json" \
-d '{
"answers": [
{ "question_id": "a1b2c3d4-...", "payload": { "answer_ids": ["..."] } },
{ "question_id": "e5f6a7b8-...", "payload": { "text": "Мой ответ" } }
]
}'Пример ответа
{
"success": true,
"data": { "saved": 2 }
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_request_body | Тело запроса не прошло валидацию. |
| 400 | invalid_id_format | id попытки не является корректным UUID. |
| 400 | question_not_in_quiz | Один из question_id не относится к тесту этой попытки. |
| 401 | unauthorized | Токен отсутствует или недействителен. |
| 404 | attempt_not_found | Попытка не найдена или принадлежит другой школе. |
| 404 | quiz_not_found | Тест попытки не найден. |
| 409 | attempt_already_submitted | Попытка уже отправлена — изменять ответы нельзя. |
/api/v1/t/attempts/:id/submitОтправить попытку
Завершает попытку и запускает автооценку. Тело запроса игнорируется полностью — любые присланные клиентом score/passed/submitted_at отбрасываются; источником истины по баллу является сервер. Возвращает обновлённую попытку с итоговым баллом.
Для тестов со свободными ответами (free_text) итоговый балл может быть неполным до проверки куратором. Повторная отправка даёт 409 attempt_already_submitted.
Параметры пути
| Параметр | Тип | Описание |
|---|---|---|
idобяз. | uuid | Идентификатор попытки. |
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/attempts/11112222-.../submit \
-H "Authorization: Bearer <token>" \
-H "X-Tenant-Subdomain: acme"Пример ответа
{
"success": true,
"data": {
"id": "11112222-...",
"quiz_id": "9c1f2e3a-...",
"user_id": "33334444-...",
"status": "submitted",
"actor_role": "student",
"score": "8.00",
"max_score": "10.00",
"passed": true,
"started_at": "2026-05-28T09:00:00Z",
"deadline_at": "2026-05-28T09:30:00Z",
"submitted_at": "2026-05-28T09:25:00Z",
"auto_finalized": false
}
}Ошибки
| Код | error | Когда |
|---|---|---|
| 400 | invalid_id_format | id попытки не является корректным UUID. |
| 401 | unauthorized | Токен отсутствует или недействителен. |
| 404 | attempt_not_found | Попытка не найдена или принадлежит другой школе. |
| 404 | quiz_not_found | Тест попытки не найден. |
| 409 | attempt_already_submitted | Попытка уже отправлена. |