can parse at least some markdown quiz questions

This commit is contained in:
alex
2023-10-03 17:21:46 -06:00
parent 9ca475dc44
commit a60151575c
4 changed files with 138 additions and 2 deletions

View File

@@ -161,6 +161,7 @@ AllowedAttempts: -1
Description: this is the Description: this is the
multi line multi line
description description
---
"; ";
var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz); var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz);
@@ -173,4 +174,47 @@ description
multi line multi line
description"); description");
} }
[Test]
public void TestCanParseQuizWithQuestions()
{
var rawMarkdownQuiz = @"
Name: Test Quiz
LockAtDueDate: true
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
---
Points: 2
`some type` of question
with many
```
lines
```
*a) true
b) false
endline
---
";
var quiz = LocalQuiz.ParseMarkdown(rawMarkdownQuiz);
var firstQuestion = quiz.Questions.First();
firstQuestion.Points.Should().Be(2);
firstQuestion.Text.Should().Contain("```");
firstQuestion.Text.Should().Contain("`some type` of question");
firstQuestion.Answers.First().Text.Should().Be("true");
firstQuestion.Answers.First().Correct.Should().BeTrue();
firstQuestion.Answers.ElementAt(1).Correct.Should().BeFalse();
firstQuestion.Answers.ElementAt(1).Text.Should().Contain("endline");
}
} }

View File

@@ -61,9 +61,22 @@ Description: {Description}
public static LocalQuiz ParseMarkdown(string input) public static LocalQuiz ParseMarkdown(string input)
{ {
var splitInput = input.Split(Environment.NewLine + Environment.NewLine); var splitInput = input.Split("---" + Environment.NewLine);
var settings = splitInput[0]; var settings = splitInput[0];
var quizWithoutQuestions = getQuizWithOnlySettings(settings);
var questions = splitInput[1..]
.Where(str => !string.IsNullOrWhiteSpace(str))
.Select(q => LocalQuizQuestion.ParseMarkdown(q))
.ToArray();
return quizWithoutQuestions with
{
Questions = questions
};
}
private static LocalQuiz getQuizWithOnlySettings(string settings)
{
var name = extractLabelValue(settings, "Name"); var name = extractLabelValue(settings, "Name");
var lockAtDueDate = bool.Parse(extractLabelValue(settings, "LockAtDueDate")); var lockAtDueDate = bool.Parse(extractLabelValue(settings, "LockAtDueDate"));
var shuffleAnswers = bool.Parse(extractLabelValue(settings, "ShuffleAnswers")); var shuffleAnswers = bool.Parse(extractLabelValue(settings, "ShuffleAnswers"));
@@ -89,6 +102,7 @@ Description: {Description}
Questions = new LocalQuizQuestion[] { } Questions = new LocalQuizQuestion[] { }
}; };
} }
static string extractLabelValue(string input, string label) static string extractLabelValue(string input, string label)
{ {
string pattern = $@"{label}: (.*?)\n"; string pattern = $@"{label}: (.*?)\n";

View File

@@ -1,3 +1,5 @@
using System.Text.RegularExpressions;
namespace LocalModels; namespace LocalModels;
public record LocalQuizQuestion public record LocalQuizQuestion
@@ -21,7 +23,7 @@ public record LocalQuizQuestion
var questionTypeIndicator = isMultipleChoice var questionTypeIndicator = isMultipleChoice
? $"{correctIndicator}{questionLetter}) " ? $"{correctIndicator}{questionLetter}) "
: $"[{correctIndicator}] "; : $"[{correctIndicator}] ";
var textWithSpecificNewline = answer.Text.Replace(Environment.NewLine, Environment.NewLine + " "); var textWithSpecificNewline = answer.Text.Replace(Environment.NewLine, Environment.NewLine + " ");
@@ -35,6 +37,67 @@ public record LocalQuizQuestion
--- ---
"; ";
} }
private static readonly string[] validFirstAnswerDelimiters = new string[] { "*a)", "a)", "[ ]", "[*]" };
public static LocalQuizQuestion ParseMarkdown(string input)
{
var lines = input.Split(Environment.NewLine);
var firstLineIsPoints = lines.First().Contains("points: ", StringComparison.CurrentCultureIgnoreCase);
int points = firstLineIsPoints ? int.Parse(lines.First().Split(": ")[1]) : 1;
var linesWithoutPoints = firstLineIsPoints ? lines[1..] : lines;
var linesWithoutAnswers = linesWithoutPoints
.TakeWhile(line => !validFirstAnswerDelimiters.Any(prefix => line.TrimStart().StartsWith(prefix)))
.ToArray();
var description = string.Join(Environment.NewLine, linesWithoutAnswers);
LocalQuizQuestionAnswer[] answers = getAnswers(linesWithoutPoints);
return new LocalQuizQuestion()
{
Text = description,
Points = points,
Answers = answers
};
}
private static LocalQuizQuestionAnswer[] getAnswers(string[] linesWithoutPoints)
{
var indexOfAnswerStart = linesWithoutPoints
.ToList()
.FindIndex(
l => validFirstAnswerDelimiters.Any(prefix => l.TrimStart().StartsWith(prefix))
);
var answerLinesRaw = linesWithoutPoints[indexOfAnswerStart..];
var answerStartPattern = @"^(\*?[a-z]\))|\[\s*\]|\[\*\]";
var answerLines = answerLinesRaw.Aggregate(new List<string>(), (acc, line) =>
{
if (!Regex.IsMatch(line, answerStartPattern))
{
if (acc.Count != 0) // Append to the previous line if there is one
{
int lastIndex = acc.Count - 1;
acc[lastIndex] += Environment.NewLine + line;
}
else
{
acc.Add(line);
}
}
else
{
acc.Add(line); // Add as a new line if it matches the pattern
}
return acc;
});
var answers = answerLines.Select(LocalQuizQuestionAnswer.ParseMarkdown).ToArray();
return answers;
}
} }
public static class QuestionType public static class QuestionType

View File

@@ -1,3 +1,5 @@
using System.Text.RegularExpressions;
namespace LocalModels; namespace LocalModels;
public record LocalQuizQuestionAnswer public record LocalQuizQuestionAnswer
@@ -11,4 +13,17 @@ public record LocalQuizQuestionAnswer
public string HtmlText => Markdig.Markdown.ToHtml(Text); public string HtmlText => Markdig.Markdown.ToHtml(Text);
public static LocalQuizQuestionAnswer ParseMarkdown(string input)
{
var isCorrect = input[0] == '*' || input[1] == '*';
string startingQuestionPattern = @"^(?:\*[a-z]\))|\[\s*\]|\[\*\] ";
var text = Regex.Replace(input, startingQuestionPattern, string.Empty).Trim();
return new LocalQuizQuestionAnswer()
{
Correct = isCorrect,
Text=text,
};
}
} }