challenge-admin-pl/docs/CHALLENGE_ANALYTICS_SUMMARY.md
Primakov Alexandr Alexandrovich e777b57991 init + api use
2025-11-03 17:59:08 +03:00

636 lines
18 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Challenge Service - Аналитика и метрики для фронтенда
Краткое руководство по ключевым метрикам и аналитике для интеграции на фронтенде.
## 📊 Ключевые метрики для отслеживания
### 1. Метрики производительности
```typescript
// Метрики для мониторинга
interface PerformanceMetrics {
// Время от отправки до получения результата
timeToFeedback: number // миллисекунды
// Время ожидания в очереди
queueWaitTime: number // миллисекунды
// Время непосредственной проверки
checkTime: number // миллисекунды
// Позиция в очереди при добавлении
initialQueuePosition: number
// Количество проверок статуса до завершения
pollsBeforeComplete: number
}
// Пример сбора метрик
class MetricsCollector {
private startTime: number = 0
private pollCount: number = 0
startTracking() {
this.startTime = Date.now()
this.pollCount = 0
}
incrementPoll() {
this.pollCount++
}
getMetrics(submission: ChallengeSubmission): PerformanceMetrics {
return {
timeToFeedback: Date.now() - this.startTime,
queueWaitTime: submission.checkedAt
? new Date(submission.checkedAt).getTime() - new Date(submission.submittedAt).getTime()
: 0,
checkTime: submission.checkedAt
? new Date(submission.checkedAt).getTime() - new Date(submission.submittedAt).getTime()
: 0,
initialQueuePosition: 0, // Сохранить из первого ответа
pollsBeforeComplete: this.pollCount
}
}
}
```
### 2. Метрики пользовательского поведения
```typescript
interface UserBehaviorMetrics {
// Время, проведенное на задании
timeSpentOnTask: number // секунды
// Количество символов в решении
solutionLength: number
// Количество редактирований текста
editCount: number
// Использовал ли черновик
usedDraft: boolean
// Время от загрузки до отправки
timeToSubmit: number // секунды
}
// Трекинг поведения
class BehaviorTracker {
private taskStartTime: number = Date.now()
private editCount: number = 0
private lastValue: string = ''
onTextChange(newValue: string) {
if (newValue !== this.lastValue) {
this.editCount++
this.lastValue = newValue
}
}
getMetrics(result: string, usedDraft: boolean): UserBehaviorMetrics {
return {
timeSpentOnTask: Math.floor((Date.now() - this.taskStartTime) / 1000),
solutionLength: result.length,
editCount: this.editCount,
usedDraft,
timeToSubmit: Math.floor((Date.now() - this.taskStartTime) / 1000)
}
}
}
```
### 3. Метрики успешности
```typescript
interface SuccessMetrics {
// Процент принятых заданий с первой попытки
firstAttemptSuccessRate: number // 0-100
// Среднее количество попыток до успеха
averageAttemptsToSuccess: number
// Процент завершенных цепочек
chainCompletionRate: number // 0-100
// Время до первого успешного задания
timeToFirstSuccess: number // минуты
}
// Расчет метрик успешности
function calculateSuccessMetrics(stats: UserStats): SuccessMetrics {
const taskStats = stats.taskStats
const firstAttemptSuccess = taskStats.filter(
t => t.status === 'completed' && t.totalAttempts === 1
).length
const completedTasks = taskStats.filter(t => t.status === 'completed')
const totalAttempts = completedTasks.reduce((sum, t) => sum + t.totalAttempts, 0)
return {
firstAttemptSuccessRate: (firstAttemptSuccess / taskStats.length) * 100,
averageAttemptsToSuccess: completedTasks.length > 0
? totalAttempts / completedTasks.length
: 0,
chainCompletionRate: (stats.chainStats.filter(c => c.progress === 100).length / stats.chainStats.length) * 100,
timeToFirstSuccess: 0 // Требует дополнительных данных
}
}
```
## 📈 Дашборды для фронтенда
### 1. Personal Dashboard (для студента)
```typescript
interface PersonalDashboard {
// Общий прогресс
overview: {
tasksCompleted: number
totalTasks: number
completionPercentage: number
currentStreak: number // дней подряд
}
// Текущие цепочки
activeChains: Array<{
chainId: string
name: string
progress: number
nextTask: ChallengeTask | null
estimatedTimeToComplete: number // минуты
}>
// Последние достижения
recentAchievements: Array<{
type: 'task_completed' | 'chain_completed' | 'first_try_success'
taskTitle: string
timestamp: string
}>
// Статистика по попыткам
attemptsStats: {
totalAttempts: number
successfulAttempts: number
successRate: number
}
// Рекомендации
recommendations: Array<{
type: 'retry' | 'continue' | 'new_chain'
message: string
actionLink: string
}>
}
// Генерация dashboard
async function generatePersonalDashboard(userId: string): Promise<PersonalDashboard> {
const stats = await challengeAPI.getUserStats(userId)
const chains = await challengeAPI.getChains()
return {
overview: {
tasksCompleted: stats.completedTasks,
totalTasks: stats.totalTasksAttempted,
completionPercentage: (stats.completedTasks / stats.totalTasksAttempted) * 100,
currentStreak: 0 // Требует дополнительной логики
},
activeChains: stats.chainStats
.filter(c => c.progress > 0 && c.progress < 100)
.map(c => {
const chain = chains.find(ch => ch.id === c.chainId)
const completedCount = c.completedTasks
const nextTask = chain?.tasks[completedCount] || null
return {
chainId: c.chainId,
name: c.chainName,
progress: c.progress,
nextTask,
estimatedTimeToComplete: (c.totalTasks - c.completedTasks) * 10 // 10 мин на задание
}
}),
recentAchievements: [], // Требует истории
attemptsStats: {
totalAttempts: stats.totalSubmissions,
successfulAttempts: stats.completedTasks,
successRate: (stats.completedTasks / stats.totalSubmissions) * 100
},
recommendations: generateRecommendations(stats)
}
}
function generateRecommendations(stats: UserStats): Array<{type: string, message: string, actionLink: string}> {
const recommendations = []
// Если есть задания требующие доработки
if (stats.needsRevisionTasks > 0) {
recommendations.push({
type: 'retry',
message: `У вас ${stats.needsRevisionTasks} заданий требуют доработки`,
actionLink: '/tasks?status=needs_revision'
})
}
// Если есть начатые цепочки
const inProgressChains = stats.chainStats.filter(c => c.progress > 0 && c.progress < 100)
if (inProgressChains.length > 0) {
recommendations.push({
type: 'continue',
message: `Продолжите цепочку "${inProgressChains[0].chainName}"`,
actionLink: `/chain/${inProgressChains[0].chainId}`
})
}
return recommendations
}
```
### 2. Admin Dashboard (для преподавателя)
```typescript
interface AdminDashboard {
// Системные метрики
system: {
totalUsers: number
activeUsers24h: number
totalTasks: number
totalChains: number
queueStatus: {
length: number
processing: number
avgWaitTime: number
}
}
// Метрики заданий
taskMetrics: Array<{
taskId: string
title: string
attemptsCount: number
successRate: number
avgAttempts: number
avgTimeToComplete: number
difficulty: 'easy' | 'medium' | 'hard' // на основе метрик
}>
// Активность пользователей
userActivity: {
registrationsToday: number
submissionsToday: number
peakHours: Array<{ hour: number, count: number }>
}
// Проблемные области
issues: Array<{
type: 'low_success_rate' | 'high_attempts' | 'long_queue'
severity: 'low' | 'medium' | 'high'
message: string
affectedEntity: string
}>
}
// Анализ сложности задания
function analyzeDifficulty(
successRate: number,
avgAttempts: number
): 'easy' | 'medium' | 'hard' {
if (successRate > 70 && avgAttempts < 2) return 'easy'
if (successRate > 40 && avgAttempts < 3) return 'medium'
return 'hard'
}
// Определение проблем
function detectIssues(stats: SystemStats): Array<any> {
const issues = []
// Длинная очередь
if (stats.queue.queueLength > 50) {
issues.push({
type: 'long_queue',
severity: 'high',
message: `Очередь содержит ${stats.queue.queueLength} заданий`,
affectedEntity: 'system'
})
}
// Низкий success rate системы
const systemSuccessRate = (stats.submissions.accepted / stats.submissions.total) * 100
if (systemSuccessRate < 30) {
issues.push({
type: 'low_success_rate',
severity: 'medium',
message: `Общий процент принятых заданий всего ${systemSuccessRate.toFixed(1)}%`,
affectedEntity: 'system'
})
}
return issues
}
```
## 🎯 Визуализация метрик
### 1. Progress Chart (круговая диаграмма)
```typescript
interface ProgressChartData {
completed: number
inProgress: number
needsRevision: number
notStarted: number
}
// Компонент для отображения (концепт)
function ProgressChart({ data }: { data: ProgressChartData }) {
const total = Object.values(data).reduce((a, b) => a + b, 0)
return (
<div className="progress-chart">
<svg viewBox="0 0 100 100">
{/* Реализация круговой диаграммы */}
</svg>
<div className="legend">
<div> Завершено: {data.completed}</div>
<div>🔄 В процессе: {data.inProgress}</div>
<div> Доработка: {data.needsRevision}</div>
<div> Не начато: {data.notStarted}</div>
</div>
</div>
)
}
```
### 2. Timeline Chart (время проверки)
```typescript
interface TimelineData {
submissions: Array<{
timestamp: string
checkTime: number
status: 'accepted' | 'needs_revision'
}>
}
// График времени проверки по времени суток
function TimelineChart({ data }: { data: TimelineData }) {
const hourlyData = new Array(24).fill(0).map((_, hour) => {
const submissions = data.submissions.filter(s =>
new Date(s.timestamp).getHours() === hour
)
return {
hour,
count: submissions.length,
avgCheckTime: submissions.length > 0
? submissions.reduce((sum, s) => sum + s.checkTime, 0) / submissions.length
: 0
}
})
return (
<div className="timeline-chart">
{/* Реализация bar chart */}
</div>
)
}
```
### 3. Heatmap (активность по дням)
```typescript
interface HeatmapData {
dates: Array<{
date: string // YYYY-MM-DD
submissions: number
successRate: number
}>
}
// Визуализация активности пользователя
function ActivityHeatmap({ data }: { data: HeatmapData }) {
return (
<div className="activity-heatmap">
{data.dates.map(day => (
<div
key={day.date}
className="heatmap-cell"
style={{
opacity: day.submissions / 10, // Интенсивность цвета
backgroundColor: day.successRate > 50 ? 'green' : 'red'
}}
title={`${day.date}: ${day.submissions} попыток, ${day.successRate}% успех`}
/>
))}
</div>
)
}
```
## 🔔 Real-time уведомления
### События для отслеживания
```typescript
enum ChallengeEventType {
SUBMISSION_QUEUED = 'submission_queued',
SUBMISSION_CHECKING = 'submission_checking',
SUBMISSION_COMPLETED = 'submission_completed',
TASK_COMPLETED = 'task_completed',
CHAIN_COMPLETED = 'chain_completed',
ACHIEVEMENT_UNLOCKED = 'achievement_unlocked'
}
interface ChallengeEvent {
type: ChallengeEventType
timestamp: string
userId: string
data: any
}
// Event emitter для уведомлений
class ChallengeEventEmitter {
private listeners: Map<ChallengeEventType, Array<(event: ChallengeEvent) => void>> = new Map()
on(type: ChallengeEventType, callback: (event: ChallengeEvent) => void) {
if (!this.listeners.has(type)) {
this.listeners.set(type, [])
}
this.listeners.get(type)!.push(callback)
}
emit(event: ChallengeEvent) {
const callbacks = this.listeners.get(event.type) || []
callbacks.forEach(cb => cb(event))
}
}
// Использование
const events = new ChallengeEventEmitter()
events.on(ChallengeEventType.TASK_COMPLETED, (event) => {
// Показать toast уведомление
showNotification('✅ Задание выполнено!', 'success')
// Обновить статистику
refreshStats()
// Отправить аналитику
analytics.track('task_completed', event.data)
})
```
## 📱 Адаптивная аналитика
### Мобильная версия дашборда
```typescript
interface MobileDashboard {
// Упрощенные метрики для мобильных
quickStats: {
completedToday: number
currentStreak: number
nextTask: string
}
// Минимальные графики
weekProgress: number[] // 7 последних дней
// Быстрые действия
quickActions: Array<{
label: string
action: () => void
icon: string
}>
}
```
## 🎨 UI Components для метрик
### Stat Card Component
```typescript
interface StatCardProps {
title: string
value: number | string
change?: number // % изменение
trend?: 'up' | 'down'
icon?: string
}
function StatCard({ title, value, change, trend, icon }: StatCardProps) {
return (
<div className="stat-card">
<div className="stat-header">
<span className="stat-icon">{icon}</span>
<span className="stat-title">{title}</span>
</div>
<div className="stat-value">{value}</div>
{change && (
<div className={`stat-change ${trend}`}>
{trend === 'up' ? '↑' : '↓'} {Math.abs(change)}%
</div>
)}
</div>
)
}
// Использование
<StatCard
title="Задания завершено"
value={42}
change={15}
trend="up"
icon="✅"
/>
```
## 🔍 A/B Testing
### Метрики для тестирования
```typescript
interface ABTestMetrics {
variant: 'A' | 'B'
// Конверсионные метрики
submissionRate: number // % пользователей, отправивших хотя бы одно задание
completionRate: number // % завершенных заданий
retryRate: number // % повторных попыток
// Временные метрики
timeToFirstSubmission: number
sessionDuration: number
// Качественные метрики
satisfactionScore?: number // если есть опрос
}
// Сравнение вариантов
function compareVariants(variantA: ABTestMetrics, variantB: ABTestMetrics) {
return {
submissionRateDiff: ((variantB.submissionRate - variantA.submissionRate) / variantA.submissionRate) * 100,
completionRateDiff: ((variantB.completionRate - variantA.completionRate) / variantA.completionRate) * 100,
winner: variantB.completionRate > variantA.completionRate ? 'B' : 'A'
}
}
```
## 📊 Экспорт данных
### CSV Export
```typescript
async function exportUserProgress(userId: string): Promise<string> {
const stats = await challengeAPI.getUserStats(userId)
const submissions = await challengeAPI.getSubmissions(userId)
let csv = 'Task,Status,Attempts,Last Attempt,Feedback\n'
stats.taskStats.forEach(task => {
csv += `"${task.taskTitle}","${task.status}",${task.totalAttempts},"${task.lastAttemptAt || 'N/A'}",""\n`
})
return csv
}
// Скачивание файла
function downloadCSV(csv: string, filename: string) {
const blob = new Blob([csv], { type: 'text/csv' })
const url = URL.createObjectURL(blob)
const link = document.createElement('a')
link.href = url
link.download = filename
link.click()
URL.revokeObjectURL(url)
}
```
---
## ✅ Чек-лист для фронтенд-разработчика
- [ ] Интегрировать API клиент
- [ ] Настроить Context для state management
- [ ] Реализовать polling механизм
- [ ] Добавить Personal Dashboard
- [ ] Создать визуализации прогресса
- [ ] Настроить event tracking
- [ ] Добавить offline support
- [ ] Реализовать экспорт данных
- [ ] Добавить A/B тестирование
- [ ] Настроить мониторинг ошибок
- [ ] Оптимизировать для мобильных
- [ ] Добавить accessibility features
## 📚 Полезные ресурсы
- **API документация**: `CHALLENGE_API_README.md`
- **Архитектура**: `CHALLENGE_ARCHITECTURE.md`
- **React пример**: `CHALLENGE_REACT_EXAMPLE.md`
- **Быстрый старт**: `CHALLENGE_QUICK_START.md`
Используйте эти метрики и компоненты для создания информативного и user-friendly интерфейса!