Files
challenge-admin-pl/docs/API_CHAIN_SUBMISSIONS.md

7.5 KiB
Raw Permalink Blame History

Техническое задание: Эндпоинт получения попыток по цепочке

Цель

Создать новый API эндпоинт для получения списка попыток (submissions) участников в рамках конкретной цепочки заданий. Это упростит работу админ-панели и уменьшит объём передаваемых данных.

Текущая проблема

Сейчас для отображения попыток по цепочке фронтенд должен:

  1. Загрузить список цепочек (GET /challenge/chains/admin)
  2. Загрузить общую статистику (GET /challenge/stats/v2)
  3. Для каждого участника отдельно загрузить его submissions (GET /challenge/user/:userId/submissions)
  4. На клиенте фильтровать submissions по taskIds из выбранной цепочки

Это создаёт избыточные запросы и усложняет логику на фронтенде.


Новый эндпоинт

GET /challenge/chain/:chainId/submissions

Возвращает все попытки всех участников для заданий из указанной цепочки.

Параметры URL

Параметр Тип Обязательный Описание
chainId string Да ID цепочки заданий

Query параметры (опциональные)

Параметр Тип По умолчанию Описание
userId string - Фильтр по конкретному пользователю
status string - Фильтр по статусу: pending, in_progress, accepted, needs_revision
limit number 100 Лимит записей
offset number 0 Смещение для пагинации

Формат ответа

interface ChainSubmissionsResponse {
  success: boolean;
  body: {
    chain: {
      id: string;
      name: string;
      tasks: Array<{
        id: string;
        title: string;
      }>;
    };
    participants: Array<{
      userId: string;
      nickname: string;
      completedTasks: number;
      totalTasks: number;
      progressPercent: number;
    }>;
    submissions: Array<{
      id: string;
      user: {
        id: string;
        nickname: string;
      };
      task: {
        id: string;
        title: string;
      };
      status: 'pending' | 'in_progress' | 'accepted' | 'needs_revision';
      attemptNumber: number;
      submittedAt: string; // ISO date
      checkedAt?: string;  // ISO date
      feedback?: string;
    }>;
    pagination: {
      total: number;
      limit: number;
      offset: number;
    };
  };
}

Пример запроса

GET /api/challenge/chain/607f1f77bcf86cd799439021/submissions?status=needs_revision&limit=50

Пример ответа

{
  "success": true,
  "body": {
    "chain": {
      "id": "607f1f77bcf86cd799439021",
      "name": "Основы JavaScript",
      "tasks": [
        { "id": "507f1f77bcf86cd799439011", "title": "Реализовать сортировку массива" },
        { "id": "507f1f77bcf86cd799439015", "title": "Валидация формы" }
      ]
    },
    "participants": [
      {
        "userId": "user_123",
        "nickname": "alex_dev",
        "completedTasks": 1,
        "totalTasks": 2,
        "progressPercent": 50
      },
      {
        "userId": "user_456",
        "nickname": "maria_coder",
        "completedTasks": 2,
        "totalTasks": 2,
        "progressPercent": 100
      }
    ],
    "submissions": [
      {
        "id": "sub_001",
        "user": {
          "id": "user_123",
          "nickname": "alex_dev"
        },
        "task": {
          "id": "507f1f77bcf86cd799439011",
          "title": "Реализовать сортировку массива"
        },
        "status": "needs_revision",
        "attemptNumber": 2,
        "submittedAt": "2024-12-10T14:30:00.000Z",
        "checkedAt": "2024-12-10T14:30:45.000Z",
        "feedback": "Алгоритм работает неверно для отрицательных чисел"
      }
    ],
    "pagination": {
      "total": 15,
      "limit": 50,
      "offset": 0
    }
  }
}

Логика на бэкенде

Алгоритм

  1. Получить цепочку по chainId
  2. Если цепочка не найдена — вернуть 404
  3. Получить список taskIds из цепочки
  4. Найти все submissions где task._id входит в taskIds
  5. Применить фильтры (userId, status) если указаны
  6. Вычислить прогресс по каждому участнику:
    • Найти уникальных пользователей из submissions
    • Для каждого посчитать completedTasks (количество уникальных tasks со статусом accepted)
    • Рассчитать progressPercent = (completedTasks / totalTasks) * 100
  7. Применить пагинацию к submissions
  8. Вернуть результат

Индексы MongoDB (рекомендуется)

// Для быстрой выборки submissions по task
db.submissions.createIndex({ "task": 1, "submittedAt": -1 })

// Составной индекс для фильтрации
db.submissions.createIndex({ "task": 1, "status": 1, "submittedAt": -1 })

Права доступа

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

  • challenge-admin
  • challenge-teacher

Коды ошибок

Код Описание
200 Успешный ответ
400 Некорректные параметры запроса
401 Не авторизован
403 Недостаточно прав
404 Цепочка не найдена
500 Внутренняя ошибка сервера

Изменения на фронтенде после реализации

После добавления эндпоинта в src/__data__/api/api.ts нужно добавить:

// В endpoints builder
getChainSubmissions: builder.query<ChainSubmissionsResponse, { 
  chainId: string; 
  userId?: string; 
  status?: SubmissionStatus;
  limit?: number;
  offset?: number;
}>({
  query: ({ chainId, userId, status, limit, offset }) => ({
    url: `/challenge/chain/${chainId}/submissions`,
    params: { userId, status, limit, offset },
  }),
  transformResponse: (response: { body: ChainSubmissionsResponse }) => response.body,
  providesTags: ['Submission'],
}),

Это позволит упростить SubmissionsPage.tsx:

  • Один запрос вместо нескольких
  • Убрать клиентскую фильтрацию по taskIds
  • Получать готовый прогресс участников

Приоритет

Средний — текущая реализация работает, но создаёт избыточную нагрузку при большом количестве участников.

Оценка трудозатрат

~4-6 часов (включая тесты)