added quiz support, improved assignment description and rubric support

This commit is contained in:
2023-08-18 13:27:12 -06:00
parent a962ef74f9
commit a57a687f3c
10 changed files with 154 additions and 69 deletions

View File

@@ -14,7 +14,8 @@
{
if (assignmentContext.Assignment != null)
{
Description = assignmentContext.Assignment.Description;
description = assignmentContext.Assignment.Description;
descriptionForPreview = description;
TemplateId = assignmentContext.Assignment.TemplateId;
UseTemplate = TemplateId != null && TemplateId != "";
VariableValues = assignmentContext.Assignment.TemplateVariables;
@@ -27,25 +28,7 @@
}
private string description { get; set; } = default!;
public string Description
{
get => description;
set
{
description = value;
if (description != string.Empty)
{
if(assignmentContext.Assignment != null)
{
var newAssignment = assignmentContext.Assignment with
{
Description = description
};
assignmentContext.SaveAssignment(newAssignment);
}
}
}
}
private string descriptionForPreview { get; set; } = default!;
public bool? UseTemplate { get; set; } = null;
public string? TemplateId { get; set; }
@@ -58,9 +41,21 @@
.AssignmentTemplates
.FirstOrDefault(t => t.Id == TemplateId);
private void saveDescription(ChangeEventArgs e)
private void handleNewDescription(ChangeEventArgs e)
{
var newDescription = e.Value?.ToString();
if (newDescription != string.Empty)
{
descriptionForPreview = newDescription;
if (assignmentContext.Assignment != null)
{
var newAssignment = assignmentContext.Assignment with
{
Description = newDescription
};
assignmentContext.SaveAssignment(newAssignment);
}
}
}
private void saveTemplateId(ChangeEventArgs e)
@@ -70,13 +65,13 @@
var newTemplateId = e.Value?.ToString();
var newAssignment = assignmentContext.Assignment with
{
Description = e.Value?.ToString() ?? ""
TemplateId = newTemplateId
};
assignmentContext.SaveAssignment(newAssignment);
}
}
private MarkupString preview { get => (MarkupString) Markdown.ToHtml(Description); }
private MarkupString preview { get => (MarkupString)Markdown.ToHtml(descriptionForPreview); }
}
@@ -167,11 +162,11 @@
id="description"
class="form-control"
rows=12
@bind="Description"
@bind:event="oninput"
@bind="description"
@oninput="handleNewDescription"
/>
</div>
<div class="col" @key="Description">
<div class="col" @key="descriptionForPreview">
@(preview)
</div>
</div>

View File

@@ -12,6 +12,20 @@
public Action MoveUp { get; set; } = default!;
[Parameter, EditorRequired]
public Action MoveDown { get; set; } = default!;
private int points { get; set; }
private string label { get; set; }
private string lastRubricItemId { get; set; }
protected override void OnParametersSet()
{
if(RubricItem.Id != lastRubricItemId)
{
lastRubricItemId = RubricItem.Id;
points = RubricItem.Points;
label = RubricItem.Label;
}
}
private void editItem(string label, int points)
{
var newRubricItem = RubricItem with
@@ -39,7 +53,7 @@
id="rubricLabel"
name="rubricLabel"
@oninput="@(e => editItem(e.Value?.ToString() ?? "", RubricItem.Points))"
value="@RubricItem.Label"
@bind="label"
/>
</div>
<div class="col">
@@ -57,7 +71,7 @@
RubricItem.Label,
int.Parse(e.Value?.ToString() != "" ? e.Value?.ToString() ?? "0" : "0"))
)"
value="@RubricItem.Points"
@bind="points"
type="number"
/>
</div>

View File

