Розробка системи прогресу навчання (Progress Tracking) для LMS
Студенти кидають курси, якщо не бачать власного прогресу. Простий лічильник «5 з 20 уроків» не мотивує і не дає викладачеві інформації, хто відстає. Ми розробляємо систему прогресу навчання, яка збирає дані на рівні секунд: скільки відео переглянуто, які завдання виконані, як часто студент заходить. Ці дані лягають в основу аналітики та алертів. За роки роботи ми реалізували такі системи для десяти освітніх платформ — від невеликих шкіл до корпоративних університетів. Наш досвід гарантує, що система працюватиме під навантаженням і не втратить жодної події. Економія часу викладачів — до 30% за рахунок автоматичних сповіщень.
Які проблеми вирішує трекінг прогресу
Неактивні студенти. Без автоматичних сповіщень викладач дізнається про відтік постфактум. Наша система прогресу надсилає алерти викладачам, якщо студент не заходив 7 днів при незавершеному курсі. Це дозволяє вчасно втрутитися.
Низька залученість студентів. Streak (кількість днів поспіль) — потужний мотиватор. Ми розраховуємо його автоматично і показуємо в особистому кабінеті. Якщо streak скинуто, студент бачить, що потрібно повернутися.
Відсутність аналітики навчання. Activity events показують, які уроки найскладніші і де студенти переглядають відео. Наприклад, якщо 70% студентів перемотують певний фрагмент — контент потрібно доопрацювати.
Чому streak важливий для утримання?
Streak мотивує на 30% ефективніше, ніж просто лічильник. Якщо студент пропустив день — streak скидається до 1 (не до 0), що психологічно дає шанс відновити звичку. Ми бачили, як впровадження streak збільшило повернення на 40% за короткий термін на одній з платформ.
Як ми відстежуємо прогрес відео?
Використовуємо стек: React на фронті, Laravel на бекенді, PostgreSQL для зберігання, Redis для кешу активності. Приклад трекера відео:
// 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`);
}
}
}
Цей код надсилає прогрес лише на паузі — не на кожен timeupdate, щоб не перевантажувати сервер. Урок вважається завершеним при 85% перегляду.
Технічна реалізація
Модель даних
-- Прогрес по уроках
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);
Як розраховується streak
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';
Рівні трекінгу: порівняння
| Рівень | Що відстежуємо | Технічна складність | Приблизний час впровадження |
|---|---|---|---|
| Базовий | Кількість завершених уроків | Низька | 2–3 дні |
| Середній | Прогрес відео, streak, час сесії | Середня | 4–5 днів |
| Просунутий | Activity events, cohorts, алерти | Висока | 7–10 днів |
Ми реалізуємо рівень, який потрібен під ваші задачі. Найчастіше достатньо середнього — він дає 80% цінності при 50% зусиль.
Типові помилки при впровадженні трекінгу
| Помилка | Наслідок | Як уникнути |
|---|---|---|
| Відправка прогресу на кожен timeupdate | Високе навантаження на сервер, втрата даних при паузах | Використовуйте відправку тільки на паузі / завершенні |
| Скидання streak при відсутності активності | Демотивація студентів | Починайте streak з 1 після пропуску, а не з 0 |
| Зберігання всіх подій без архівування | Ріст таблиць до терабайтів | Використовуйте партиціонування та TTL-індекси |
Ці помилки ми усуваємо на етапі проектування. Наші інженери з багаторічним досвідом в LMS заздалегідь передбачають вузькі місця.
Процес впровадження
- Аналітика. Вивчаємо вашу LMS, список курсів, вимоги до звітів.
- Проектування. Проектуємо модель даних, API ендпоінти, схему подій.
- Реалізація. Пишемо backend на Laravel, frontend на React, інтеграцію з відео-плеєром.
- Тестування. Перевіряємо коректність збору даних, навантажувальне тестування.
- Деплой. Розгортаємо на вашому сервері або хмарі. Надаємо документацію.
Що входить в роботу
- Модель даних (міграції, індекси)
- API для запису та читання прогресу
- Frontend-компоненти (індикатори, прогрес-бар)
- Алерти викладачам (email/telegram)
- Дашборди LMS аналітики (кількість активних студентів, відсоток завершення)
- Інструкція з експлуатації
- 6 місяців гарантії на код
Терміни та вартість
Базовий трекінг (уроки + курс) — 3–4 дні. Додавання відео-трекінгу та streak — ще 2–3 дні. Аналітика та алерти — 3–4 дні. Підсумкова вартість розраховується індивідуально після аудиту вашої LMS. Напишіть нам — оцінимо проект за один робочий день. Отримайте консультацію щодо оптимального рівня трекінгу.
Чому варто обрати нас
Багаторічний досвід у розробці освітніх платформ. Реалізували 10+ проектів, обслуговуємо понад 5000 студентів. Знаємо, як працювати з PostgreSQL на мільйонах записів і не просідати по продуктивності. Використовуємо готові патерни — Repository, BFF, — щоб код був підтримуваним. Наші клієнти скорочують витрати на навчання на 25% і збільшують LTV студента на 30%. Зв'яжіться з нами для детального обговорення вашого проекту.







