Розробка системи тестів і квізів для LMS

Наша компанія займається розробкою, підтримкою та обслуговуванням сайтів будь-якої складності. Від простих односторінкових сайтів до масштабних кластерних систем, побудованих на мікро сервісах. Досвід розробників підтверджено сертифікатами від вендорів.

Розробка та обслуговування будь-яких видів сайтів:

Інформаційні сайти або веб-програми
Сайти візитки, landing page, корпоративні сайти, онлайн каталоги, квіз, промо-сайти, блоги, ресурси новин, інформаційні портали, форуми, агрегатори
Сайти або веб-програми електронної комерції
Інтернет-магазини, B2B-портали, маркетплейси, онлайн-обмінники, кешбек-сайти, біржі, дропшиппінг-платформи, парсери товарів
Веб-програми для управління бізнес-процесами
CRM-системи, ERP-системи, корпоративні портали, системи управління виробництвом, парсери інформації
Сайти або веб-програми електронних послуг
Дошки оголошень, онлайн-школи, онлайн-кінотеатри, конструктори сайтів, портали надання електронних послуг, відеохостинги, тематичні портали

Це лише деякі з технічних типів сайтів, з якими ми працюємо, і кожен із них може мати свої специфічні особливості та функціональність, а також бути адаптованим під конкретні потреби та цілі клієнта.

Пропоновані послуги
Показано 1 з 1 послугУсі 2065 послуг
Розробка системи тестів і квізів для LMS
Середня
~5 робочих днів
Часті питання

Наші компетенції:

Етапи розробки

Останні роботи

  • image_website-b2b-advance_0.png
    Розробка сайту компанії B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Розробка веб-додатків для компанії FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Розробка веб-сайту для компанії БЕЛФІНГРУП
    874
  • image_ecommerce_furnoro_435_0.webp
    Розробка інтернет магазину для компанії FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Розробка веб-додатків для компанії Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Розробка веб-сайту для компанії ФІКСПЕР
    851

Розроблення системи тестів та квізів у LMS

Тести в LMS — це не просто питання та відповіді. Потрібні: різні типи питань, перемішування, обмеження часу, кілька спроб, детальний результат з правильними відповідями, захист від списування.

Типи питань

type QuestionType =
  | 'single_choice'      // одна правильна відповідь
  | 'multiple_choice'    // кілька правильних
  | 'true_false'
  | 'short_answer'       // текст, перевіряється по ключовим словам або вручну
  | 'ordering'           // розставити в правильному порядку
  | 'matching'           // відповідність пар
  | 'fill_blank';        // заповнити пропуск

interface Question {
  id: string;
  type: QuestionType;
  text: string;
  points: number;
  explanation?: string;   // показується після відповіді
  answers: Answer[];
}

interface QuizSettings {
  timeLimit?: number;       // секунди, null = без обмеження
  maxAttempts: number;      // 0 = без обмеження
  passingScore: number;     // процент
  shuffleQuestions: boolean;
  shuffleAnswers: boolean;
  showCorrectAnswers: 'never' | 'after_attempt' | 'after_passing';
}

Початок спроби

app.post('/api/quizzes/:quizId/attempts', authenticate, async (req, res) => {
  const quiz = await db.quizzes.findById(req.params.quizId);
  const enrollment = await db.enrollments.findByUserAndCourse(req.user.id, quiz.courseId);

  // Перевірити обмеження спроб
  const previousAttempts = await db.quizAttempts.countByUserAndQuiz(
    req.user.id, quiz.id
  );

  if (quiz.settings.maxAttempts > 0 && previousAttempts >= quiz.settings.maxAttempts) {
    return res.status(429).json({ error: 'Max attempts reached' });
  }

  // Перемішати питання за потреби
  let questions = quiz.questions;
  if (quiz.settings.shuffleQuestions) {
    questions = shuffleArray([...questions]);
  }

  const attempt = await db.quizAttempts.create({
    quizId: quiz.id,
    userId: req.user.id,
    questions: questions.map(q => ({
      id: q.id,
      answers: q.answers.map(a => ({ id: a.id, text: a.text })),
    })),
    startedAt: new Date(),
    expiresAt: quiz.settings.timeLimit
      ? new Date(Date.now() + quiz.settings.timeLimit * 1000)
      : null,
  });

  res.json({
    attemptId: attempt.id,
    questions: attempt.questions,
    expiresAt: attempt.expiresAt,
  });
});

Відправлення та оцінювання

app.post('/api/attempts/:attemptId/submit', authenticate, async (req, res) => {
  const attempt = await db.quizAttempts.findById(req.params.attemptId);

  if (attempt.userId !== req.user.id) return res.status(403).end();
  if (attempt.submittedAt) return res.status(409).json({ error: 'Already submitted' });

  const { answers } = req.body;

  const quiz = await db.quizzes.findById(attempt.quizId);
  let totalPoints = 0;
  let earnedPoints = 0;

  const results = quiz.questions.map(question => {
    totalPoints += question.points;
    const userAnswer = answers[question.id];
    let isCorrect = false;
    let pointsEarned = 0;

    switch (question.type) {
      case 'single_choice':
      case 'true_false':
        const correctAnswer = question.answers.find(a => a.isCorrect);
        isCorrect = userAnswer === correctAnswer?.id;
        pointsEarned = isCorrect ? question.points : 0;
        break;

      case 'multiple_choice':
        const correctIds = new Set(question.answers.filter(a => a.isCorrect).map(a => a.id));
        const userIds = new Set(Array.isArray(userAnswer) ? userAnswer : []);
        isCorrect = correctIds.size === userIds.size &&
          [...correctIds].every(id => userIds.has(id));
        pointsEarned = isCorrect ? question.points : 0;
        break;

      case 'short_answer':
        const keywords = question.answers[0]?.keywords ?? [];
        const matchCount = keywords.filter(kw =>
          (userAnswer as string).toLowerCase().includes(kw.toLowerCase())
        ).length;
        isCorrect = matchCount >= (question.answers[0]?.minKeywords ?? 1);
        pointsEarned = isCorrect ? question.points : 0;
        break;
    }

    earnedPoints += pointsEarned;
    return { questionId: question.id, isCorrect, pointsEarned };
  });

  const scorePercent = Math.round((earnedPoints / totalPoints) * 100);
  const passed = scorePercent >= quiz.settings.passingScore;

  await db.quizAttempts.update(attempt.id, {
    answers,
    results,
    score: scorePercent,
    passed,
    submittedAt: new Date(),
  });

  res.json({
    score: scorePercent,
    passed,
    earnedPoints,
    totalPoints,
  });
});

Компонент таймера тесту

function QuizTimer({ expiresAt, onExpired }) {
  const [remaining, setRemaining] = useState(0);

  useEffect(() => {
    const interval = setInterval(() => {
      const diff = Math.max(0, new Date(expiresAt).getTime() - Date.now());
      setRemaining(Math.floor(diff / 1000));
      if (diff <= 0) {
        onExpired();
        clearInterval(interval);
      }
    }, 1000);

    return () => clearInterval(interval);
  }, [expiresAt, onExpired]);

  const minutes = Math.floor(remaining / 60);
  const seconds = remaining % 60;

  return (
    <div className={remaining < 60 ? 'text-red-600 font-bold' : 'text-gray-600'}>
      Часу залишилось: {minutes}:{seconds.toString().padStart(2, '0')}
    </div>
  );
}

Строки виконання

Базова система квізів (одиночний вибір, оцінювання) — 1 тиждень. З усіма типами питань, обмеженнями часу, кількома спробами та аналітикою — 2–3 тижні.