Разработка системы прогресса обучения (Progress Tracking) для LMS
Progress Tracking в LMS — это не просто счётчик «просмотрено X из Y уроков». Полноценная система отслеживает активность на уровне секунд, анализирует паттерны обучения и помогает выявить студентов, которые рискуют бросить курс.
Что и как отслеживать
Уровень урока:
- Время открытия и закрытия
- Доля просмотренного видео (не просто «открыл», а «досмотрел до 85%»)
- Скроллинг текстового контента
- Клики по интерактивным элементам
Уровень курса:
- Процент завершённых уроков
- Процент завершённых заданий
- Последний день активности
- Streak (дней подряд)
Уровень платформы:
- Среднее время сессии
- Устройства и время суток активности
- Cohort retention (сколько % студентов из когорты активны через 7/14/30 дней)
Модель данных
-- Прогресс по урокам
CREATE TABLE lesson_progress (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
student_id UUID REFERENCES users(id),
lesson_id UUID REFERENCES lessons(id),
course_id UUID REFERENCES courses(id),
status VARCHAR(30) DEFAULT 'not_started', -- not_started, in_progress, completed
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
time_spent_sec INT DEFAULT 0,
video_progress NUMERIC(5,2), -- % просмотра видео
last_position INT, -- секунда видео при последнем просмотре
UNIQUE(student_id, lesson_id)
);
-- Прогресс по курсу (агрегат)
CREATE TABLE course_progress (
student_id UUID REFERENCES users(id),
course_id UUID REFERENCES courses(id),
lessons_completed INT DEFAULT 0,
lessons_total INT NOT NULL,
percentage NUMERIC(5,2) DEFAULT 0,
last_activity_at TIMESTAMPTZ,
started_at TIMESTAMPTZ,
completed_at TIMESTAMPTZ,
streak_days INT DEFAULT 0,
PRIMARY KEY(student_id, course_id)
);
-- Детальный лог активности для аналитики
CREATE TABLE activity_events (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
student_id UUID REFERENCES users(id),
event_type VARCHAR(100) NOT NULL, -- 'video_played', 'video_paused', 'lesson_completed'
entity_type VARCHAR(50),
entity_id UUID,
metadata JSONB DEFAULT '{}', -- position, duration, device, etc.
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE INDEX ON activity_events (student_id, created_at DESC);
CREATE INDEX ON activity_events (entity_id, event_type);
Трекинг прогресса видео
// Frontend: отправка прогресса видео
class VideoProgressTracker {
constructor(videoElement, lessonId) {
this.video = videoElement;
this.lessonId = lessonId;
this.maxReached = 0;
this.setupListeners();
}
setupListeners() {
// Отправляем прогресс при паузе, не при каждом timeupdate
this.video.addEventListener('pause', () => this.reportProgress());
this.video.addEventListener('ended', () => this.markCompleted());
// Треккинг максимально просмотренной точки (не считаем перемотку назад)
this.video.addEventListener('timeupdate', () => {
const pct = (this.video.currentTime / this.video.duration) * 100;
if (pct > this.maxReached) this.maxReached = pct;
});
}
async reportProgress() {
await api.post(`/lessons/${this.lessonId}/progress`, {
videoProgress: this.maxReached,
lastPosition: Math.floor(this.video.currentTime),
timeSpentSec: Math.floor(this.video.currentTime),
});
}
async markCompleted() {
if (this.maxReached >= 85) { // Считаем урок просмотренным при 85%
await api.post(`/lessons/${this.lessonId}/complete`);
}
}
}
Расчёт streak
async function updateStreak(studentId, courseId) {
const lastActivity = await db.courseProgress.findOne({ studentId, courseId }, 'last_activity_at');
const today = new Date().toDateString();
const yesterday = new Date(Date.now() - 86400000).toDateString();
const lastDate = new Date(lastActivity.lastActivityAt).toDateString();
let streakDelta = 0;
if (lastDate === today) {
streakDelta = 0; // Уже обновлено сегодня
} else if (lastDate === yesterday) {
streakDelta = 1; // Продолжение streak
} else {
// Streak сброшен — начинаем заново с 1
await db.courseProgress.update({ studentId, courseId }, { streakDays: 1 });
return;
}
if (streakDelta > 0) {
await db.courseProgress.increment({ studentId, courseId }, 'streak_days', 1);
}
}
Выявление «отстающих»
Автоматические алерты для преподавателей:
- Студент не заходил 7+ дней при незавершённом курсе
- Прогресс < 20% через 2 недели после записи
- Резкое замедление: прошлая неделя — 5 уроков, эта — 0
-- Студенты, неактивные 7+ дней
SELECT cp.student_id, u.name, u.email,
cp.percentage, cp.last_activity_at
FROM course_progress cp
JOIN users u ON u.id = cp.student_id
WHERE cp.course_id = $1
AND cp.completed_at IS NULL
AND cp.last_activity_at < NOW() - INTERVAL '7 days';
Сроки
Базовый трекинг прогресса уроков с агрегацией по курсу — 3–4 дня. Видеотрекинг с отправкой событий и streak — ещё 2–3 дня. Аналитические дашборды для преподавателей и алерты — 3–4 дня.







