got assignment template variables and replacement working

This commit is contained in:
2023-07-27 19:51:36 -06:00
parent b03c699381
commit 7c857a3887
10 changed files with 63 additions and 81 deletions

View File

@@ -11,13 +11,11 @@
@inject CanvasService canvas @inject CanvasService canvas
@inject CoursePlanner planner @inject CoursePlanner planner
@inject ICanvasTokenManagement tokenManagement
@code @code
{ {
private bool showNewFile { get; set; } = false; private bool showNewFile { get; set; } = false;
private bool hasCanvasToken { get; set; } = false;
protected override void OnInitialized() protected override void OnInitialized()
{ {
planner.StateHasChanged += reload; planner.StateHasChanged += reload;
@@ -32,29 +30,10 @@
{ {
planner.StateHasChanged -= reload; planner.StateHasChanged -= reload;
} }
protected override async Task OnAfterRenderAsync(bool firstRender)
{
if(firstRender)
{
hasCanvasToken = await tokenManagement.GetCanvasToken() != null;
StateHasChanged();
}
}
private async Task SetToken(string newToken)
{
await tokenManagement.SaveCanvasToken(newToken);
hasCanvasToken = true;
StateHasChanged();
}
} }
<PageTitle>Index</PageTitle> <PageTitle>Index</PageTitle>
@if(!hasCanvasToken)
{
<ValidateCanvasToken SetToken="SetToken" />
}
@if(planner.LocalCourse == null) @if(planner.LocalCourse == null)
{ {
<CurrentFiles /> <CurrentFiles />

View File

@@ -19,7 +19,6 @@ var builder = WebApplication.CreateBuilder(args);
// Add services to the container. // Add services to the container.
builder.Services.AddRazorPages(); builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor(); builder.Services.AddServerSideBlazor();
builder.Services.AddScoped<ICanvasTokenManagement, BrowserStorageManagement>();
builder.Services.AddScoped<IWebRequestor, WebRequestor>(); builder.Services.AddScoped<IWebRequestor, WebRequestor>();
builder.Services.AddScoped<CanvasService, CanvasService>(); builder.Services.AddScoped<CanvasService, CanvasService>();
builder.Services.AddSingleton<YamlManager>(); builder.Services.AddSingleton<YamlManager>();

View File

@@ -134,7 +134,23 @@
<div class="card-text"> <div class="card-text">
<div class="row"> <div class="row">
<div class="col"> <div class="col">
@if(Assignment.use_template)
{
var template = planner.LocalCourse?.AssignmentTemplates.First((t) => t.Id == Assignment.template_id);
if(template == null)
{
System.Console.WriteLine($"Could not find template fof assignment, {Assignment.template_id}");
}
else
{
var html = AssignmentTemplate.GetHtml(template, Assignment);
@((MarkupString) html)
}
}
else
{
@Assignment.description @Assignment.description
}
</div> </div>
<div class="col-auto"> <div class="col-auto">

View File

@@ -9,6 +9,8 @@
public bool UseTemplate { get; set; } public bool UseTemplate { get; set; }
[Parameter] [Parameter]
public string? TemplateId { get; set; } public string? TemplateId { get; set; }
[Parameter]
public Dictionary<string, string> VariableValues { get; set; } = new Dictionary<string, string>();
[Parameter] [Parameter]
public EventCallback<string> DescriptionChanged { get; set; } public EventCallback<string> DescriptionChanged { get; set; }
@@ -19,12 +21,14 @@
[Parameter] [Parameter]
public EventCallback<string?> TemplateIdChanged { get; set; } public EventCallback<string?> TemplateIdChanged { get; set; }
private string selectedTemplateId { get; set; } [Parameter]
public EventCallback<Dictionary<string, string>> VariableValuesChanged { get; set; }
private AssignmentTemplate? selectedTemplate => private AssignmentTemplate? selectedTemplate =>
planner planner
.LocalCourse? .LocalCourse?
.AssignmentTemplates .AssignmentTemplates
.FirstOrDefault(t => t.Id == selectedTemplateId); .FirstOrDefault(t => t.Id == TemplateId);
} }
@@ -55,8 +59,14 @@
<div class="col-auto text-center"> <div class="col-auto text-center">
<form @onsubmit:preventDefault="true"> <form @onsubmit:preventDefault="true">
<label for="templateSelect">Templates</label> <label for="templateSelect">Templates</label>
<select id="templateSelect" class="form-select" @bind="selectedTemplateId"> <select
<option></option> id="templateSelect"
class="form-select"
value="@TemplateId"
@onchange="async (e) =>
await TemplateIdChanged.InvokeAsync(e.Value?.ToString())"
>
<option value=""></option>
@foreach (var template in planner.LocalCourse.AssignmentTemplates) @foreach (var template in planner.LocalCourse.AssignmentTemplates)
{ {
<option value="@template.Id">@template.Name</option> <option value="@template.Id">@template.Name</option>
@@ -79,6 +89,16 @@
</label> </label>
<input <input
class="form-control" class="form-control"
value="@VariableValues.GetValueOrDefault(variable, String.Empty)"
@oninput="async (e) =>
{
var newValue = e.Value?.ToString() ?? String.Empty;
var newDictionary = new Dictionary<string, string>(VariableValues);
newDictionary[variable] = newValue;
await VariableValuesChanged.InvokeAsync(newDictionary);
}
"
/> />
</div> </div>
} }

View File

@@ -55,6 +55,7 @@
.Where(r => !r.Label.Contains(RubricItem.extraCredit)) .Where(r => !r.Label.Contains(RubricItem.extraCredit))
.Select(s => s.Points) .Select(s => s.Points)
.Sum(); .Sum();
var newAssignment = Assignment with var newAssignment = Assignment with
{ {
name=name, name=name,
@@ -117,6 +118,7 @@
@bind-Description="description" @bind-Description="description"
@bind-UseTemplate="useTemplate" @bind-UseTemplate="useTemplate"
@bind-TemplateId="templateId" @bind-TemplateId="templateId"
@bind-VariableValues="templateVariables"
/> />
</div> </div>

View File

@@ -1,28 +0,0 @@
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
public class BrowserStorageManagement : ICanvasTokenManagement
{
// private string moduleStorageKey = "module storage key";
// private string assignmentStorageKey = "assignment storage key";
private string canvasKey = "canvas key";
private ProtectedLocalStorage storage { get; }
public BrowserStorageManagement(ProtectedLocalStorage BrowserStorage)
{
storage = BrowserStorage;
}
public async Task<string?> GetCanvasToken()
{
var result = await storage.GetAsync<string>(canvasKey);
if (!result.Success)
return null;
return result.Value;
}
public async Task SaveCanvasToken(string token)
{
await storage.SetAsync(canvasKey, token);
}
}

View File

@@ -7,6 +7,7 @@
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Markdig" Version="0.31.0" />
<PackageReference Include="RestSharp" Version="108.0.3" /> <PackageReference Include="RestSharp" Version="108.0.3" />
<PackageReference Include="YamlDotNet" Version="13.1.1" /> <PackageReference Include="YamlDotNet" Version="13.1.1" />
</ItemGroup> </ItemGroup>

View File

@@ -15,4 +15,16 @@ public record AssignmentTemplate
return matches.Select(match => match.Groups[1].Value); return matches.Select(match => match.Groups[1].Value);
} }
public static string GetHtml(AssignmentTemplate template, LocalAssignment assignment)
{
var html = Markdig.Markdown.ToHtml(template.Markdown);
foreach (KeyValuePair<string, string> entry in assignment.template_variables)
{
html = html.Replace($"%7B%7B{entry.Key}%7D%7D", entry.Value);
}
return html;
}
} }

