Add detailed statistics API v2 documentation and implement frontend components for displaying statistics
This commit is contained in:
@@ -11,7 +11,8 @@ stubs/api/
|
||||
│ ├── chains.json # Цепочки (3 шт.)
|
||||
│ ├── users.json # Пользователи (8 шт.)
|
||||
│ ├── submissions.json # Попытки (8 шт.)
|
||||
│ └── stats.json # Системная статистика
|
||||
│ ├── stats.json # Системная статистика (v1)
|
||||
│ └── stats-v2.json # Детальная статистика (v2, 20 заданий)
|
||||
├── index.js # API роуты
|
||||
└── README.md # Эта документация
|
||||
```
|
||||
@@ -36,7 +37,8 @@ stubs/api/
|
||||
- `GET /api/challenge/users` - список всех пользователей
|
||||
|
||||
### Statistics (Статистика)
|
||||
- `GET /api/challenge/stats` - общая системная статистика
|
||||
- `GET /api/challenge/stats` - общая системная статистика (v1)
|
||||
- `GET /api/challenge/stats/v2` - детальная статистика с таблицами и прогрессом (v2)
|
||||
- `GET /api/challenge/user/:userId/stats` - статистика пользователя (генерируется динамически)
|
||||
|
||||
### Submissions (Попытки)
|
||||
@@ -150,6 +152,17 @@ GET /api/challenge/user/user001/stats
|
||||
|
||||
Ответ будет содержать динамически вычисленную статистику на основе всех попыток пользователя.
|
||||
|
||||
### Получить детальную статистику (v2)
|
||||
```bash
|
||||
GET /api/challenge/stats/v2
|
||||
```
|
||||
|
||||
Ответ будет содержать:
|
||||
- Базовую статистику (users, tasks, chains, submissions, queue)
|
||||
- Таблицу заданий с детальной статистикой (20 заданий с попытками, успешностью, средними показателями)
|
||||
- 6 активных участников с прогрессом по цепочкам
|
||||
- Детальную матрицу прогресса по каждой из 2 цепочек (Backend разработка - 10 заданий, Frontend разработка - 10 заданий)
|
||||
|
||||
## ⚙️ Настройка задержки
|
||||
|
||||
По умолчанию все запросы имеют задержку 300ms для имитации сетевых запросов. Изменить можно в `index.js`:
|
||||
|
||||
592
stubs/api/data/stats-v2.json
Normal file
592
stubs/api/data/stats-v2.json
Normal file
@@ -0,0 +1,592 @@
|
||||
{
|
||||
"users": 8,
|
||||
"tasks": 20,
|
||||
"chains": 2,
|
||||
"submissions": {
|
||||
"total": 95,
|
||||
"accepted": 38,
|
||||
"rejected": 42,
|
||||
"pending": 8,
|
||||
"inProgress": 7
|
||||
},
|
||||
"averageCheckTimeMs": 2143,
|
||||
"queue": {
|
||||
"queueLength": 15,
|
||||
"waiting": 8,
|
||||
"inProgress": 7,
|
||||
"maxConcurrency": 10,
|
||||
"currentlyProcessing": 7
|
||||
},
|
||||
"tasksTable": [
|
||||
{
|
||||
"taskId": "task_1",
|
||||
"title": "Создание REST API",
|
||||
"totalAttempts": 12,
|
||||
"uniqueUsers": 6,
|
||||
"acceptedCount": 5,
|
||||
"successRate": 83,
|
||||
"averageAttemptsToSuccess": 2.4
|
||||
},
|
||||
{
|
||||
"taskId": "task_2",
|
||||
"title": "Работа с базой данных MongoDB",
|
||||
"totalAttempts": 10,
|
||||
"uniqueUsers": 5,
|
||||
"acceptedCount": 3,
|
||||
"successRate": 60,
|
||||
"averageAttemptsToSuccess": 3.3
|
||||
},
|
||||
{
|
||||
"taskId": "task_3",
|
||||
"title": "JWT аутентификация",
|
||||
"totalAttempts": 8,
|
||||
"uniqueUsers": 4,
|
||||
"acceptedCount": 2,
|
||||
"successRate": 50,
|
||||
"averageAttemptsToSuccess": 4.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_4",
|
||||
"title": "Middleware для Express",
|
||||
"totalAttempts": 6,
|
||||
"uniqueUsers": 4,
|
||||
"acceptedCount": 3,
|
||||
"successRate": 75,
|
||||
"averageAttemptsToSuccess": 2.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_5",
|
||||
"title": "WebSocket сервер",
|
||||
"totalAttempts": 5,
|
||||
"uniqueUsers": 3,
|
||||
"acceptedCount": 1,
|
||||
"successRate": 33,
|
||||
"averageAttemptsToSuccess": 5.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_6",
|
||||
"title": "Кэширование с Redis",
|
||||
"totalAttempts": 7,
|
||||
"uniqueUsers": 4,
|
||||
"acceptedCount": 2,
|
||||
"successRate": 57,
|
||||
"averageAttemptsToSuccess": 3.5
|
||||
},
|
||||
{
|
||||
"taskId": "task_7",
|
||||
"title": "GraphQL Schema",
|
||||
"totalAttempts": 4,
|
||||
"uniqueUsers": 3,
|
||||
"acceptedCount": 2,
|
||||
"successRate": 67,
|
||||
"averageAttemptsToSuccess": 2.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_8",
|
||||
"title": "Docker контейнеризация",
|
||||
"totalAttempts": 3,
|
||||
"uniqueUsers": 2,
|
||||
"acceptedCount": 1,
|
||||
"successRate": 50,
|
||||
"averageAttemptsToSuccess": 3.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_9",
|
||||
"title": "CI/CD Pipeline",
|
||||
"totalAttempts": 2,
|
||||
"uniqueUsers": 2,
|
||||
"acceptedCount": 1,
|
||||
"successRate": 50,
|
||||
"averageAttemptsToSuccess": 2.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_10",
|
||||
"title": "Микросервисная архитектура",
|
||||
"totalAttempts": 2,
|
||||
"uniqueUsers": 1,
|
||||
"acceptedCount": 0,
|
||||
"successRate": 0,
|
||||
"averageAttemptsToSuccess": 0
|
||||
},
|
||||
{
|
||||
"taskId": "task_11",
|
||||
"title": "React компоненты",
|
||||
"totalAttempts": 11,
|
||||
"uniqueUsers": 6,
|
||||
"acceptedCount": 6,
|
||||
"successRate": 100,
|
||||
"averageAttemptsToSuccess": 1.8
|
||||
},
|
||||
{
|
||||
"taskId": "task_12",
|
||||
"title": "React Hooks",
|
||||
"totalAttempts": 9,
|
||||
"uniqueUsers": 5,
|
||||
"acceptedCount": 4,
|
||||
"successRate": 80,
|
||||
"averageAttemptsToSuccess": 2.3
|
||||
},
|
||||
{
|
||||
"taskId": "task_13",
|
||||
"title": "Redux State Management",
|
||||
"totalAttempts": 7,
|
||||
"uniqueUsers": 4,
|
||||
"acceptedCount": 2,
|
||||
"successRate": 57,
|
||||
"averageAttemptsToSuccess": 3.5
|
||||
},
|
||||
{
|
||||
"taskId": "task_14",
|
||||
"title": "React Router",
|
||||
"totalAttempts": 6,
|
||||
"uniqueUsers": 4,
|
||||
"acceptedCount": 3,
|
||||
"successRate": 75,
|
||||
"averageAttemptsToSuccess": 2.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_15",
|
||||
"title": "Form валидация",
|
||||
"totalAttempts": 5,
|
||||
"uniqueUsers": 3,
|
||||
"acceptedCount": 2,
|
||||
"successRate": 67,
|
||||
"averageAttemptsToSuccess": 2.5
|
||||
},
|
||||
{
|
||||
"taskId": "task_16",
|
||||
"title": "API интеграция",
|
||||
"totalAttempts": 8,
|
||||
"uniqueUsers": 5,
|
||||
"acceptedCount": 3,
|
||||
"successRate": 60,
|
||||
"averageAttemptsToSuccess": 2.7
|
||||
},
|
||||
{
|
||||
"taskId": "task_17",
|
||||
"title": "CSS-in-JS стилизация",
|
||||
"totalAttempts": 4,
|
||||
"uniqueUsers": 3,
|
||||
"acceptedCount": 2,
|
||||
"successRate": 67,
|
||||
"averageAttemptsToSuccess": 2.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_18",
|
||||
"title": "Оптимизация производительности",
|
||||
"totalAttempts": 3,
|
||||
"uniqueUsers": 2,
|
||||
"acceptedCount": 1,
|
||||
"successRate": 50,
|
||||
"averageAttemptsToSuccess": 3.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_19",
|
||||
"title": "Unit тесты с Jest",
|
||||
"totalAttempts": 2,
|
||||
"uniqueUsers": 2,
|
||||
"acceptedCount": 1,
|
||||
"successRate": 50,
|
||||
"averageAttemptsToSuccess": 2.0
|
||||
},
|
||||
{
|
||||
"taskId": "task_20",
|
||||
"title": "E2E тесты с Playwright",
|
||||
"totalAttempts": 1,
|
||||
"uniqueUsers": 1,
|
||||
"acceptedCount": 0,
|
||||
"successRate": 0,
|
||||
"averageAttemptsToSuccess": 0
|
||||
}
|
||||
],
|
||||
"activeParticipants": [
|
||||
{
|
||||
"userId": "user_1",
|
||||
"nickname": "alex_dev",
|
||||
"totalSubmissions": 18,
|
||||
"completedTasks": 12,
|
||||
"chainProgress": [
|
||||
{
|
||||
"chainId": "chain_1",
|
||||
"chainName": "Backend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 6,
|
||||
"progressPercent": 60
|
||||
},
|
||||
{
|
||||
"chainId": "chain_2",
|
||||
"chainName": "Frontend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 6,
|
||||
"progressPercent": 60
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"userId": "user_2",
|
||||
"nickname": "maria_coder",
|
||||
"totalSubmissions": 15,
|
||||
"completedTasks": 9,
|
||||
"chainProgress": [
|
||||
{
|
||||
"chainId": "chain_1",
|
||||
"chainName": "Backend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 5,
|
||||
"progressPercent": 50
|
||||
},
|
||||
{
|
||||
"chainId": "chain_2",
|
||||
"chainName": "Frontend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 4,
|
||||
"progressPercent": 40
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"userId": "user_3",
|
||||
"nickname": "ivan_programmer",
|
||||
"totalSubmissions": 10,
|
||||
"completedTasks": 5,
|
||||
"chainProgress": [
|
||||
{
|
||||
"chainId": "chain_1",
|
||||
"chainName": "Backend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 3,
|
||||
"progressPercent": 30
|
||||
},
|
||||
{
|
||||
"chainId": "chain_2",
|
||||
"chainName": "Frontend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 2,
|
||||
"progressPercent": 20
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"userId": "user_4",
|
||||
"nickname": "kate_fullstack",
|
||||
"totalSubmissions": 22,
|
||||
"completedTasks": 15,
|
||||
"chainProgress": [
|
||||
{
|
||||
"chainId": "chain_1",
|
||||
"chainName": "Backend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 8,
|
||||
"progressPercent": 80
|
||||
},
|
||||
{
|
||||
"chainId": "chain_2",
|
||||
"chainName": "Frontend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 7,
|
||||
"progressPercent": 70
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"userId": "user_5",
|
||||
"nickname": "dmitry_backend",
|
||||
"totalSubmissions": 12,
|
||||
"completedTasks": 6,
|
||||
"chainProgress": [
|
||||
{
|
||||
"chainId": "chain_1",
|
||||
"chainName": "Backend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 5,
|
||||
"progressPercent": 50
|
||||
},
|
||||
{
|
||||
"chainId": "chain_2",
|
||||
"chainName": "Frontend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 1,
|
||||
"progressPercent": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"userId": "user_6",
|
||||
"nickname": "anna_react",
|
||||
"totalSubmissions": 14,
|
||||
"completedTasks": 7,
|
||||
"chainProgress": [
|
||||
{
|
||||
"chainId": "chain_1",
|
||||
"chainName": "Backend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 1,
|
||||
"progressPercent": 10
|
||||
},
|
||||
{
|
||||
"chainId": "chain_2",
|
||||
"chainName": "Frontend разработка",
|
||||
"totalTasks": 10,
|
||||
"completedTasks": 6,
|
||||
"progressPercent": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"chainsDetailed": [
|
||||
{
|
||||
"chainId": "chain_1",
|
||||
"name": "Backend разработка",
|
||||
"totalTasks": 10,
|
||||
"tasks": [
|
||||
{ "taskId": "task_1", "title": "Создание REST API", "description": "Создайте REST API с Express.js" },
|
||||
{ "taskId": "task_2", "title": "Работа с базой данных MongoDB", "description": "Интегрируйте MongoDB" },
|
||||
{ "taskId": "task_3", "title": "JWT аутентификация", "description": "Реализуйте JWT-аутентификацию" },
|
||||
{ "taskId": "task_4", "title": "Middleware для Express", "description": "Создайте кастомные middleware" },
|
||||
{ "taskId": "task_5", "title": "WebSocket сервер", "description": "Реализуйте WebSocket для real-time" },
|
||||
{ "taskId": "task_6", "title": "Кэширование с Redis", "description": "Внедрите Redis для кэширования" },
|
||||
{ "taskId": "task_7", "title": "GraphQL Schema", "description": "Создайте GraphQL API" },
|
||||
{ "taskId": "task_8", "title": "Docker контейнеризация", "description": "Контейнеризируйте приложение" },
|
||||
{ "taskId": "task_9", "title": "CI/CD Pipeline", "description": "Настройте CI/CD" },
|
||||
{ "taskId": "task_10", "title": "Микросервисная архитектура", "description": "Разбейте на микросервисы" }
|
||||
],
|
||||
"participantProgress": [
|
||||
{
|
||||
"userId": "user_1",
|
||||
"nickname": "alex_dev",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_1", "taskTitle": "Создание REST API", "status": "completed" },
|
||||
{ "taskId": "task_2", "taskTitle": "Работа с базой данных MongoDB", "status": "completed" },
|
||||
{ "taskId": "task_3", "taskTitle": "JWT аутентификация", "status": "completed" },
|
||||
{ "taskId": "task_4", "taskTitle": "Middleware для Express", "status": "completed" },
|
||||
{ "taskId": "task_5", "taskTitle": "WebSocket сервер", "status": "completed" },
|
||||
{ "taskId": "task_6", "taskTitle": "Кэширование с Redis", "status": "completed" },
|
||||
{ "taskId": "task_7", "taskTitle": "GraphQL Schema", "status": "in_progress" },
|
||||
{ "taskId": "task_8", "taskTitle": "Docker контейнеризация", "status": "not_started" },
|
||||
{ "taskId": "task_9", "taskTitle": "CI/CD Pipeline", "status": "not_started" },
|
||||
{ "taskId": "task_10", "taskTitle": "Микросервисная архитектура", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 6,
|
||||
"progressPercent": 60
|
||||
},
|
||||
{
|
||||
"userId": "user_2",
|
||||
"nickname": "maria_coder",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_1", "taskTitle": "Создание REST API", "status": "completed" },
|
||||
{ "taskId": "task_2", "taskTitle": "Работа с базой данных MongoDB", "status": "completed" },
|
||||
{ "taskId": "task_3", "taskTitle": "JWT аутентификация", "status": "completed" },
|
||||
{ "taskId": "task_4", "taskTitle": "Middleware для Express", "status": "completed" },
|
||||
{ "taskId": "task_5", "taskTitle": "WebSocket сервер", "status": "completed" },
|
||||
{ "taskId": "task_6", "taskTitle": "Кэширование с Redis", "status": "needs_revision" },
|
||||
{ "taskId": "task_7", "taskTitle": "GraphQL Schema", "status": "pending" },
|
||||
{ "taskId": "task_8", "taskTitle": "Docker контейнеризация", "status": "not_started" },
|
||||
{ "taskId": "task_9", "taskTitle": "CI/CD Pipeline", "status": "not_started" },
|
||||
{ "taskId": "task_10", "taskTitle": "Микросервисная архитектура", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 5,
|
||||
"progressPercent": 50
|
||||
},
|
||||
{
|
||||
"userId": "user_3",
|
||||
"nickname": "ivan_programmer",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_1", "taskTitle": "Создание REST API", "status": "completed" },
|
||||
{ "taskId": "task_2", "taskTitle": "Работа с базой данных MongoDB", "status": "completed" },
|
||||
{ "taskId": "task_3", "taskTitle": "JWT аутентификация", "status": "completed" },
|
||||
{ "taskId": "task_4", "taskTitle": "Middleware для Express", "status": "needs_revision" },
|
||||
{ "taskId": "task_5", "taskTitle": "WebSocket сервер", "status": "not_started" },
|
||||
{ "taskId": "task_6", "taskTitle": "Кэширование с Redis", "status": "not_started" },
|
||||
{ "taskId": "task_7", "taskTitle": "GraphQL Schema", "status": "not_started" },
|
||||
{ "taskId": "task_8", "taskTitle": "Docker контейнеризация", "status": "not_started" },
|
||||
{ "taskId": "task_9", "taskTitle": "CI/CD Pipeline", "status": "not_started" },
|
||||
{ "taskId": "task_10", "taskTitle": "Микросервисная архитектура", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 3,
|
||||
"progressPercent": 30
|
||||
},
|
||||
{
|
||||
"userId": "user_4",
|
||||
"nickname": "kate_fullstack",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_1", "taskTitle": "Создание REST API", "status": "completed" },
|
||||
{ "taskId": "task_2", "taskTitle": "Работа с базой данных MongoDB", "status": "completed" },
|
||||
{ "taskId": "task_3", "taskTitle": "JWT аутентификация", "status": "completed" },
|
||||
{ "taskId": "task_4", "taskTitle": "Middleware для Express", "status": "completed" },
|
||||
{ "taskId": "task_5", "taskTitle": "WebSocket сервер", "status": "completed" },
|
||||
{ "taskId": "task_6", "taskTitle": "Кэширование с Redis", "status": "completed" },
|
||||
{ "taskId": "task_7", "taskTitle": "GraphQL Schema", "status": "completed" },
|
||||
{ "taskId": "task_8", "taskTitle": "Docker контейнеризация", "status": "completed" },
|
||||
{ "taskId": "task_9", "taskTitle": "CI/CD Pipeline", "status": "in_progress" },
|
||||
{ "taskId": "task_10", "taskTitle": "Микросервисная архитектура", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 8,
|
||||
"progressPercent": 80
|
||||
},
|
||||
{
|
||||
"userId": "user_5",
|
||||
"nickname": "dmitry_backend",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_1", "taskTitle": "Создание REST API", "status": "completed" },
|
||||
{ "taskId": "task_2", "taskTitle": "Работа с базой данных MongoDB", "status": "completed" },
|
||||
{ "taskId": "task_3", "taskTitle": "JWT аутентификация", "status": "completed" },
|
||||
{ "taskId": "task_4", "taskTitle": "Middleware для Express", "status": "completed" },
|
||||
{ "taskId": "task_5", "taskTitle": "WebSocket сервер", "status": "completed" },
|
||||
{ "taskId": "task_6", "taskTitle": "Кэширование с Redis", "status": "in_progress" },
|
||||
{ "taskId": "task_7", "taskTitle": "GraphQL Schema", "status": "not_started" },
|
||||
{ "taskId": "task_8", "taskTitle": "Docker контейнеризация", "status": "not_started" },
|
||||
{ "taskId": "task_9", "taskTitle": "CI/CD Pipeline", "status": "not_started" },
|
||||
{ "taskId": "task_10", "taskTitle": "Микросервисная архитектура", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 5,
|
||||
"progressPercent": 50
|
||||
},
|
||||
{
|
||||
"userId": "user_6",
|
||||
"nickname": "anna_react",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_1", "taskTitle": "Создание REST API", "status": "completed" },
|
||||
{ "taskId": "task_2", "taskTitle": "Работа с базой данных MongoDB", "status": "pending" },
|
||||
{ "taskId": "task_3", "taskTitle": "JWT аутентификация", "status": "not_started" },
|
||||
{ "taskId": "task_4", "taskTitle": "Middleware для Express", "status": "not_started" },
|
||||
{ "taskId": "task_5", "taskTitle": "WebSocket сервер", "status": "not_started" },
|
||||
{ "taskId": "task_6", "taskTitle": "Кэширование с Redis", "status": "not_started" },
|
||||
{ "taskId": "task_7", "taskTitle": "GraphQL Schema", "status": "not_started" },
|
||||
{ "taskId": "task_8", "taskTitle": "Docker контейнеризация", "status": "not_started" },
|
||||
{ "taskId": "task_9", "taskTitle": "CI/CD Pipeline", "status": "not_started" },
|
||||
{ "taskId": "task_10", "taskTitle": "Микросервисная архитектура", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 1,
|
||||
"progressPercent": 10
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"chainId": "chain_2",
|
||||
"name": "Frontend разработка",
|
||||
"totalTasks": 10,
|
||||
"tasks": [
|
||||
{ "taskId": "task_11", "title": "React компоненты", "description": "Создайте переиспользуемые компоненты" },
|
||||
{ "taskId": "task_12", "title": "React Hooks", "description": "Используйте хуки" },
|
||||
{ "taskId": "task_13", "title": "Redux State Management", "description": "Управление состоянием" },
|
||||
{ "taskId": "task_14", "title": "React Router", "description": "Маршрутизация" },
|
||||
{ "taskId": "task_15", "title": "Form валидация", "description": "Валидация форм" },
|
||||
{ "taskId": "task_16", "title": "API интеграция", "description": "Интеграция с API" },
|
||||
{ "taskId": "task_17", "title": "CSS-in-JS стилизация", "description": "Стилизация" },
|
||||
{ "taskId": "task_18", "title": "Оптимизация производительности", "description": "Оптимизация React" },
|
||||
{ "taskId": "task_19", "title": "Unit тесты с Jest", "description": "Юнит тестирование" },
|
||||
{ "taskId": "task_20", "title": "E2E тесты с Playwright", "description": "End-to-end тестирование" }
|
||||
],
|
||||
"participantProgress": [
|
||||
{
|
||||
"userId": "user_1",
|
||||
"nickname": "alex_dev",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_11", "taskTitle": "React компоненты", "status": "completed" },
|
||||
{ "taskId": "task_12", "taskTitle": "React Hooks", "status": "completed" },
|
||||
{ "taskId": "task_13", "taskTitle": "Redux State Management", "status": "completed" },
|
||||
{ "taskId": "task_14", "taskTitle": "React Router", "status": "completed" },
|
||||
{ "taskId": "task_15", "taskTitle": "Form валидация", "status": "completed" },
|
||||
{ "taskId": "task_16", "taskTitle": "API интеграция", "status": "completed" },
|
||||
{ "taskId": "task_17", "taskTitle": "CSS-in-JS стилизация", "status": "in_progress" },
|
||||
{ "taskId": "task_18", "taskTitle": "Оптимизация производительности", "status": "not_started" },
|
||||
{ "taskId": "task_19", "taskTitle": "Unit тесты с Jest", "status": "not_started" },
|
||||
{ "taskId": "task_20", "taskTitle": "E2E тесты с Playwright", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 6,
|
||||
"progressPercent": 60
|
||||
},
|
||||
{
|
||||
"userId": "user_2",
|
||||
"nickname": "maria_coder",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_11", "taskTitle": "React компоненты", "status": "completed" },
|
||||
{ "taskId": "task_12", "taskTitle": "React Hooks", "status": "completed" },
|
||||
{ "taskId": "task_13", "taskTitle": "Redux State Management", "status": "completed" },
|
||||
{ "taskId": "task_14", "taskTitle": "React Router", "status": "completed" },
|
||||
{ "taskId": "task_15", "taskTitle": "Form валидация", "status": "needs_revision" },
|
||||
{ "taskId": "task_16", "taskTitle": "API интеграция", "status": "pending" },
|
||||
{ "taskId": "task_17", "taskTitle": "CSS-in-JS стилизация", "status": "not_started" },
|
||||
{ "taskId": "task_18", "taskTitle": "Оптимизация производительности", "status": "not_started" },
|
||||
{ "taskId": "task_19", "taskTitle": "Unit тесты с Jest", "status": "not_started" },
|
||||
{ "taskId": "task_20", "taskTitle": "E2E тесты с Playwright", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 4,
|
||||
"progressPercent": 40
|
||||
},
|
||||
{
|
||||
"userId": "user_3",
|
||||
"nickname": "ivan_programmer",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_11", "taskTitle": "React компоненты", "status": "completed" },
|
||||
{ "taskId": "task_12", "taskTitle": "React Hooks", "status": "completed" },
|
||||
{ "taskId": "task_13", "taskTitle": "Redux State Management", "status": "in_progress" },
|
||||
{ "taskId": "task_14", "taskTitle": "React Router", "status": "not_started" },
|
||||
{ "taskId": "task_15", "taskTitle": "Form валидация", "status": "not_started" },
|
||||
{ "taskId": "task_16", "taskTitle": "API интеграция", "status": "not_started" },
|
||||
{ "taskId": "task_17", "taskTitle": "CSS-in-JS стилизация", "status": "not_started" },
|
||||
{ "taskId": "task_18", "taskTitle": "Оптимизация производительности", "status": "not_started" },
|
||||
{ "taskId": "task_19", "taskTitle": "Unit тесты с Jest", "status": "not_started" },
|
||||
{ "taskId": "task_20", "taskTitle": "E2E тесты с Playwright", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 2,
|
||||
"progressPercent": 20
|
||||
},
|
||||
{
|
||||
"userId": "user_4",
|
||||
"nickname": "kate_fullstack",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_11", "taskTitle": "React компоненты", "status": "completed" },
|
||||
{ "taskId": "task_12", "taskTitle": "React Hooks", "status": "completed" },
|
||||
{ "taskId": "task_13", "taskTitle": "Redux State Management", "status": "completed" },
|
||||
{ "taskId": "task_14", "taskTitle": "React Router", "status": "completed" },
|
||||
{ "taskId": "task_15", "taskTitle": "Form валидация", "status": "completed" },
|
||||
{ "taskId": "task_16", "taskTitle": "API интеграция", "status": "completed" },
|
||||
{ "taskId": "task_17", "taskTitle": "CSS-in-JS стилизация", "status": "completed" },
|
||||
{ "taskId": "task_18", "taskTitle": "Оптимизация производительности", "status": "in_progress" },
|
||||
{ "taskId": "task_19", "taskTitle": "Unit тесты с Jest", "status": "not_started" },
|
||||
{ "taskId": "task_20", "taskTitle": "E2E тесты с Playwright", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 7,
|
||||
"progressPercent": 70
|
||||
},
|
||||
{
|
||||
"userId": "user_5",
|
||||
"nickname": "dmitry_backend",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_11", "taskTitle": "React компоненты", "status": "completed" },
|
||||
{ "taskId": "task_12", "taskTitle": "React Hooks", "status": "pending" },
|
||||
{ "taskId": "task_13", "taskTitle": "Redux State Management", "status": "not_started" },
|
||||
{ "taskId": "task_14", "taskTitle": "React Router", "status": "not_started" },
|
||||
{ "taskId": "task_15", "taskTitle": "Form валидация", "status": "not_started" },
|
||||
{ "taskId": "task_16", "taskTitle": "API интеграция", "status": "not_started" },
|
||||
{ "taskId": "task_17", "taskTitle": "CSS-in-JS стилизация", "status": "not_started" },
|
||||
{ "taskId": "task_18", "taskTitle": "Оптимизация производительности", "status": "not_started" },
|
||||
{ "taskId": "task_19", "taskTitle": "Unit тесты с Jest", "status": "not_started" },
|
||||
{ "taskId": "task_20", "taskTitle": "E2E тесты с Playwright", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 1,
|
||||
"progressPercent": 10
|
||||
},
|
||||
{
|
||||
"userId": "user_6",
|
||||
"nickname": "anna_react",
|
||||
"taskProgress": [
|
||||
{ "taskId": "task_11", "taskTitle": "React компоненты", "status": "completed" },
|
||||
{ "taskId": "task_12", "taskTitle": "React Hooks", "status": "completed" },
|
||||
{ "taskId": "task_13", "taskTitle": "Redux State Management", "status": "completed" },
|
||||
{ "taskId": "task_14", "taskTitle": "React Router", "status": "completed" },
|
||||
{ "taskId": "task_15", "taskTitle": "Form валидация", "status": "completed" },
|
||||
{ "taskId": "task_16", "taskTitle": "API интеграция", "status": "completed" },
|
||||
{ "taskId": "task_17", "taskTitle": "CSS-in-JS стилизация", "status": "in_progress" },
|
||||
{ "taskId": "task_18", "taskTitle": "Оптимизация производительности", "status": "not_started" },
|
||||
{ "taskId": "task_19", "taskTitle": "Unit тесты с Jest", "status": "not_started" },
|
||||
{ "taskId": "task_20", "taskTitle": "E2E тесты с Playwright", "status": "not_started" }
|
||||
],
|
||||
"completedCount": 6,
|
||||
"progressPercent": 60
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,21 +1,21 @@
|
||||
{
|
||||
"users": 8,
|
||||
"tasks": 5,
|
||||
"chains": 3,
|
||||
"tasks": 20,
|
||||
"chains": 2,
|
||||
"submissions": {
|
||||
"total": 8,
|
||||
"accepted": 5,
|
||||
"rejected": 3,
|
||||
"pending": 0,
|
||||
"inProgress": 0
|
||||
"total": 95,
|
||||
"accepted": 38,
|
||||
"rejected": 42,
|
||||
"pending": 8,
|
||||
"inProgress": 7
|
||||
},
|
||||
"averageCheckTimeMs": 3275,
|
||||
"averageCheckTimeMs": 2143,
|
||||
"queue": {
|
||||
"queueLength": 0,
|
||||
"waiting": 0,
|
||||
"inProgress": 0,
|
||||
"maxConcurrency": 5,
|
||||
"currentlyProcessing": 0
|
||||
"queueLength": 15,
|
||||
"waiting": 8,
|
||||
"inProgress": 7,
|
||||
"maxConcurrency": 10,
|
||||
"currentlyProcessing": 7
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ let chainsCache = null;
|
||||
let usersCache = null;
|
||||
let submissionsCache = null;
|
||||
let statsCache = null;
|
||||
let statsV2Cache = null;
|
||||
|
||||
const getTasks = () => {
|
||||
if (!tasksCache) tasksCache = loadJSON('tasks.json');
|
||||
@@ -55,6 +56,11 @@ const getStats = () => {
|
||||
return statsCache;
|
||||
};
|
||||
|
||||
const getStatsV2 = () => {
|
||||
if (!statsV2Cache) statsV2Cache = loadJSON('stats-v2.json');
|
||||
return statsV2Cache;
|
||||
};
|
||||
|
||||
router.use(timer());
|
||||
|
||||
// ============= TASKS =============
|
||||
@@ -282,6 +288,46 @@ router.get('/challenge/stats', (req, res) => {
|
||||
respond(res, stats);
|
||||
});
|
||||
|
||||
// GET /api/challenge/stats/v2
|
||||
router.get('/challenge/stats/v2', (req, res) => {
|
||||
const statsV2 = getStatsV2();
|
||||
const chainId = req.query.chainId;
|
||||
|
||||
// Если chainId не передан, возвращаем все данные
|
||||
if (!chainId) {
|
||||
respond(res, statsV2);
|
||||
return;
|
||||
}
|
||||
|
||||
// Фильтруем данные по выбранной цепочке
|
||||
const filteredChain = statsV2.chainsDetailed.find(c => c.chainId === chainId);
|
||||
|
||||
if (!filteredChain) {
|
||||
return respondError(res, 'Chain not found', 404);
|
||||
}
|
||||
|
||||
// Фильтруем tasksTable - только задания из этой цепочки
|
||||
const chainTaskIds = new Set(filteredChain.tasks.map(t => t.taskId));
|
||||
const filteredTasksTable = statsV2.tasksTable.filter(t => chainTaskIds.has(t.taskId));
|
||||
|
||||
// Фильтруем activeParticipants - только участники с попытками в этой цепочке
|
||||
const participantIds = new Set(filteredChain.participantProgress.map(p => p.userId));
|
||||
const filteredParticipants = statsV2.activeParticipants
|
||||
.filter(p => participantIds.has(p.userId))
|
||||
.map(p => ({
|
||||
...p,
|
||||
chainProgress: p.chainProgress.filter(cp => cp.chainId === chainId)
|
||||
}));
|
||||
|
||||
// Возвращаем отфильтрованные данные
|
||||
respond(res, {
|
||||
...statsV2,
|
||||
tasksTable: filteredTasksTable,
|
||||
activeParticipants: filteredParticipants,
|
||||
chainsDetailed: [filteredChain]
|
||||
});
|
||||
});
|
||||
|
||||
// GET /api/challenge/user/:userId/stats
|
||||
router.get('/challenge/user/:userId/stats', (req, res) => {
|
||||
const users = getUsers();
|
||||
|
||||
Reference in New Issue
Block a user