before I dive too much more into adding pages in canvas

This commit is contained in:
2024-01-12 15:57:35 -07:00
parent 9f19704724
commit bca8497255
17 changed files with 472 additions and 60 deletions

View File

@@ -31,9 +31,6 @@
private string rawText { get; set; } = string.Empty;
private string? error = null;
public bool? UseTemplate { get; set; } = null;
public string? TemplateId { get; set; }
private void handleChange(string newRawAssignment)
{

View File

@@ -155,7 +155,7 @@
<h5>Assignments</h5>
<div class="row">
@foreach(var p in Module.Pages)
@* @foreach(var p in Module.Pages)
{
<PageListItem Page=p />
}
@@ -167,6 +167,15 @@
@foreach (var quiz in Module.Quizzes)
{
<QuizListItem Quiz="quiz" />
} *@
@foreach(var item in Module.SortedModuleItems)
{
@(item switch
{
LocalAssignment assignment => (@<AssignmentListItem Assignment="assignment" Module="Module" />),
LocalQuiz quiz => (@<QuizListItem Quiz="quiz" />),
LocalCoursePage page => (@<PageListItem Page=page />),
})
}
</div>
</div>

View File

@@ -0,0 +1,169 @@
@using Management.Web.Shared.Components
@using CanvasModel.Pages
@inject CoursePlanner planner
@inject CanvasService canvas
@inject NavigationManager Navigation
@inject PageEditorContext pageContext
@code {
protected override void OnInitialized()
{
pageContext.StateHasChanged += reload;
reload();
}
private void reload()
{
if (pageContext.Page != null)
{
name = pageContext.Page.Name;
}
this.InvokeAsync(this.StateHasChanged);
}
public void Dispose()
{
pageContext.StateHasChanged -= reload;
}
private string name { get; set; } = String.Empty;
private bool addingPageToCanvas = false;
private bool deletingPageFromCanvas = false;
private CanvasPage? pageInCanvas =>
planner.CanvasPages?.FirstOrDefault(a => a.Title == pageContext.Page?.Name);
private string canvasPageUrl =>
$"https://snow.instructure.com/courses/{planner.LocalCourse?.Settings.CanvasId}/assignments/{pageInCanvas?.PageId}";
private void submitHandler()
{
if (pageContext.Page != null)
{
var newPage = pageContext.Page with
{
Name = name,
};
pageContext.SavePage(newPage);
}
pageContext.Page = null;
}
private async Task HandleDelete()
{
}
private void handleNameChange(ChangeEventArgs e)
{
if (pageContext.Page != null)
{
var newPage = pageContext.Page with { Name = e.Value?.ToString() ?? "" };
pageContext.SavePage(newPage);
}
}
private async Task addToCanvas()
{
addingPageToCanvas = true;
await pageContext.AddPageToCanvas();
await planner.LoadCanvasData();
addingPageToCanvas = false;
}
private async Task updateInCanvas()
{
if(pageInCanvas != null)
{
addingPageToCanvas = true;
await pageContext.UpdateInCanvas(pageInCanvas.PageId);
await planner.LoadCanvasData();
addingPageToCanvas = false;
}
}
private async Task deleteFromCanvas()
{
if (pageInCanvas == null
|| planner?.LocalCourse?.Settings.CanvasId == null
|| pageContext.Page == null
)
return;
deletingPageFromCanvas = true;
@* await canvas.Pages.Delete(
(ulong)planner.LocalCourse.Settings.CanvasId,
pageInCanvas.Id,
assignmentContext.Assignment.Name
); *@
await planner.LoadCanvasData();
deletingPageFromCanvas = false;
StateHasChanged();
}
}
<div class="d-flex flex-column p-2 h-100 w-100" style="height: 100%;" >
<div>
@pageContext.Page?.Name
</div>
<section class="flex-grow-1 p-1 border rounded-4 bg-dark-subtle" style="min-height: 0;">
@if (pageContext.Page != null)
{
<CoursePageMarkdownEditor />
}
</section>
<div class="d-flex justify-content-end p-3">
@if (addingPageToCanvas || deletingPageFromCanvas)
{
<div>
<Spinner />
</div>
}
<ConfirmationModal Label="Delete" Class="btn btn-danger" OnConfirmAsync="HandleDelete" />
<button
class="btn btn-outline-secondary mx-3"
disabled="@(addingPageToCanvas || deletingPageFromCanvas)"
@onclick="addToCanvas"
>
Add To Canvas
</button>
@if (pageInCanvas != null)
{
<a
class="btn btn-outline-secondary me-1"
href="@canvasPageUrl"
target="_blank"
disabled="@(addingPageToCanvas || deletingPageFromCanvas)"
>
View in Canvas
</a>
<button
class="btn btn-outline-secondary mx-3"
disabled="@(addingPageToCanvas || deletingPageFromCanvas)"
@onclick="updateInCanvas"
>
Update In Canvas
</button>
<ConfirmationModal
Disabled="@(addingPageToCanvas || deletingPageFromCanvas)"
Label="Delete from Canvas"
Class="btn btn-outline-danger mx-3"
OnConfirmAsync="deleteFromCanvas"
/>
}
<button class="btn btn-primary mx-2" @onclick="@(() => {
pageContext.Page = null;
Navigation.NavigateTo("/course/" + planner.LocalCourse?.Settings.Name);
})">
Done
</button>
</div>
</div>

