mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
have basic quiz editor implemented
This commit is contained in:
@@ -35,7 +35,7 @@ this is my description in markdown
|
|||||||
markdown.Should().Contain("LockAtDueDate: true");
|
markdown.Should().Contain("LockAtDueDate: true");
|
||||||
markdown.Should().Contain("ShuffleAnswers: true");
|
markdown.Should().Contain("ShuffleAnswers: true");
|
||||||
markdown.Should().Contain("OneQuestionAtATime: false");
|
markdown.Should().Contain("OneQuestionAtATime: false");
|
||||||
markdown.Should().Contain("LocalAssignmentGroupName: someId");
|
markdown.Should().Contain("AssignmentGroup: someId");
|
||||||
markdown.Should().Contain("AllowedAttempts: -1");
|
markdown.Should().Contain("AllowedAttempts: -1");
|
||||||
}
|
}
|
||||||
[Test]
|
[Test]
|
||||||
@@ -70,8 +70,8 @@ lines
|
|||||||
QuestionType = QuestionType.MULTIPLE_CHOICE,
|
QuestionType = QuestionType.MULTIPLE_CHOICE,
|
||||||
Answers = new LocalQuizQuestionAnswer[]
|
Answers = new LocalQuizQuestionAnswer[]
|
||||||
{
|
{
|
||||||
new LocalQuizQuestionAnswer() { Id = "asdfa", Correct = true, Text = "true" },
|
new LocalQuizQuestionAnswer() { Correct = true, Text = "true" },
|
||||||
new LocalQuizQuestionAnswer() { Id = "wef", Correct = false, Text = "false" + Environment.NewLine +Environment.NewLine + "endline" },
|
new LocalQuizQuestionAnswer() { Correct = false, Text = "false" + Environment.NewLine +Environment.NewLine + "endline" },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -120,9 +120,9 @@ b) false
|
|||||||
QuestionType = QuestionType.MULTIPLE_ANSWERS,
|
QuestionType = QuestionType.MULTIPLE_ANSWERS,
|
||||||
Answers = new LocalQuizQuestionAnswer[]
|
Answers = new LocalQuizQuestionAnswer[]
|
||||||
{
|
{
|
||||||
new LocalQuizQuestionAnswer() { Id = "asdfsa", Correct = true, Text = "true" },
|
new LocalQuizQuestionAnswer() { Correct = true, Text = "true" },
|
||||||
new LocalQuizQuestionAnswer() { Id = "wsef", Correct = true, Text = "false"},
|
new LocalQuizQuestionAnswer() { Correct = true, Text = "false"},
|
||||||
new LocalQuizQuestionAnswer() { Id = "ws5ef", Correct = false, Text = "neither"},
|
new LocalQuizQuestionAnswer() { Correct = false, Text = "neither"},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,45 @@
|
|||||||
|
@using Markdig
|
||||||
|
|
||||||
|
@code {
|
||||||
|
[Parameter, EditorRequired]
|
||||||
|
public LocalQuizQuestion Question { get; set; } = default!;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@((MarkupString)Question.HtmlText)
|
||||||
|
|
||||||
|
@foreach(var answer in Question.Answers)
|
||||||
|
{
|
||||||
|
|
||||||
|
<div class="mx-3 mb-1 bg-dark px-2 rounded rounded-2 d-flex flex-row border">
|
||||||
|
<div>
|
||||||
|
@if(answer.Correct)
|
||||||
|
{
|
||||||
|
<svg
|
||||||
|
style="width: 1em;"
|
||||||
|
class="me-1"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
d="M4 12.6111L8.92308 17.5L20 6.5"
|
||||||
|
stroke="var(--bs-success)"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div
|
||||||
|
class="me-1"
|
||||||
|
style="width: 1em;"
|
||||||
|
></div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
@((MarkupString)answer.HtmlText)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
@@ -0,0 +1,121 @@
|
|||||||
|
@using Management.Web.Shared.Components
|
||||||
|
|
||||||
|
@inject QuizEditorContext quizContext
|
||||||
|
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private Modal? modal { get; set; }
|
||||||
|
|
||||||
|
private LocalQuiz testQuiz;
|
||||||
|
|
||||||
|
private string? error { get; set; } = null;
|
||||||
|
private string _quizMarkdownInput { get; set; } = "";
|
||||||
|
private string quizMarkdownInput
|
||||||
|
{
|
||||||
|
get => _quizMarkdownInput;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_quizMarkdownInput = value;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var newQuiz = LocalQuiz.ParseMarkdown(_quizMarkdownInput);
|
||||||
|
error = null;
|
||||||
|
testQuiz = newQuiz;
|
||||||
|
}
|
||||||
|
catch(Exception e)
|
||||||
|
{
|
||||||
|
error = e.Message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
quizContext.StateHasChanged += reload;
|
||||||
|
}
|
||||||
|
private void reload()
|
||||||
|
{
|
||||||
|
if (quizContext.Quiz != null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("reloading quiz editor");
|
||||||
|
|
||||||
|
if(quizMarkdownInput == "")
|
||||||
|
{
|
||||||
|
quizMarkdownInput = quizContext.Quiz.ToMarkdown();
|
||||||
|
}
|
||||||
|
modal?.Show();
|
||||||
|
this.InvokeAsync(this.StateHasChanged);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
quizContext.StateHasChanged -= reload;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void deleteQuiz()
|
||||||
|
{
|
||||||
|
quizContext.DeleteQuiz();
|
||||||
|
modal?.Hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task addToCanvas()
|
||||||
|
{
|
||||||
|
await quizContext.AddQuizToCanvas();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void onHide()
|
||||||
|
{
|
||||||
|
quizMarkdownInput = "";
|
||||||
|
quizContext.Quiz = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
<Modal @ref="modal" OnHide="onHide" >
|
||||||
|
<Title>
|
||||||
|
<div class="row justify-content-between">
|
||||||
|
<div class="col-auto">
|
||||||
|
@quizContext.Quiz?.Name
|
||||||
|
</div>
|
||||||
|
<div class="col-auto me-3">
|
||||||
|
Points: @quizContext.Quiz?.Questions.Sum(q => q.Points)
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Title>
|
||||||
|
<Body>
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-6">
|
||||||
|
<textarea
|
||||||
|
rows="30"
|
||||||
|
class="form-control"
|
||||||
|
@bind="quizMarkdownInput"
|
||||||
|
@bind:event="oninput"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="col-6">
|
||||||
|
@if(error != null)
|
||||||
|
{
|
||||||
|
<p class="text-danger">@error</p>
|
||||||
|
}
|
||||||
|
<QuizPreview Quiz="testQuiz" />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Body>
|
||||||
|
<Footer>
|
||||||
|
<ConfirmationModal Label="Delete" Class="btn btn-danger" OnConfirm="deleteQuiz" />
|
||||||
|
|
||||||
|
|
||||||
|
<button
|
||||||
|
class="btn btn-outline-secondary"
|
||||||
|
@onclick="addToCanvas"
|
||||||
|
>
|
||||||
|
Add to Canvas
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
class="btn btn-primary"
|
||||||
|
@onclick="() => modal?.Hide()"
|
||||||
|
>
|
||||||
|
Done
|
||||||
|
</button>
|
||||||
|
</Footer>
|
||||||
|
</Modal>
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
@using Management.Web.Shared.Components
|
||||||
|
|
||||||
|
@inject QuizEditorContext quizContext
|
||||||
|
|
||||||
|
|
||||||
|
@code {
|
||||||
|
|
||||||
|
[Parameter, EditorRequired]
|
||||||
|
public LocalQuiz Quiz { get; set; } = default!;
|
||||||
|
protected override void OnInitialized()
|
||||||
|
{
|
||||||
|
quizContext.StateHasChanged += reload;
|
||||||
|
}
|
||||||
|
private void reload()
|
||||||
|
{
|
||||||
|
Console.WriteLine(JsonSerializer.Serialize(quizContext.Quiz));
|
||||||
|
this.InvokeAsync(this.StateHasChanged);
|
||||||
|
}
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
quizContext.StateHasChanged -= reload;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@if(Quiz != null)
|
||||||
|
{
|
||||||
|
<div>Name: @Quiz.Name</div>
|
||||||
|
<div>Due At: @Quiz.DueAt</div>
|
||||||
|
<div>Lock At: @Quiz.LockAt</div>
|
||||||
|
<div>Shuffle Answers: @Quiz.ShuffleAnswers</div>
|
||||||
|
<div>Allowed Attempts: @Quiz.AllowedAttempts</div>
|
||||||
|
<div>One question at a time: @Quiz.OneQuestionAtATime</div>
|
||||||
|
<div>Assignment Group: @Quiz.LocalAssignmentGroupName</div>
|
||||||
|
|
||||||
|
<div class="p-3">@Quiz.Description</div>
|
||||||
|
|
||||||
|
@foreach(var question in Quiz.Questions)
|
||||||
|
{
|
||||||
|
|
||||||
|
<div class="bg-dark-subtle mt-1 p-1 rounded rounded-2">
|
||||||
|
<MarkdownQuestionPreview
|
||||||
|
Question="question"
|
||||||
|
@key="question"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
@using Management.Web.Shared.Semester
|
@using Management.Web.Shared.Semester
|
||||||
@using Management.Web.Shared.Components.AssignmentForm
|
@using Management.Web.Shared.Components.AssignmentForm
|
||||||
@using Management.Web.Shared.Components.Quiz
|
@using Management.Web.Shared.Components.Quiz
|
||||||
|
@using Management.Web.Shared.Components.Quiz.Markdown
|
||||||
|
|
||||||
@inject CanvasService canvas
|
@inject CanvasService canvas
|
||||||
@inject CoursePlanner planner
|
@inject CoursePlanner planner
|
||||||
@@ -40,7 +41,8 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
<QuizForm />
|
<MarkdownQuizForm />
|
||||||
|
@* <QuizForm /> *@
|
||||||
|
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col overflow-y-auto border rounded " style="max-height: 95vh;">
|
<div class="col overflow-y-auto border rounded " style="max-height: 95vh;">
|
||||||
|
|||||||
@@ -49,7 +49,7 @@ LockAt: {LockAt}
|
|||||||
DueAt: {DueAt}
|
DueAt: {DueAt}
|
||||||
ShuffleAnswers: {ShuffleAnswers.ToString().ToLower()}
|
ShuffleAnswers: {ShuffleAnswers.ToString().ToLower()}
|
||||||
OneQuestionAtATime: {OneQuestionAtATime.ToString().ToLower()}
|
OneQuestionAtATime: {OneQuestionAtATime.ToString().ToLower()}
|
||||||
LocalAssignmentGroupName: {LocalAssignmentGroupName}
|
AssignmentGroup: {LocalAssignmentGroupName}
|
||||||
AllowedAttempts: {AllowedAttempts}
|
AllowedAttempts: {AllowedAttempts}
|
||||||
Description: {Description}
|
Description: {Description}
|
||||||
---
|
---
|
||||||
@@ -84,8 +84,8 @@ Description: {Description}
|
|||||||
var dueAt = DateTime.Parse(extractLabelValue(settings, "DueAt"));
|
var dueAt = DateTime.Parse(extractLabelValue(settings, "DueAt"));
|
||||||
var lockAt = DateTime.Parse(extractLabelValue(settings, "LockAt"));
|
var lockAt = DateTime.Parse(extractLabelValue(settings, "LockAt"));
|
||||||
var description = extractDescription(settings);
|
var description = extractDescription(settings);
|
||||||
|
var assignmentGroup = extractLabelValue(settings, "AssignmentGroup");
|
||||||
|
|
||||||
// var assignmentGroup = ExtractLabelValue(settings, "AssignmentGroup");
|
|
||||||
return new LocalQuiz()
|
return new LocalQuiz()
|
||||||
{
|
{
|
||||||
Id = "id-" + name,
|
Id = "id-" + name,
|
||||||
@@ -96,7 +96,7 @@ Description: {Description}
|
|||||||
DueAt = dueAt,
|
DueAt = dueAt,
|
||||||
ShuffleAnswers = shuffleAnswers,
|
ShuffleAnswers = shuffleAnswers,
|
||||||
OneQuestionAtATime = oneQuestionAtATime,
|
OneQuestionAtATime = oneQuestionAtATime,
|
||||||
// LocalAssignmentGroupId = "someId",
|
LocalAssignmentGroupName = assignmentGroup,
|
||||||
AllowedAttempts = allowedAttempts,
|
AllowedAttempts = allowedAttempts,
|
||||||
Questions = new LocalQuizQuestion[] { }
|
Questions = new LocalQuizQuestion[] { }
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ public record LocalQuizQuestionAnswer
|
|||||||
public static LocalQuizQuestionAnswer ParseMarkdown(string input)
|
public static LocalQuizQuestionAnswer ParseMarkdown(string input)
|
||||||
{
|
{
|
||||||
var isCorrect = input[0] == '*' || input[1] == '*';
|
var isCorrect = input[0] == '*' || input[1] == '*';
|
||||||
string startingQuestionPattern = @"^(?:\*[a-z]\))|\[\s*\]|\[\*\] ";
|
string startingQuestionPattern = @"^(\*?[a-z]\))|\[\s*\]|\[\*\] ";
|
||||||
var text = Regex.Replace(input, startingQuestionPattern, string.Empty).Trim();
|
var text = Regex.Replace(input, startingQuestionPattern, string.Empty).Trim();
|
||||||
|
|
||||||
return new LocalQuizQuestionAnswer()
|
return new LocalQuizQuestionAnswer()
|
||||||
@@ -22,5 +22,4 @@ public record LocalQuizQuestionAnswer
|
|||||||
Text=text,
|
Text=text,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user