Разработка системы домашних заданий для LMS
Система домашних заданий в LMS — это не просто форма загрузки файлов. Полноценная реализация включает несколько типов заданий, управление сроками сдачи, хранение файлов, статусы выполнения и связь с системой оценок.
Типы домашних заданий
Разные курсы требуют разных форматов ответов:
- Текстовый ответ — эссе, теоретические вопросы
- Загрузка файлов — PDF, Word, архивы с кодом
- Ссылка на внешний ресурс — GitHub репозиторий, Google Docs, опубликованный сайт
- Онлайн-редактор кода — встроенный редактор для программирования (Monaco Editor)
- Quiz — тестирование с вариантами ответов
- Peer review — взаимная проверка между студентами
Модель данных
CREATE TABLE assignments (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
lesson_id UUID REFERENCES lessons(id),
course_id UUID REFERENCES courses(id),
title VARCHAR(500) NOT NULL,
description TEXT,
type VARCHAR(50) NOT NULL, -- text, file, url, code, quiz
max_score INT NOT NULL DEFAULT 100,
deadline TIMESTAMPTZ,
late_submission BOOLEAN DEFAULT FALSE,
late_penalty INT DEFAULT 0, -- % штраф за опоздание
max_attempts INT DEFAULT 1,
is_required BOOLEAN DEFAULT TRUE,
created_at TIMESTAMPTZ DEFAULT NOW()
);
CREATE TABLE submissions (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
assignment_id UUID REFERENCES assignments(id),
student_id UUID REFERENCES users(id),
attempt_number INT NOT NULL DEFAULT 1,
status VARCHAR(50) DEFAULT 'draft', -- draft, submitted, reviewing, graded
text_answer TEXT,
file_urls JSONB DEFAULT '[]',
external_url VARCHAR(2000),
code_answer TEXT,
submitted_at TIMESTAMPTZ,
graded_at TIMESTAMPTZ,
score INT,
feedback TEXT,
UNIQUE(assignment_id, student_id, attempt_number)
);
Загрузка файлов
Прямая загрузка через сервер — антипаттерн для LMS с тысячами студентов. Правильный подход: presigned URLs для загрузки напрямую из браузера в S3/MinIO.
// Backend: генерация presigned URL
async function getUploadUrl(assignmentId, fileName, fileSize, userId) {
if (fileSize > 50 * 1024 * 1024) { // 50MB лимит
throw new Error('File too large');
}
const allowedTypes = ['application/pdf', 'application/zip', 'image/png', 'image/jpeg'];
if (!allowedTypes.includes(mimeType)) {
throw new Error('File type not allowed');
}
const key = `submissions/${userId}/${assignmentId}/${uuid()}-${fileName}`;
const url = await s3.getSignedUrlPromise('putObject', {
Bucket: process.env.S3_BUCKET,
Key: key,
Expires: 300, // 5 минут на загрузку
ContentType: mimeType,
ContentLength: fileSize,
});
return { uploadUrl: url, fileKey: key };
}
Статусы и workflow
draft → submitted → reviewing → graded
↘ returned (запрошена доработка)
При переходе в submitted — уведомление преподавателю. При graded — уведомление студенту. При returned — уведомление студенту с комментарием и возможность переотправить.
Поздняя сдача
Если late_submission = true, задание принимается после дедлайна, но оценка автоматически снижается на late_penalty процентов. Если false — форма загрузки блокируется после дедлайна.
function calculateFinalScore(rawScore, submittedAt, deadline, latePenalty) {
if (submittedAt <= deadline) return rawScore;
const hoursLate = (submittedAt - deadline) / (1000 * 60 * 60);
if (hoursLate > 168) return 0; // Более недели — автоматически 0
const penalty = Math.min(latePenalty * Math.ceil(hoursLate / 24), 50);
return Math.round(rawScore * (1 - penalty / 100));
}
Множественные попытки
При max_attempts > 1 студент может переотправить задание. В этом случае в submissions создаётся новая запись с инкрементированным attempt_number. В систему оценок передаётся лучший результат из всех попыток — или последний, в зависимости от настроек курса.
Сроки
Базовая система с текстовыми ответами и загрузкой файлов, статусами и уведомлениями — 5–7 дней. Добавление code editor (Monaco), quiz-типа и peer review — ещё 5–7 дней.