View File

@@ -0,0 +1,71 @@
@page "/course/{CourseName}/page/{PageName}"
@using CanvasModel.EnrollmentTerms
@using CanvasModel.Courses
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
@using LocalModels
@using Management.Web.Pages.Course.Module.ModuleItems
@using Management.Web.Shared.Components
@inject FileStorageManager fileStorageManager
@inject CanvasService canvas
@inject CoursePlanner planner
@inject PageEditorContext pageContext
@inject ILogger<CoursePageFormPage> logger
@code {
[Parameter]
public string? CourseName { get; set; } = default!;
[Parameter]
public string? PageName { get; set; } = default!;
private bool loading { get; set; } = true;
protected override async Task OnInitializedAsync()
{
if (loading)
{
loading = false;
logger.LogInformation($"loading page {CourseName} {PageName}");
if (planner.LocalCourse == null)
{
var courses = await fileStorageManager.LoadSavedCourses();
planner.LocalCourse = courses.First(c => c.Settings.Name == CourseName);
logger.LogInformation($"set course to '{planner.LocalCourse?.Settings.Name}'");
}
if (pageContext.Page == null)
{
var page = planner
.LocalCourse?
.Modules
.SelectMany(m => m.Pages)
.FirstOrDefault(a => a.Name == PageName);
pageContext.Page = page;
logger.LogInformation($"set page to '{pageContext.Page?.Name}'");
}
await planner.LoadCanvasData();
base.OnInitialized();
StateHasChanged();
}
}
}
<PageTitle>@CourseName - @PageName</PageTitle>
<div style="height: 100vh;" class="m-0 p-1 d-flex flex-row">
@if (loading)
{
<Spinner />
}
@if (planner.LocalCourse != null && pageContext.Page != null)
{
<CoursePageForm />
}
</div>

View File

@@ -0,0 +1,79 @@
@using Markdig
@using Management.Web.Shared.Components
@inject CoursePlanner planner
@inject PageEditorContext pageContext
@code {
protected override void OnInitialized()
{
pageContext.StateHasChanged += reload;
reload();
}
private void reload()
{
if (pageContext.Page != null)
{
if(rawText == string.Empty)
{
rawText = pageContext.Page.ToMarkdown();
this.InvokeAsync(this.StateHasChanged);
}
}
}
public void Dispose()
{
pageContext.StateHasChanged -= reload;
}
private string rawText { get; set; } = string.Empty;
private string? error = null;
private MarkupString preview { get => (MarkupString)Markdown.ToHtml(pageContext?.Page?.Text ?? ""); }
private void handleChange(string newRawPage)
{
rawText = newRawPage;
if (newRawPage != string.Empty)
{
try
{
var parsed = LocalCoursePage.ParseMarkdown(newRawPage);
error = null;
pageContext.SavePage(parsed);
}
catch(LocalPageMarkdownParseException e)
{
error = e.Message;
}
finally
{
StateHasChanged();
}
}
StateHasChanged();
}
}
<div class="d-flex w-100 h-100 flex-row">
@if(pageContext.Page != null && planner.LocalCourse != null)
{
<div class="row h-100 w-100">
<div class="col-6">
<MonacoTextArea Value=@rawText OnChange=@handleChange />
</div>
<div class="col-6 overflow-y-auto h-100" >
@if (error != null)
{
<p class="text-danger text-truncate">Error: @error</p>
}
<div>Due At: @pageContext.Page.DueAt</div>
<hr>
<div>
@(preview)
</div>
</div>
</div>
}
</div>

View File

@@ -74,6 +74,7 @@ builder.Services.AddScoped(typeof(MyLogger<>));
builder.Services.AddScoped<IWebRequestor, WebRequestor>();
builder.Services.AddScoped<CanvasServiceUtils>();
builder.Services.AddScoped<CanvasAssignmentService>();
builder.Services.AddScoped<CanvasCoursePageService>();
builder.Services.AddScoped<CanvasAssignmentGroupService>();
builder.Services.AddScoped<CanvasQuizService>();
builder.Services.AddScoped<CanvasModuleService>();

View File

