Начисление баллов и ответы на вопросы

Режимы оценивания (всё-или-ничего и частичный), формулы начисления баллов для вопросов с несколькими вариантами и на соответствие, и как корректно передавать ответы по каждому типу вопроса.

Режимы оценивания

У теста есть режим по умолчанию default_scoring_mode, а у каждого вопроса — необязательный scoring_mode, который его перекрывает. Если на вопросе режим не задан (null), применяется режим теста.

РежимКак считаетсяГде доступен
all_or_nothingПолный балл только за полностью верный ответ, иначе 0.Все типы вопросов
partialЧастичный балл — пропорционально верным элементам ответа.Только multiple и matching
Режим задаётся при создании теста (default_scoring_mode) и переопределяется на вопросе (scoring_mode). Пустая строка "" в scoring_mode сбрасывает переопределение — вопрос снова наследует режим теста. Для single и free_text частичный режим запрещён (проверяется и на сервере, и в БД).

Множественный выбор (multiple): частичный режим

В частичном режиме балл за вопрос с несколькими верными вариантами считается так:

Формула
балл = вес × max(0, (верных − неверных) / всего_верных)

верных        — сколько выбрано верных вариантов
неверных      — сколько выбрано неверных вариантов
всего_верных  — сколько всего верных вариантов в вопросе

Каждый неверный выбор гасит один верный — это штраф за угадывание. Результат не опускается ниже 0. Полный вес начисляется только при точном совпадении: выбраны все верные варианты и ни одного лишнего.

Поле is_correct у ответа становится true только при точном совпадении (полный балл). Частично верный ответ приносит баллы, но is_correct остаётся false — поэтому в аналитике он не считается «решённым».

Пример: вопрос на 10 баллов, 3 верных варианта (C1, C2, C3) и 2 неверных (W1, W2).

Выбор студентаРежим partialРежим all_or_nothing
C1 + C2 + C3 (все верные)10.00 — верно10.00 — верно
C1 + C2 (2 из 3 верных)(2−0)/3 × 10 = 6.67 — неверно0.00 — неверно
C1 + W1 (1 верный, 1 неверный)(1−1)/3 × 10 = 0.000.00
C1 + C2 + W1 + W2(2−2)/3 × 10 = 0.000.00
только W1(0−1)/3 → 0.00 (не ниже нуля)0.00
ничего не выбрано0.000.00
Если в вопросе не настроено ни одного верного варианта, любой ответ оценивается в 0 (защита от деления на ноль).

Вопросы на соответствие (matching): частичный режим

Идентифицирующие вопросы (тип matching, «установление соответствия») сопоставляют пункты левого столбца с вариантами правого. В частичном режиме:

Формула
балл = вес × верных_пар / всего_пар

верных_пар — сколько пар сопоставлено правильно
всего_пар  — число реальных пар (дистракторы не считаются)

В отличие от множественного выбора, штрафа за неверную пару нет — балл просто пропорционален числу правильно сопоставленных пар. Строки-дистракторы (есть только правый вариант, без левого) в знаменатель не входят.

В режиме all_or_nothing полный балл начисляется, только если верны все реальные пары и ни одна не пропущена. Пример: 2 реальные пары, вес 4 балла, верно сопоставлена 1 → partial даёт 4 × 1/2 = 2.00, а all_or_nothing0.00.

Как передавать ответы на идентифицирующие вопросы

Ответ на matching-вопрос передаётся в Сохранить ответы как массив пар pairs:

payload для matching
{
  "answers": [
    {
      "question_id": "<id вопроса>",
      "payload": {
        "pairs": [
          { "left_id": "<id из match_lefts>", "right_id": "<токен из match_rights>" }
        ]
      }
    }
  ]
}

Идентификаторы берутся из ответа GET /api/v1/t/quizzes/:id, полученного под ролью студента (студенческое представление вопроса):

  • left_id — это id элемента из match_lefts[] (реальный идентификатор левого пункта).
  • right_id — это id элемента из match_rights[]. Правый столбец перемешан, а его id — это одноразовый токен, а не реальный match.id. Сервер на отправке восстанавливает из токена настоящий идентификатор.
Откуда брать right_id
right_id — это токен из match_rights[].id студенческого представления, а НЕ сырой match.id из кураторского ответа (matches[].id в GET /quizzes/:id под куратором). Если прислать сырой id, токен не сопоставится, пара тихо засчитается неверной и студент получит 0 без какой-либо ошибки. Отправляйте пары только для тех left_id, что показаны в match_lefts — дистракторы присутствуют только в правом столбце.
Пример запроса
curl -X POST https://api.edumentor.kz/api/v1/t/attempts/<attempt_id>/answers \
  -H "Authorization: Bearer <token>" \
  -H "X-Tenant-Subdomain: acme" \
  -H "Content-Type: application/json" \
  -d '{
    "answers": [
      {
        "question_id": "a1b2c3d4-...",
        "payload": {
          "pairs": [
            { "left_id": "11111111-...", "right_id": "ffffffff-..." },
            { "left_id": "22222222-...", "right_id": "eeeeeeee-..." }
          ]
        }
      }
    ]
  }'

Для совместимости со старыми клиентами сервер также принимает синонимы ключей: конверт pairs либо matches; внутри пары — left_id/left и right_id/right. Канонична форма pairs + left_id/right_id.

Ответы на остальные типы вопросов

ТипpayloadПринимаемые синонимы ключа
single{ "selected_answer_id": "<id>" }selected_option_id, answer_id, answer_ids (берётся первый)
multiple{ "selected_answer_ids": ["<id>", …] }selected_option_ids, answer_ids
free_text{ "text": "..." }
matching{ "pairs": [{ "left_id", "right_id" }] }matches; left/right

Идентификаторы вариантов (selected_answer_id, selected_answer_ids) — это id из answers[] студенческого представления вопроса. Сервер декодирует payload по типу вопроса; отправка ответов в уже завершённую попытку даёт 409 attempt_already_submitted.