View File

@@ -1,5 +0,0 @@
public interface ICanvasTokenManagement
{
Task<string?> GetCanvasToken();
Task SaveCanvasToken(string token);
}

View File

@@ -3,46 +3,33 @@ using RestSharp;
public class WebRequestor : IWebRequestor public class WebRequestor : IWebRequestor
{ {
private const string BaseUrl = "https://snow.instructure.com/api/v1/"; private const string BaseUrl = "https://snow.instructure.com/api/v1/";
private bool tokenSet = false; private string token;
private RestClient client; private RestClient client;
private ICanvasTokenManagement tokenManagement { get; } public WebRequestor()
public WebRequestor(ICanvasTokenManagement tokenManagement)
{ {
token =
Environment.GetEnvironmentVariable("CANVAS_TOKEN")
?? throw new Exception("CANVAS_TOKEN not in environment");
client = new RestClient(BaseUrl); client = new RestClient(BaseUrl);
this.tokenManagement = tokenManagement; client.AddDefaultHeader("Authorization", $"Bearer {token}");
} }
private async Task EnsureCanvasTokenSet()
{
if (tokenSet)
return;
var newToken = await tokenManagement.GetCanvasToken();
if(newToken == null)
throw new Exception("cannot request canvas, no token in storage");
client.AddDefaultHeader("Authorization", $"Bearer {newToken}");
tokenSet = true;
}
public async Task<(T[]?, RestResponse)> GetManyAsync<T>(RestRequest request) public async Task<(T[]?, RestResponse)> GetManyAsync<T>(RestRequest request)
{ {
await EnsureCanvasTokenSet();
var response = await client.ExecuteGetAsync(request); var response = await client.ExecuteGetAsync(request);
return (Deserialize<T[]>(response), response); return (Deserialize<T[]>(response), response);
} }
public async Task<(T?, RestResponse)> GetAsync<T>(RestRequest request) public async Task<(T?, RestResponse)> GetAsync<T>(RestRequest request)
{ {
await EnsureCanvasTokenSet();
var response = await client.ExecuteGetAsync(request); var response = await client.ExecuteGetAsync(request);
return (Deserialize<T>(response), response); return (Deserialize<T>(response), response);
} }
public async Task<RestResponse> PostAsync(RestRequest request) public async Task<RestResponse> PostAsync(RestRequest request)
{ {
await EnsureCanvasTokenSet();
var response = await client.ExecutePostAsync(request); var response = await client.ExecutePostAsync(request);
if (!response.IsSuccessful) if (!response.IsSuccessful)
{ {
@@ -56,7 +43,6 @@ public class WebRequestor : IWebRequestor
public async Task<(T?, RestResponse)> PostAsync<T>(RestRequest request) public async Task<(T?, RestResponse)> PostAsync<T>(RestRequest request)
{ {
await EnsureCanvasTokenSet();
var response = await client.ExecutePostAsync(request); var response = await client.ExecutePostAsync(request);
return (Deserialize<T>(response), response); return (Deserialize<T>(response), response);
} }