@@ -8,6 +8,7 @@ using Management.Services.Canvas;
using System.Text.RegularExpressions;
using CanvasModel.Quizzes;
using Management.Services;
using CanvasModel.Pages;
namespace Management.Planner;
@@ -109,6 +110,7 @@ public class CoursePlanner
public IEnumerable<CanvasAssignmentGroup>? CanvasAssignmentGroups { get; internal set; }
public IEnumerable<CanvasQuiz>? CanvasQuizzes { get; internal set; }
public IEnumerable<CanvasModule>? CanvasModules { get; internal set; }
public IEnumerable<CanvasPage>? CanvasPages { get; internal set; }
public Dictionary<CanvasModule, IEnumerable<CanvasModuleItem>>? CanvasModulesItems { get; internal set; }
public async Task<(
@@ -131,6 +133,7 @@ public class CoursePlanner
var quizzesTask = canvas.Quizzes.GetAll(canvasId);
var modulesTask = canvas.Modules.GetModules(canvasId);
var assignmentGroupsTask = canvas.AssignmentGroups.GetAll(canvasId);
var coursePagesTask = canvas.Pages.GetAll(canvasId);
CanvasAssignments = await assignmentsTask;
CanvasQuizzes = await quizzesTask;

View File

@@ -82,13 +82,13 @@ public class PageEditorContext(
public async Task AddPageToCanvas()
{
// logger.Log("started to add quiz to canvas");
// if (Quiz == null)
// {
// logger.Log("cannot add null quiz to canvas");
// return;
// }
// await planner.LoadCanvasData();
logger.Log("started to add page to canvas");
if (Page == null)
{
logger.Log("cannot add null page to canvas");
return;
}
await planner.LoadCanvasData();
// if (planner.CanvasQuizzes == null)
// {
// logger.Log("cannot add quiz to canvas, failed to retrieve current quizzes");
@@ -128,6 +128,11 @@ public class PageEditorContext(
// logger.Log($"finished adding quiz {Quiz.Name} to canvas");
}
public async Task UpdateInCanvas(string pageId)
{
}
private static LocalModule getCurrentLocalModule(LocalCoursePage page, LocalCourse course)

View File

@@ -1,6 +1,6 @@
namespace CanvasModel.Pages;
public record PageModel (
public record CanvasPage (
[property: JsonPropertyName("page_id")] string PageId,
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("title")] string Title,

View File

@@ -20,7 +20,6 @@ public record LocalAssignment: IModuleItem
}
}
public string Description { get; init; } = "";
// public bool LockAtDueDate { get; init; }
public DateTime? LockAt { get; init; }
public DateTime DueAt { get; init; }
public string? LocalAssignmentGroupName { get; init; }

View File

@@ -5,6 +5,7 @@ public record LocalCoursePage: IModuleItem
public required string Name { get; init; }
public required string Text { get; set; }
public DateTime DueAt { get; init; }
public string GetBodyHtml() => Markdig.Markdown.ToHtml(Text);
public string ToMarkdown()
{
@@ -34,6 +35,7 @@ public record LocalCoursePage: IModuleItem
Text = text
};
}
}

View File

@@ -7,4 +7,11 @@ public record LocalModule
public IEnumerable<LocalAssignment> Assignments { get; init; } = [];
public IEnumerable<LocalQuiz> Quizzes { get; init; } = [];
public IEnumerable<LocalCoursePage> Pages { get; init; } = [];
public IEnumerable<IModuleItem> SortedModuleItems =>
Enumerable.Empty<IModuleItem>()
.Concat(Assignments)
.Concat(Quizzes)
.Concat(Pages)
.OrderBy(i => i.DueAt);
}

View File

