From a4b260941f704b134176a63c7988bd1356a4c023 Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Tue, 12 Dec 2023 12:09:39 -0700 Subject: [PATCH] added shorter syntax for multipel choice --- .../Markdown/Quiz/MatchingTests.cs | 62 ++++ .../Markdown/Quiz/MultipleAnswersTests.cs | 83 +++++ .../Markdown/Quiz/MultipleChoiceTests.cs | 73 +++++ .../Markdown/{ => Quiz}/QuizMarkdownTests.cs | 0 .../Markdown/Quiz/TextAnswerTests.cs | 114 +++++++ .../Markdown/QuizQuestionMarkdownTests.cs | 302 ------------------ Management.Web/Pages/QuizFormPage.razor | 7 +- .../Components/Quiz/EditableQuizAnswer.razor | 88 ----- .../Markdown/MarkdownQuestionPreview.razor | 97 +++--- .../Models/Local/Quiz/LocalQuizQuestion.cs | 15 +- .../Local/Quiz/LocalQuizQuestionAnswer.cs | 4 +- 11 files changed, 408 insertions(+), 437 deletions(-) create mode 100644 Management.Test/Markdown/Quiz/MatchingTests.cs create mode 100644 Management.Test/Markdown/Quiz/MultipleAnswersTests.cs create mode 100644 Management.Test/Markdown/Quiz/MultipleChoiceTests.cs rename Management.Test/Markdown/{ => Quiz}/QuizMarkdownTests.cs (100%) create mode 100644 Management.Test/Markdown/Quiz/TextAnswerTests.cs delete mode 100644 Management.Test/Markdown/QuizQuestionMarkdownTests.cs delete mode 100644 Management.Web/Shared/Components/Quiz/EditableQuizAnswer.razor diff --git a/Management.Test/Markdown/Quiz/MatchingTests.cs b/Management.Test/Markdown/Quiz/MatchingTests.cs new file mode 100644 index 0000000..48d2dea --- /dev/null +++ b/Management.Test/Markdown/Quiz/MatchingTests.cs @@ -0,0 +1,62 @@ +using LocalModels; + +public class MatchingTests +{ + [Test] + public void CanParseMatchingQuestion() + { + var rawMarkdownQuiz = @" +Name: Test Quiz +ShuffleAnswers: true +OneQuestionAtATime: false +DueAt: 2023-08-21T23:59:00 +LockAt: 2023-08-21T23:59:00 +AssignmentGroup: Assignments +AllowedAttempts: -1 +Description: +--- +Match the following terms & definitions + +^ statement - a single command to be executed +^ identifier - name of a variable +^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.) +"; + + var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); + var firstQuestion = quiz.Questions.First(); + firstQuestion.QuestionType.Should().Be(QuestionType.MATCHING); + firstQuestion.Text.Should().NotContain("statement"); + firstQuestion.Answers.First().MatchedText.Should().Be("a single command to be executed"); + } + + [Test] + public void CanCreateMarkdownForMatchingQuesiton() + { + var rawMarkdownQuiz = @" +Name: Test Quiz +ShuffleAnswers: true +OneQuestionAtATime: false +DueAt: 2023-08-21T23:59:00 +LockAt: 2023-08-21T23:59:00 +AssignmentGroup: Assignments +AllowedAttempts: -1 +Description: +--- +Match the following terms & definitions + +^ statement - a single command to be executed +^ identifier - name of a variable +^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.) +"; + + var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); + var questionMarkdown = quiz.Questions.First().ToMarkdown(); + var expectedMarkdown = @"Points: 1 +Match the following terms & definitions + +^ statement - a single command to be executed +^ identifier - name of a variable +^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.)"; + questionMarkdown.Should().Contain(expectedMarkdown); + } +} \ No newline at end of file diff --git a/Management.Test/Markdown/Quiz/MultipleAnswersTests.cs b/Management.Test/Markdown/Quiz/MultipleAnswersTests.cs new file mode 100644 index 0000000..521cd12 --- /dev/null +++ b/Management.Test/Markdown/Quiz/MultipleAnswersTests.cs @@ -0,0 +1,83 @@ +using LocalModels; + +public class MultipleAnswersTests +{ + + [Test] + public void QuzMarkdownIncludesMultipleAnswerQuestion() + { + var quiz = new LocalQuiz() + { + Name = "Test Quiz", + Description = "desc", + LockAt = DateTime.MaxValue, + DueAt = DateTime.MaxValue, + ShuffleAnswers = true, + OneQuestionAtATime = false, + LocalAssignmentGroupName = "someId", + AllowedAttempts = -1, + Questions = new LocalQuizQuestion[] + { + new() + { + Text = "oneline question", + Points = 1, + QuestionType = QuestionType.MULTIPLE_ANSWERS, + Answers = new LocalQuizQuestionAnswer[] + { + new() { Correct = true, Text = "true" }, + new() { Correct = true, Text = "false"}, + new() { Correct = false, Text = "neither"}, + } + } + } + }; + var markdown = quiz.ToMarkdown(); + var expectedQuestionString = @" +Points: 1 +oneline question +[*] true +[*] false +[ ] neither +"; + markdown.Should().Contain(expectedQuestionString); + } + + [Test] + public void CanParseQuestionWithMultipleAnswers() + { + var rawMarkdownQuiz = @" +Name: Test Quiz +ShuffleAnswers: true +OneQuestionAtATime: false +DueAt: 2023-08-21T23:59:00 +LockAt: 2023-08-21T23:59:00 +AssignmentGroup: Assignments +AllowedAttempts: -1 +Description: this is the +multi line +description +--- +Which events are triggered when the user clicks on an input field? +[*] click +[*] focus +[*] mousedown +[] submit +[] change +[] mouseout +[] keydown +--- +"; + + var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); + var firstQuestion = quiz.Questions.First(); + firstQuestion.Points.Should().Be(1); + firstQuestion.QuestionType.Should().Be(QuestionType.MULTIPLE_ANSWERS); + firstQuestion.Text.Should().Contain("Which events are triggered when the user clicks on an input field?"); + firstQuestion.Answers.First().Text.Should().Be("click"); + firstQuestion.Answers.First().Correct.Should().BeTrue(); + firstQuestion.Answers.ElementAt(3).Correct.Should().BeFalse(); + firstQuestion.Answers.ElementAt(3).Text.Should().Be("submit"); + } + +} \ No newline at end of file diff --git a/Management.Test/Markdown/Quiz/MultipleChoiceTests.cs b/Management.Test/Markdown/Quiz/MultipleChoiceTests.cs new file mode 100644 index 0000000..265b449 --- /dev/null +++ b/Management.Test/Markdown/Quiz/MultipleChoiceTests.cs @@ -0,0 +1,73 @@ +using LocalModels; + +public class MultipleChoiceTests +{ + [Test] + public void QuzMarkdownIncludesMultipleChoiceQuestion() + { + var quiz = new LocalQuiz() + { + Name = "Test Quiz", + Description = "desc", + LockAt = DateTime.MaxValue, + DueAt = DateTime.MaxValue, + ShuffleAnswers = true, + OneQuestionAtATime = false, + LocalAssignmentGroupName = "someId", + AllowedAttempts = -1, + Questions = new LocalQuizQuestion[] + { + new LocalQuizQuestion() + { + Points = 2, + Text = @"`some type` of question + +with many + +``` +lines +``` +", + QuestionType = QuestionType.MULTIPLE_CHOICE, + Answers = new LocalQuizQuestionAnswer[] + { + new LocalQuizQuestionAnswer() { Correct = true, Text = "true" }, + new LocalQuizQuestionAnswer() { Correct = false, Text = "false" + Environment.NewLine +Environment.NewLine + "endline" }, + } + } + } + }; + + var markdown = quiz.ToMarkdown(); + var expectedQuestionString = @" +Points: 2 +`some type` of question + +with many + +``` +lines +``` + +*a) true +b) false + +endline +"; + markdown.Should().Contain(expectedQuestionString); + } + + + [Test] + public void LetterOptionalForMultipleChoice() + { + + var questionMarkdown = @"Points: 2 +`some type` of question +*) true +) false + "; + var question = LocalQuizQuestion.ParseMarkdown(questionMarkdown, 0); + question.Answers.Count().Should().Be(2); + } +} \ No newline at end of file diff --git a/Management.Test/Markdown/QuizMarkdownTests.cs b/Management.Test/Markdown/Quiz/QuizMarkdownTests.cs similarity index 100% rename from Management.Test/Markdown/QuizMarkdownTests.cs rename to Management.Test/Markdown/Quiz/QuizMarkdownTests.cs diff --git a/Management.Test/Markdown/Quiz/TextAnswerTests.cs b/Management.Test/Markdown/Quiz/TextAnswerTests.cs new file mode 100644 index 0000000..dc05120 --- /dev/null +++ b/Management.Test/Markdown/Quiz/TextAnswerTests.cs @@ -0,0 +1,114 @@ +using LocalModels; + +public class TextAnswerTests +{ + [Test] + public void CanParseEssay() + { + var rawMarkdownQuiz = @" +Name: Test Quiz +ShuffleAnswers: true +OneQuestionAtATime: false +DueAt: 2023-08-21T23:59:00 +LockAt: 2023-08-21T23:59:00 +AssignmentGroup: Assignments +AllowedAttempts: -1 +Description: this is the +multi line +description +--- +Which events are triggered when the user clicks on an input field? +essay +"; + + var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); + var firstQuestion = quiz.Questions.First(); + firstQuestion.Points.Should().Be(1); + firstQuestion.QuestionType.Should().Be(QuestionType.ESSAY); + firstQuestion.Text.Should().NotContain("essay"); + } + + [Test] + public void CanParseShortAnswer() + { + var rawMarkdownQuiz = @" +Name: Test Quiz +ShuffleAnswers: true +OneQuestionAtATime: false +DueAt: 2023-08-21T23:59:00 +LockAt: 2023-08-21T23:59:00 +AssignmentGroup: Assignments +AllowedAttempts: -1 +Description: this is the +multi line +description +--- +Which events are triggered when the user clicks on an input field? +short answer +"; + + var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); + var firstQuestion = quiz.Questions.First(); + firstQuestion.Points.Should().Be(1); + firstQuestion.QuestionType.Should().Be(QuestionType.SHORT_ANSWER); + firstQuestion.Text.Should().NotContain("short answer"); + } + + [Test] + public void ShortAnswerToMarkdown_IsCorrect() + { + var rawMarkdownQuiz = @" +Name: Test Quiz +ShuffleAnswers: true +OneQuestionAtATime: false +DueAt: 2023-08-21T23:59:00 +LockAt: 2023-08-21T23:59:00 +AssignmentGroup: Assignments +AllowedAttempts: -1 +Description: this is the +multi line +description +--- +Which events are triggered when the user clicks on an input field? +short answer +"; + + var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); + var firstQuestion = quiz.Questions.First(); + + var questionMarkdown = firstQuestion.ToMarkdown(); + var expectedMarkdown = @"Points: 1 +Which events are triggered when the user clicks on an input field? +short_answer"; + questionMarkdown.Should().Contain(expectedMarkdown); + } + + [Test] + public void EssayQuestionToMarkdown_IsCorrect() + { + var rawMarkdownQuiz = @" +Name: Test Quiz +ShuffleAnswers: true +OneQuestionAtATime: false +DueAt: 2023-08-21T23:59:00 +LockAt: 2023-08-21T23:59:00 +AssignmentGroup: Assignments +AllowedAttempts: -1 +Description: this is the +multi line +description +--- +Which events are triggered when the user clicks on an input field? +essay +"; + + var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); + var firstQuestion = quiz.Questions.First(); + + var questionMarkdown = firstQuestion.ToMarkdown(); + var expectedMarkdown = @"Points: 1 +Which events are triggered when the user clicks on an input field? +essay"; + questionMarkdown.Should().Contain(expectedMarkdown); + } +} \ No newline at end of file diff --git a/Management.Test/Markdown/QuizQuestionMarkdownTests.cs b/Management.Test/Markdown/QuizQuestionMarkdownTests.cs deleted file mode 100644 index a0d1cd2..0000000 --- a/Management.Test/Markdown/QuizQuestionMarkdownTests.cs +++ /dev/null @@ -1,302 +0,0 @@ -using LocalModels; - -public class QuizQuestionMarkdownTests -{ - [Test] - public void QuzMarkdownIncludesMultipleChoiceQuestion() - { - var quiz = new LocalQuiz() - { - Name = "Test Quiz", - Description = "desc", - LockAt = DateTime.MaxValue, - DueAt = DateTime.MaxValue, - ShuffleAnswers = true, - OneQuestionAtATime = false, - LocalAssignmentGroupName = "someId", - AllowedAttempts = -1, - Questions = new LocalQuizQuestion[] - { - new LocalQuizQuestion() - { - Points = 2, - Text = @"`some type` of question - -with many - -``` -lines -``` -", - QuestionType = QuestionType.MULTIPLE_CHOICE, - Answers = new LocalQuizQuestionAnswer[] - { - new LocalQuizQuestionAnswer() { Correct = true, Text = "true" }, - new LocalQuizQuestionAnswer() { Correct = false, Text = "false" + Environment.NewLine +Environment.NewLine + "endline" }, - } - } - } - }; - - var markdown = quiz.ToMarkdown(); - var expectedQuestionString = @" -Points: 2 -`some type` of question - -with many - -``` -lines -``` - -*a) true -b) false - -endline -"; - markdown.Should().Contain(expectedQuestionString); - } - - [Test] - public void QuzMarkdownIncludesMultipleAnswerQuestion() - { - var quiz = new LocalQuiz() - { - Name = "Test Quiz", - Description = "desc", - LockAt = DateTime.MaxValue, - DueAt = DateTime.MaxValue, - ShuffleAnswers = true, - OneQuestionAtATime = false, - LocalAssignmentGroupName = "someId", - AllowedAttempts = -1, - Questions = new LocalQuizQuestion[] - { - new() - { - Text = "oneline question", - Points = 1, - QuestionType = QuestionType.MULTIPLE_ANSWERS, - Answers = new LocalQuizQuestionAnswer[] - { - new() { Correct = true, Text = "true" }, - new() { Correct = true, Text = "false"}, - new() { Correct = false, Text = "neither"}, - } - } - } - }; - var markdown = quiz.ToMarkdown(); - var expectedQuestionString = @" -Points: 1 -oneline question -[*] true -[*] false -[ ] neither -"; - markdown.Should().Contain(expectedQuestionString); - } - - [Test] - public void CanParseQuestionWithMultipleAnswers() - { - var rawMarkdownQuiz = @" -Name: Test Quiz -ShuffleAnswers: true -OneQuestionAtATime: false -DueAt: 2023-08-21T23:59:00 -LockAt: 2023-08-21T23:59:00 -AssignmentGroup: Assignments -AllowedAttempts: -1 -Description: this is the -multi line -description ---- -Which events are triggered when the user clicks on an input field? -[*] click -[*] focus -[*] mousedown -[] submit -[] change -[] mouseout -[] keydown ---- -"; - - var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); - var firstQuestion = quiz.Questions.First(); - firstQuestion.Points.Should().Be(1); - firstQuestion.QuestionType.Should().Be(QuestionType.MULTIPLE_ANSWERS); - firstQuestion.Text.Should().Contain("Which events are triggered when the user clicks on an input field?"); - firstQuestion.Answers.First().Text.Should().Be("click"); - firstQuestion.Answers.First().Correct.Should().BeTrue(); - firstQuestion.Answers.ElementAt(3).Correct.Should().BeFalse(); - firstQuestion.Answers.ElementAt(3).Text.Should().Be("submit"); - } - - [Test] - public void CanParseEssay() - { - var rawMarkdownQuiz = @" -Name: Test Quiz -ShuffleAnswers: true -OneQuestionAtATime: false -DueAt: 2023-08-21T23:59:00 -LockAt: 2023-08-21T23:59:00 -AssignmentGroup: Assignments -AllowedAttempts: -1 -Description: this is the -multi line -description ---- -Which events are triggered when the user clicks on an input field? -essay -"; - - var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); - var firstQuestion = quiz.Questions.First(); - firstQuestion.Points.Should().Be(1); - firstQuestion.QuestionType.Should().Be(QuestionType.ESSAY); - firstQuestion.Text.Should().NotContain("essay"); - } - - [Test] - public void CanParseShortAnswer() - { - var rawMarkdownQuiz = @" -Name: Test Quiz -ShuffleAnswers: true -OneQuestionAtATime: false -DueAt: 2023-08-21T23:59:00 -LockAt: 2023-08-21T23:59:00 -AssignmentGroup: Assignments -AllowedAttempts: -1 -Description: this is the -multi line -description ---- -Which events are triggered when the user clicks on an input field? -short answer -"; - - var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); - var firstQuestion = quiz.Questions.First(); - firstQuestion.Points.Should().Be(1); - firstQuestion.QuestionType.Should().Be(QuestionType.SHORT_ANSWER); - firstQuestion.Text.Should().NotContain("short answer"); - } - - [Test] - public void ShortAnswerToMarkdown_IsCorrect() - { - var rawMarkdownQuiz = @" -Name: Test Quiz -ShuffleAnswers: true -OneQuestionAtATime: false -DueAt: 2023-08-21T23:59:00 -LockAt: 2023-08-21T23:59:00 -AssignmentGroup: Assignments -AllowedAttempts: -1 -Description: this is the -multi line -description ---- -Which events are triggered when the user clicks on an input field? -short answer -"; - - var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); - var firstQuestion = quiz.Questions.First(); - - var questionMarkdown = firstQuestion.ToMarkdown(); - var expectedMarkdown = @"Points: 1 -Which events are triggered when the user clicks on an input field? -short_answer"; - questionMarkdown.Should().Contain(expectedMarkdown); - } - [Test] - public void EssayQuestionToMarkdown_IsCorrect() - { - var rawMarkdownQuiz = @" -Name: Test Quiz -ShuffleAnswers: true -OneQuestionAtATime: false -DueAt: 2023-08-21T23:59:00 -LockAt: 2023-08-21T23:59:00 -AssignmentGroup: Assignments -AllowedAttempts: -1 -Description: this is the -multi line -description ---- -Which events are triggered when the user clicks on an input field? -essay -"; - - var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); - var firstQuestion = quiz.Questions.First(); - - var questionMarkdown = firstQuestion.ToMarkdown(); - var expectedMarkdown = @"Points: 1 -Which events are triggered when the user clicks on an input field? -essay"; - questionMarkdown.Should().Contain(expectedMarkdown); - } - - [Test] - public void CanParseMatchingQuestion() - { - var rawMarkdownQuiz = @" -Name: Test Quiz -ShuffleAnswers: true -OneQuestionAtATime: false -DueAt: 2023-08-21T23:59:00 -LockAt: 2023-08-21T23:59:00 -AssignmentGroup: Assignments -AllowedAttempts: -1 -Description: ---- -Match the following terms & definitions - -^ statement - a single command to be executed -^ identifier - name of a variable -^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.) -"; - - var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); - var firstQuestion = quiz.Questions.First(); - firstQuestion.QuestionType.Should().Be(QuestionType.MATCHING); - firstQuestion.Text.Should().NotContain("statement"); - firstQuestion.Answers.First().MatchedText.Should().Be("a single command to be executed"); - } - [Test] - public void CanCreateMarkdownForMatchingQuesiton() - { - var rawMarkdownQuiz = @" -Name: Test Quiz -ShuffleAnswers: true -OneQuestionAtATime: false -DueAt: 2023-08-21T23:59:00 -LockAt: 2023-08-21T23:59:00 -AssignmentGroup: Assignments -AllowedAttempts: -1 -Description: ---- -Match the following terms & definitions - -^ statement - a single command to be executed -^ identifier - name of a variable -^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.) -"; - - var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); - var questionMarkdown = quiz.Questions.First().ToMarkdown(); - var expectedMarkdown = @"Points: 1 -Match the following terms & definitions - -^ statement - a single command to be executed -^ identifier - name of a variable -^ keyword - reserved word that has special meaning in a program (e.g. class, void, static, etc.)"; - questionMarkdown.Should().Contain(expectedMarkdown); - } -} \ No newline at end of file diff --git a/Management.Web/Pages/QuizFormPage.razor b/Management.Web/Pages/QuizFormPage.razor index f127e15..7b0fd3f 100644 --- a/Management.Web/Pages/QuizFormPage.razor +++ b/Management.Web/Pages/QuizFormPage.razor @@ -138,7 +138,12 @@ short_answer --- points: 4 the underscore is optional -short answer"; +short answer +--- +this is a matching question +^ left answer - right dropdown +^ other thing shown - another option +"; }
diff --git a/Management.Web/Shared/Components/Quiz/EditableQuizAnswer.razor b/Management.Web/Shared/Components/Quiz/EditableQuizAnswer.razor deleted file mode 100644 index ee3f7ce..0000000 --- a/Management.Web/Shared/Components/Quiz/EditableQuizAnswer.razor +++ /dev/null @@ -1,88 +0,0 @@ - - -@code { - [Parameter, EditorRequired] - public LocalQuizQuestionAnswer Answer { get; set; } = default!; - [Parameter, EditorRequired] - public int AnswerIndex { get; set; } = default!; - [Parameter, EditorRequired] - public int QuestionIndex { get; set; } = default!; - [Parameter, EditorRequired] - public LocalQuizQuestion Question { get; set; } = default!; - - [Parameter, EditorRequired] - public Action SaveAnswer { get; set; } = (_, _) => {}; - - private string label => "question_" + QuestionIndex + "_answer_" + AnswerIndex; - private string _text { get; set; } = string.Empty; - private string text - { - get => _text; - set - { - _text = value; - SaveAnswer(Answer with { Text = _text }, AnswerIndex); - } - } - - protected override void OnParametersSet() - { - if(_text == string.Empty) - _text = Answer.Text; - base.OnParametersSet(); - } - - private void handleOneAnswerChange() - { - SaveAnswer(Answer with {Correct = !Answer.Correct}, AnswerIndex); - } -} - - -
-
- @if(Question.QuestionType == QuestionType.MULTIPLE_ANSWERS) - { -
- - -
- } - @if(Question.QuestionType == QuestionType.MULTIPLE_CHOICE) - { -
- - -
- } -
-
-
-