@@ -137,6 +137,7 @@
@oninput="handleNewDescription"
/>
</div>
<QuizSettings />
@foreach(var question in quizContext.Quiz.Questions)
{

View File

@@ -42,7 +42,9 @@
{
if (quizContext.Quiz != null)
{
var newPoints = int.Parse(e.Value?.ToString() ?? "0");
var pointsString = e.Value?.ToString() ?? "0";
var newPoints = int.Parse(pointsString == string.Empty ? "0" : pointsString);
var newQuestion = Question with
{
Points = newPoints
@@ -123,28 +125,20 @@
/>
</div>
<div class="col-auto">
</div>
</div>
@foreach (var typeOption in QuestionType.AllTypes)
{
<div class="form-check">
<input
class="form-check-input"
type="radio"
name="@(Question.Id + "question_type")"
id="@(Question.Id + typeOption)"
value="@typeOption"
checked="@(typeOption == questionType)"
@onchange="() => handleTypeUpdate(typeOption)"
>
<label
class="form-check-label"
for="@(Question.Id + typeOption)"
>
<input class="form-check-input" type="radio" name="@(Question.Id + "question_type")"
id="@(Question.Id + typeOption)" value="@typeOption" checked="@(typeOption == questionType)"
@onchange="() => handleTypeUpdate(typeOption)">
<label class="form-check-label" for="@(Question.Id + typeOption)">
@typeOption
</label>
</div>
}
</div>
</div>
<div class="mb-3 row">
<div class="col-auto">
<label
@@ -166,15 +160,6 @@
@if(questionType == QuestionType.MULTIPLE_ANSWERS || questionType == QuestionType.MULTIPLE_CHOICE)
{
@foreach(var answer in answers)
{
<EditableQuizAnswer
Answer="answer"
SaveAnswer="saveAnswer"
Question="Question"
/>
}
<button
class="btn btn-outline-danger"
@onclick="removeAnswer"
@@ -188,6 +173,15 @@
+ Add Answer
</button>
@foreach(var answer in answers)
{
<EditableQuizAnswer
Answer="answer"
SaveAnswer="saveAnswer"
Question="Question"
/>
}
}
<div>

View File

@@ -0,0 +1,74 @@
@using Management.Web.Shared.Components
@inject QuizEditorContext quizContext
@code {
protected override void OnInitialized()
{
quizContext.StateHasChanged += reload;
}
private void reload()
{
if (quizContext.Quiz != null)
{
if (!lockAtDueDate)
lockAtDueDate = quizContext.Quiz.LockAtDueDate;
if (lockAt == null)
lockAt = quizContext.Quiz.LockAt;
if (!shuffleAnswers)
shuffleAnswers = quizContext.Quiz.ShuffleAnswers;
if (!oneQuestionAtATime)
oneQuestionAtATime = quizContext.Quiz.OneQuestionAtATime;
if (allowedAttempts == 0)
allowedAttempts = quizContext.Quiz.AllowedAttempts;
this.InvokeAsync(this.StateHasChanged);
}
}
public void Dispose()
{
quizContext.StateHasChanged -= reload;
}
private bool lockAtDueDate { get; set; }
private DateTime? lockAt { get; set; }
private bool shuffleAnswers { get; set; }
private bool oneQuestionAtATime { get; set; }
private int allowedAttempts { get; set; }
private void handleLockAtDueDateChange()
{
if(quizContext.Quiz != null)
{
var newValue = !quizContext.Quiz.LockAtDueDate;
var newQuiz = newValue
? quizContext.Quiz with
{
LockAtDueDate = newValue,
LockAt = quizContext.Quiz.DueAt
}
: quizContext.Quiz with
{
LockAtDueDate = newValue
};
quizContext.SaveQuiz(newQuiz);
}
}
}
<div>
<div class="form-check form-switch">
<input
class="form-check-input"
type="checkbox"
role="switch"
id="lockAtDueDate"
checked="lockAtDueDate"
@onchange="handleLockAtDueDateChange">
<label
class="form-check-label" for="lockAtDueDate">
Lock at Due Date
</label>
</div>
</div>

View File

@@ -16,7 +16,8 @@
{
var newModule = new LocalModule
{
Name=Name
Name=Name,
Id=Guid.NewGuid().ToString()
};

View File

@@ -12,10 +12,9 @@ public record LocalQuiz
public bool ShuffleAnswers { get; init; }
public bool OneQuestionAtATime { get; init; }
public int AllowedAttempts { get; init; } = -1; // -1 is infinite
public bool ShowCorrectAnswers { get; init; }
public int? TimeLimit { get; init; } = null;
public string? HideResults { get; init; } = null;
// public bool ShowCorrectAnswers { get; init; }
// public int? TimeLimit { get; init; } = null;
// public string? HideResults { get; init; } = null;
// If null, students can see their results after any attempt.
// If “always”, students can never see their results.
// If “until_after_last_attempt”, students can only see results after their last attempt. (Only valid if allowed_attempts > 1). Defaults to null.

View File

@@ -40,9 +40,9 @@ public class CanvasQuizService
title = localQuiz.Name,
description = localQuiz.Description,
// assignment_group_id = "quiz", TODO: support specific assignment groups
time_limit = localQuiz.TimeLimit,
// time_limit = localQuiz.TimeLimit,
shuffle_answers = localQuiz.ShuffleAnswers,
hide_results = localQuiz.HideResults,
// hide_results = localQuiz.HideResults,
allowed_attempts = localQuiz.AllowedAttempts,
one_question_at_a_time = true,
cant_go_back = false,

View File

@@ -74,7 +74,14 @@ public class CanvasService
Console.WriteLine($"Creating Module: {name}");
var url = $"courses/{courseId}/modules";
var request = new RestRequest(url);
request.AddParameter("module[name]", name);
var body = new
{
module = new
{
name = name
}
};
request.AddBody(body);
var (newModule, _) = await webRequestor.PostAsync<CanvasModule>(request);
return newModule ?? throw new Exception($"failed to create new canvas module {name}");

View File

@@ -14,7 +14,7 @@ public class YamlManager
public LocalCourse ParseCourse(string rawCourse)
{
var deserializer = new DeserializerBuilder().Build();
var deserializer = new DeserializerBuilder().IgnoreUnmatchedProperties().Build();
var course = deserializer.Deserialize<LocalCourse>(rawCourse);
return course;