@@ -4,22 +4,15 @@ using RestSharp;
namespace Management.Services.Canvas;
public class CanvasAssignmentService
{
private readonly IWebRequestor webRequestor;
private readonly CanvasServiceUtils utils;
private readonly MyLogger<CanvasAssignmentService> log;
public CanvasAssignmentService(
IWebRequestor webRequestor,
CanvasServiceUtils utils,
MyLogger<CanvasAssignmentService> logger
public class CanvasAssignmentService(
IWebRequestor webRequestor,
CanvasServiceUtils utils,
MyLogger<CanvasAssignmentService> logger
)
{
this.webRequestor = webRequestor;
this.utils = utils;
this.log = logger;
}
{
private readonly IWebRequestor webRequestor = webRequestor;
private readonly CanvasServiceUtils utils = utils;
private readonly MyLogger<CanvasAssignmentService> log = logger;
public async Task<IEnumerable<CanvasAssignment>> GetAll(ulong courseId)
{

View File

@@ -0,0 +1,87 @@
using CanvasModel.Courses;
using CanvasModel.Modules;
using CanvasModel.Pages;
using LocalModels;
using RestSharp;
namespace Management.Services.Canvas;
public class CanvasCoursePageService(
IWebRequestor webRequestor,
CanvasServiceUtils utils,
MyLogger<CanvasCoursePageService> logger
)
{
private readonly IWebRequestor webRequestor = webRequestor;
private readonly CanvasServiceUtils utils = utils;
private readonly MyLogger<CanvasCoursePageService> log = logger;
public async Task<IEnumerable<CanvasPage>> GetAll(ulong courseId)
{
var url = $"courses/{courseId}/pages";
var request = new RestRequest(url);
// request.AddParameter("include[]", "overrides");
var pagesResponse = await utils.PaginatedRequest<IEnumerable<CanvasPage>>(request);
return pagesResponse.SelectMany(
pages => pages
);
}
public async Task<string> Create(
ulong canvasCourseId,
LocalCoursePage localCourse
)
{
log.Log($"creating course page: {localCourse.Name}");
var url = $"courses/{canvasCourseId}/pages";
var request = new RestRequest(url);
var body = new
{
title = localCourse.Name,
body = localCourse.GetBodyHtml()
};
var bodyObj = new { wiki_page = body };
request.AddBody(bodyObj);
var (canvasPage, response) = await webRequestor.PostAsync<CanvasPage>(request);
if (canvasPage == null)
throw new Exception("created canvas course page was null");
return canvasPage.PageId;
}
public async Task Update(
ulong courseId,
string canvasPageId,
LocalCoursePage localCourse
)
{
log.Log($"updating course page: {localCourse.Name}");
var url = $"courses/{courseId}/pages/{canvasPageId}";
var request = new RestRequest(url);
var body = new
{
title = localCourse.Name,
body = localCourse.GetBodyHtml()
};
var bodyObj = new { wiki_page = body };
request.AddBody(bodyObj);
await webRequestor.PutAsync(request);
}
public async Task Delete(ulong courseId, string canvasPageId)
{
log.Log($"deleting page from canvas {canvasPageId}");
var url = $"courses/{courseId}/pages/{canvasPageId}";
var request = new RestRequest(url);
var response = await webRequestor.DeleteAsync(request);
if (!response.IsSuccessful)
{
log.Log(url);
throw new Exception("Failed to delete canvas course page");
}
}
}

View File

@@ -8,35 +8,26 @@ using RestSharp;
namespace Management.Services.Canvas;
public class CanvasService
{
private readonly IWebRequestor webRequestor;
private readonly CanvasServiceUtils utils;
private readonly MyLogger<CanvasService> logger;
public CanvasAssignmentService Assignments { get; }
public CanvasAssignmentGroupService AssignmentGroups { get; }
public CanvasModuleService Modules { get; }
public CanvasQuizService Quizzes { get; }
public CanvasService(
IWebRequestor webRequestor,
CanvasServiceUtils utils,
CanvasAssignmentService Assignments,
CanvasAssignmentGroupService AssignmentGroups,
CanvasModuleService Modules,
CanvasQuizService Quizzes,
MyLogger<CanvasService> logger
public class CanvasService(
IWebRequestor webRequestor,
CanvasServiceUtils utils,
CanvasAssignmentService Assignments,
CanvasAssignmentGroupService AssignmentGroups,
CanvasModuleService Modules,
CanvasQuizService Quizzes,
CanvasCoursePageService Pages,
MyLogger<CanvasService> logger
)
{
this.webRequestor = webRequestor;
this.utils = utils;
this.Assignments = Assignments;
this.AssignmentGroups = AssignmentGroups;
this.Modules = Modules;
this.Quizzes = Quizzes;
this.logger = logger;
}
{
private readonly IWebRequestor webRequestor = webRequestor;
private readonly CanvasServiceUtils utils = utils;
private readonly MyLogger<CanvasService> logger = logger;
public CanvasAssignmentService Assignments { get; } = Assignments;
public CanvasAssignmentGroupService AssignmentGroups { get; } = AssignmentGroups;
public CanvasModuleService Modules { get; } = Modules;
public CanvasQuizService Quizzes { get; } = Quizzes;
public CanvasCoursePageService Pages { get; } = Pages;
public async Task<IEnumerable<EnrollmentTermModel>> GetTerms()
{

View File

@@ -1,7 +1,6 @@
using System.Diagnostics;
using LocalModels;
using Management.Services;
using YamlDotNet.Serialization;
public class FileStorageManager
{

View File

@@ -131,7 +131,7 @@ public class CourseMarkdownLoader
}
private async Task<IEnumerable<LocalCoursePage>> loadPagesFromPath(string modulePath)
{
using var activity = DiagnosticsConfig.Source?.StartActivity("loading Pages from file");
using var activity = DiagnosticsConfig.Source?.StartActivity("loading Pages from path");
var pagesPath = $"{modulePath}/pages";
if (!Directory.Exists(pagesPath))
{