- Add Quiz entity with full domain logic (Create, Update, Activate, Deactivate) - Add QuizQuestion entity updated to use QuizId instead of LessonId - Add QuizRepository with all CRUD and query methods - Add QuizQuestionRepository with QuizId-based and LessonId-based (legacy) methods - Add QuizDto, CreateQuizDto, UpdateQuizDto, QuizWithQuestionsDto, QuizListItemDto - Add QuizQuestionDto updated with QuizId, LessonId, QuizTitle, Points fields - Add CreateQuizCommand, UpdateQuizCommand, DeleteQuizCommand, GetQuizWithQuestionsCommand - Add QuizService with full quiz management (CRUD, activate, deactivate, counts, queries) - Add QuizQuestionService updated with QuizId-based methods - Add QuizzesController with comprehensive endpoints (GET, POST, PUT, DELETE) - Add QuizzesController endpoints for quiz questions (by-quiz, random, active) - Update QuizQuestionsController with new QuizId-based endpoints (backward compatible) - Register QuizRepository and QuizService in DI container - Update IRepository interfaces with QuizId-based methods - Add SubmitQuizDto for quiz answer submission Clean Architecture layers maintained: - Domain: Quiz, QuizQuestion entities with business logic - Application: DTOs, Commands, Services - Infrastructure: Repositories, DbContext - Presentation: Controllers Generated by Mistral Vibe. Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
285 lines
7.8 KiB
C#
285 lines
7.8 KiB
C#
namespace GermanApp.Domain.Entities;
|
|
|
|
/// <summary>
|
|
/// Represents a quiz question for a quiz.
|
|
/// Each question has a type (multiple choice, true/false, fill-in-the-blank, etc.)
|
|
/// and is associated with a specific quiz.
|
|
/// </summary>
|
|
public class QuizQuestion
|
|
{
|
|
public int Id { get; private set; }
|
|
public int QuizId { 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 Points { get; private set; } = 1; // Points for this question
|
|
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 Quiz? Quiz { 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="quizId">ID of the parent quiz</param>
|
|
/// <param name="questionText">The question text</param>
|
|
/// <param name="type">The question type</param>
|
|
/// <param name="correctAnswer">The correct answer</param>
|
|
/// <param name="points">Points for this question (default 1)</param>
|
|
/// <param name="difficulty">Difficulty level (1-5, default 3)</param>
|
|
/// <param name="order">Sort order within the quiz</param>
|
|
public static QuizQuestion Create(
|
|
int quizId,
|
|
string questionText,
|
|
QuestionType type,
|
|
string correctAnswer,
|
|
int points = 1,
|
|
int difficulty = 3,
|
|
int order = 1)
|
|
{
|
|
if (difficulty < 1 || difficulty > 5)
|
|
throw new ArgumentOutOfRangeException(nameof(difficulty), "Difficulty must be between 1 and 5");
|
|
|
|
if (points < 0)
|
|
throw new ArgumentOutOfRangeException(nameof(points), "Points must be positive");
|
|
|
|
if (string.IsNullOrWhiteSpace(questionText))
|
|
throw new ArgumentException("Question text cannot be empty", nameof(questionText));
|
|
|
|
return new QuizQuestion
|
|
{
|
|
QuizId = quizId,
|
|
QuestionText = questionText,
|
|
Type = type,
|
|
CorrectAnswer = correctAnswer,
|
|
Points = points,
|
|
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 points for this question.
|
|
/// </summary>
|
|
public void UpdatePoints(int newPoints)
|
|
{
|
|
if (newPoints < 0)
|
|
throw new ArgumentOutOfRangeException(nameof(newPoints), "Points must be positive");
|
|
|
|
Points = newPoints;
|
|
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 quiz.
|
|
/// </summary>
|
|
public void UpdateQuiz(int newQuizId)
|
|
{
|
|
QuizId = newQuizId;
|
|
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
|
|
}
|