- Add QuizQuestion and QuizOption domain entities with QuestionType enum - Add IQuizQuestionRepository and IQuizOptionRepository interfaces - Add QuizQuestionRepository and QuizOptionRepository EF Core implementations - Add QuizQuestionService with CRUD, quiz submission, and statistics - Add QuizQuestionsController with comprehensive REST API endpoints - Add QuizQuestionDto and related DTOs for API communication - Add EF Core migration (20260612050652_AddQuizQuestionTables) for QuizQuestion and QuizOption - Add seed data with sample quiz questions for Greetings, Numbers, Grammar lessons - Update AppDbContext with DbSets and entity configurations - Update Program.cs with migration retry logic for Docker - Update docker-compose.yml with SeedDatabase configuration - Add unit tests for QuizQuestionService Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
266 lines
7.2 KiB
C#
266 lines
7.2 KiB
C#
namespace GermanApp.Domain.Entities;
|
|
|
|
/// <summary>
|
|
/// Represents a quiz question for a lesson.
|
|
/// Each question has a type (multiple choice, true/false, fill-in-the-blank, etc.)
|
|
/// and is associated with a specific lesson.
|
|
/// </summary>
|
|
public class QuizQuestion
|
|
{
|
|
public int Id { get; private set; }
|
|
public int LessonId { get; private set; }
|
|
public string QuestionText { get; private set; } = string.Empty;
|
|
public QuestionType Type { get; private set; }
|
|
public string? CorrectAnswer { get; private set; }
|
|
public int Difficulty { get; private set; } // 1-5 scale
|
|
public int Order { get; private set; }
|
|
public bool IsActive { get; private set; } = true;
|
|
public DateTime CreatedAt { get; private set; }
|
|
public DateTime? UpdatedAt { get; private set; }
|
|
|
|
// Navigation property
|
|
public virtual Lesson? Lesson { get; private set; }
|
|
public virtual ICollection<QuizOption> Options { get; private set; } = new List<QuizOption>();
|
|
|
|
/// <summary>
|
|
/// Constructor for EF Core deserialization.
|
|
/// </summary>
|
|
private QuizQuestion() { }
|
|
|
|
/// <summary>
|
|
/// Factory method to create a new quiz question.
|
|
/// </summary>
|
|
/// <param name="lessonId">ID of the parent lesson</param>
|
|
/// <param name="questionText">The question text</param>
|
|
/// <param name="type">The question type</param>
|
|
/// <param name="correctAnswer">The correct answer</param>
|
|
/// <param name="difficulty">Difficulty level (1-5)</param>
|
|
/// <param name="order">Sort order within the lesson</param>
|
|
public static QuizQuestion Create(
|
|
int lessonId,
|
|
string questionText,
|
|
QuestionType type,
|
|
string correctAnswer,
|
|
int difficulty = 3,
|
|
int order = 1)
|
|
{
|
|
if (difficulty < 1 || difficulty > 5)
|
|
throw new ArgumentOutOfRangeException(nameof(difficulty), "Difficulty must be between 1 and 5");
|
|
|
|
if (string.IsNullOrWhiteSpace(questionText))
|
|
throw new ArgumentException("Question text cannot be empty", nameof(questionText));
|
|
|
|
return new QuizQuestion
|
|
{
|
|
LessonId = lessonId,
|
|
QuestionText = questionText,
|
|
Type = type,
|
|
CorrectAnswer = correctAnswer,
|
|
Difficulty = difficulty,
|
|
Order = order,
|
|
CreatedAt = DateTime.UtcNow
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the question text.
|
|
/// </summary>
|
|
public void UpdateQuestionText(string newQuestionText)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(newQuestionText))
|
|
throw new ArgumentException("Question text cannot be empty", nameof(newQuestionText));
|
|
|
|
QuestionText = newQuestionText;
|
|
UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the correct answer.
|
|
/// </summary>
|
|
public void UpdateCorrectAnswer(string newCorrectAnswer)
|
|
{
|
|
CorrectAnswer = newCorrectAnswer;
|
|
UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the difficulty level.
|
|
/// </summary>
|
|
public void UpdateDifficulty(int newDifficulty)
|
|
{
|
|
if (newDifficulty < 1 || newDifficulty > 5)
|
|
throw new ArgumentOutOfRangeException(nameof(newDifficulty), "Difficulty must be between 1 and 5");
|
|
|
|
Difficulty = newDifficulty;
|
|
UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the sort order.
|
|
/// </summary>
|
|
public void UpdateOrder(int newOrder)
|
|
{
|
|
Order = newOrder;
|
|
UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the question type.
|
|
/// </summary>
|
|
public void UpdateType(QuestionType newType)
|
|
{
|
|
Type = newType;
|
|
UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the associated lesson.
|
|
/// </summary>
|
|
public void UpdateLesson(int newLessonId)
|
|
{
|
|
LessonId = newLessonId;
|
|
UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Activates the quiz question.
|
|
/// </summary>
|
|
public void Activate()
|
|
{
|
|
IsActive = true;
|
|
UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Deactivates the quiz question.
|
|
/// </summary>
|
|
public void Deactivate()
|
|
{
|
|
IsActive = false;
|
|
UpdatedAt = DateTime.UtcNow;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Adds an option to this question.
|
|
/// </summary>
|
|
public void AddOption(QuizOption option)
|
|
{
|
|
Options.Add(option);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Removes an option from this question.
|
|
/// </summary>
|
|
public void RemoveOption(QuizOption option)
|
|
{
|
|
Options.Remove(option);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Represents an option for a multiple-choice quiz question.
|
|
/// </summary>
|
|
public class QuizOption
|
|
{
|
|
public int Id { get; private set; }
|
|
public int QuizQuestionId { get; private set; }
|
|
public string Text { get; private set; } = string.Empty;
|
|
public bool IsCorrect { get; private set; }
|
|
public int Order { get; private set; }
|
|
|
|
// Navigation property
|
|
public virtual QuizQuestion? QuizQuestion { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Constructor for EF Core deserialization.
|
|
/// </summary>
|
|
private QuizOption() { }
|
|
|
|
/// <summary>
|
|
/// Factory method to create a new quiz option.
|
|
/// </summary>
|
|
/// <param name="quizQuestionId">ID of the parent quiz question</param>
|
|
/// <param name="text">The option text</param>
|
|
/// <param name="isCorrect">Whether this is the correct answer</param>
|
|
/// <param name="order">Sort order within the question's options</param>
|
|
public static QuizOption Create(
|
|
int quizQuestionId,
|
|
string text,
|
|
bool isCorrect = false,
|
|
int order = 1)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
throw new ArgumentException("Option text cannot be empty", nameof(text));
|
|
|
|
return new QuizOption
|
|
{
|
|
QuizQuestionId = quizQuestionId,
|
|
Text = text,
|
|
IsCorrect = isCorrect,
|
|
Order = order
|
|
};
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the option text.
|
|
/// </summary>
|
|
public void UpdateText(string newText)
|
|
{
|
|
if (string.IsNullOrWhiteSpace(newText))
|
|
throw new ArgumentException("Option text cannot be empty", nameof(newText));
|
|
|
|
Text = newText;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates whether this option is correct.
|
|
/// </summary>
|
|
public void UpdateIsCorrect(bool newIsCorrect)
|
|
{
|
|
IsCorrect = newIsCorrect;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the sort order.
|
|
/// </summary>
|
|
public void UpdateOrder(int newOrder)
|
|
{
|
|
Order = newOrder;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Enum representing the types of quiz questions.
|
|
/// </summary>
|
|
public enum QuestionType
|
|
{
|
|
/// <summary>
|
|
/// Multiple choice with single correct answer
|
|
/// </summary>
|
|
MultipleChoice = 0,
|
|
|
|
/// <summary>
|
|
/// Multiple choice with multiple correct answers
|
|
/// </summary>
|
|
MultipleSelect = 1,
|
|
|
|
/// <summary>
|
|
/// True or false question
|
|
/// </summary>
|
|
TrueFalse = 2,
|
|
|
|
/// <summary>
|
|
/// Fill-in-the-blank question
|
|
/// </summary>
|
|
FillInTheBlank = 3,
|
|
|
|
/// <summary>
|
|
/// Matching question
|
|
/// </summary>
|
|
Matching = 4,
|
|
|
|
/// <summary>
|
|
/// Short answer question
|
|
/// </summary>
|
|
ShortAnswer = 5
|
|
}
|