From bca849725515e744e0137b003e997bfafd9b97a5 Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Fri, 12 Jan 2024 15:57:35 -0700 Subject: [PATCH] before I dive too much more into adding pages in canvas --- .../AssignmentMarkdownEditor.razor | 3 - .../Pages/Course/Module/ModuleDetail.razor | 11 +- .../Pages/CoursePageForm/CoursePageForm.razor | 169 ++++++++++++++++++ .../CoursePageForm/CoursePageFormPage.razor | 71 ++++++++ .../CoursePageMarkdownEditor.razor | 79 ++++++++ Management.Web/Program.cs | 1 + .../Features/Configuration/CoursePlanner.cs | 3 + .../Configuration/PageEditorContext.cs | 19 +- .../Models/CanvasModels/Pages/CanvasPage.cs | 2 +- .../Local/Assignment/LocalAssignment.cs | 1 - Management/Models/Local/LocalCoursePage.cs | 2 + Management/Models/Local/LocalModules.cs | 7 + .../Canvas/CanvasAssignmentService.cs | 27 ++- .../Canvas/CanvasCoursePageService.cs | 87 +++++++++ Management/Services/Canvas/CanvasService.cs | 47 ++--- .../Services/Files/FileStorageManager.cs | 1 - .../Services/Files/LoadMarkdownCourse.cs | 2 +- 17 files changed, 472 insertions(+), 60 deletions(-) create mode 100644 Management.Web/Pages/CoursePageForm/CoursePageForm.razor create mode 100644 Management.Web/Pages/CoursePageForm/CoursePageFormPage.razor create mode 100644 Management.Web/Pages/CoursePageForm/CoursePageMarkdownEditor.razor create mode 100644 Management/Services/Canvas/CanvasCoursePageService.cs diff --git a/Management.Web/Pages/AssignmentForm/AssignmentMarkdownEditor.razor b/Management.Web/Pages/AssignmentForm/AssignmentMarkdownEditor.razor index 50d272f..f557e23 100644 --- a/Management.Web/Pages/AssignmentForm/AssignmentMarkdownEditor.razor +++ b/Management.Web/Pages/AssignmentForm/AssignmentMarkdownEditor.razor @@ -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) { diff --git a/Management.Web/Pages/Course/Module/ModuleDetail.razor b/Management.Web/Pages/Course/Module/ModuleDetail.razor index 0d34832..8eeca9f 100644 --- a/Management.Web/Pages/Course/Module/ModuleDetail.razor +++ b/Management.Web/Pages/Course/Module/ModuleDetail.razor @@ -155,7 +155,7 @@
Assignments
- @foreach(var p in Module.Pages) + @* @foreach(var p in Module.Pages) { } @@ -167,6 +167,15 @@ @foreach (var quiz in Module.Quizzes) { + } *@ + @foreach(var item in Module.SortedModuleItems) + { + @(item switch + { + LocalAssignment assignment => (@), + LocalQuiz quiz => (@), + LocalCoursePage page => (@), + }) }
diff --git a/Management.Web/Pages/CoursePageForm/CoursePageForm.razor b/Management.Web/Pages/CoursePageForm/CoursePageForm.razor new file mode 100644 index 0000000..ed479d4 --- /dev/null +++ b/Management.Web/Pages/CoursePageForm/CoursePageForm.razor @@ -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(); + } +} + +
+
+ @pageContext.Page?.Name +
+ +
+ @if (pageContext.Page != null) + { + + } +
+ +
+ @if (addingPageToCanvas || deletingPageFromCanvas) + { +
+ +
+ } + + + + @if (pageInCanvas != null) + { + + View in Canvas + + + + } + +
+ + +
diff --git a/Management.Web/Pages/CoursePageForm/CoursePageFormPage.razor b/Management.Web/Pages/CoursePageForm/CoursePageFormPage.razor new file mode 100644 index 0000000..be3e1e6 --- /dev/null +++ b/Management.Web/Pages/CoursePageForm/CoursePageFormPage.razor @@ -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 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(); + } + } +} + + +@CourseName - @PageName + +
+ @if (loading) + { + + } + + @if (planner.LocalCourse != null && pageContext.Page != null) + { + + } +
diff --git a/Management.Web/Pages/CoursePageForm/CoursePageMarkdownEditor.razor b/Management.Web/Pages/CoursePageForm/CoursePageMarkdownEditor.razor new file mode 100644 index 0000000..6a91de7 --- /dev/null +++ b/Management.Web/Pages/CoursePageForm/CoursePageMarkdownEditor.razor @@ -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(); + } +} + +
+ @if(pageContext.Page != null && planner.LocalCourse != null) + { +
+
+ + +
+
+ @if (error != null) + { +

Error: @error

+ } +
Due At: @pageContext.Page.DueAt
+
+
+ @(preview) +
+
+
+ } +
diff --git a/Management.Web/Program.cs b/Management.Web/Program.cs index f1fe951..977ce3a 100644 --- a/Management.Web/Program.cs +++ b/Management.Web/Program.cs @@ -74,6 +74,7 @@ builder.Services.AddScoped(typeof(MyLogger<>)); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); +builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Management/Features/Configuration/CoursePlanner.cs b/Management/Features/Configuration/CoursePlanner.cs index 0680084..277f07b 100644 --- a/Management/Features/Configuration/CoursePlanner.cs +++ b/Management/Features/Configuration/CoursePlanner.cs @@ -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? CanvasAssignmentGroups { get; internal set; } public IEnumerable? CanvasQuizzes { get; internal set; } public IEnumerable? CanvasModules { get; internal set; } + public IEnumerable? CanvasPages { get; internal set; } public Dictionary>? 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; diff --git a/Management/Features/Configuration/PageEditorContext.cs b/Management/Features/Configuration/PageEditorContext.cs index fbf67b0..f2e1d7b 100644 --- a/Management/Features/Configuration/PageEditorContext.cs +++ b/Management/Features/Configuration/PageEditorContext.cs @@ -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) diff --git a/Management/Models/CanvasModels/Pages/CanvasPage.cs b/Management/Models/CanvasModels/Pages/CanvasPage.cs index e24faed..55b6fdd 100644 --- a/Management/Models/CanvasModels/Pages/CanvasPage.cs +++ b/Management/Models/CanvasModels/Pages/CanvasPage.cs @@ -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, diff --git a/Management/Models/Local/Assignment/LocalAssignment.cs b/Management/Models/Local/Assignment/LocalAssignment.cs index 0731d45..a1f9159 100644 --- a/Management/Models/Local/Assignment/LocalAssignment.cs +++ b/Management/Models/Local/Assignment/LocalAssignment.cs @@ -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; } diff --git a/Management/Models/Local/LocalCoursePage.cs b/Management/Models/Local/LocalCoursePage.cs index 353432a..8742dfb 100644 --- a/Management/Models/Local/LocalCoursePage.cs +++ b/Management/Models/Local/LocalCoursePage.cs @@ -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 }; } + } diff --git a/Management/Models/Local/LocalModules.cs b/Management/Models/Local/LocalModules.cs index f2136bc..5b87e6d 100644 --- a/Management/Models/Local/LocalModules.cs +++ b/Management/Models/Local/LocalModules.cs @@ -7,4 +7,11 @@ public record LocalModule public IEnumerable Assignments { get; init; } = []; public IEnumerable Quizzes { get; init; } = []; public IEnumerable Pages { get; init; } = []; + + public IEnumerable SortedModuleItems => + Enumerable.Empty() + .Concat(Assignments) + .Concat(Quizzes) + .Concat(Pages) + .OrderBy(i => i.DueAt); } diff --git a/Management/Services/Canvas/CanvasAssignmentService.cs b/Management/Services/Canvas/CanvasAssignmentService.cs index fbc4727..eaddf3e 100644 --- a/Management/Services/Canvas/CanvasAssignmentService.cs +++ b/Management/Services/Canvas/CanvasAssignmentService.cs @@ -4,22 +4,15 @@ using RestSharp; namespace Management.Services.Canvas; -public class CanvasAssignmentService -{ - private readonly IWebRequestor webRequestor; - private readonly CanvasServiceUtils utils; - private readonly MyLogger log; - - public CanvasAssignmentService( - IWebRequestor webRequestor, - CanvasServiceUtils utils, - MyLogger logger +public class CanvasAssignmentService( + IWebRequestor webRequestor, + CanvasServiceUtils utils, + MyLogger logger ) - { - this.webRequestor = webRequestor; - this.utils = utils; - this.log = logger; - } +{ + private readonly IWebRequestor webRequestor = webRequestor; + private readonly CanvasServiceUtils utils = utils; + private readonly MyLogger log = logger; public async Task> GetAll(ulong courseId) { @@ -86,10 +79,10 @@ public class CanvasAssignmentService points_possible = localAssignment.PointsPossible, assignment_group_id = canvasAssignmentGroupId, }; - + var bodyObj = new { assignment = body }; request.AddBody(bodyObj); - + await webRequestor.PutAsync(request); await CreateRubric(courseId, canvasAssignmentId, localAssignment); diff --git a/Management/Services/Canvas/CanvasCoursePageService.cs b/Management/Services/Canvas/CanvasCoursePageService.cs new file mode 100644 index 0000000..3f0f382 --- /dev/null +++ b/Management/Services/Canvas/CanvasCoursePageService.cs @@ -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 logger + ) +{ + private readonly IWebRequestor webRequestor = webRequestor; + private readonly CanvasServiceUtils utils = utils; + private readonly MyLogger log = logger; + + + + public async Task> GetAll(ulong courseId) + { + var url = $"courses/{courseId}/pages"; + var request = new RestRequest(url); + // request.AddParameter("include[]", "overrides"); + var pagesResponse = await utils.PaginatedRequest>(request); + return pagesResponse.SelectMany( + pages => pages + ); + } + + + public async Task 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(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"); + } + } +} diff --git a/Management/Services/Canvas/CanvasService.cs b/Management/Services/Canvas/CanvasService.cs index ae19752..aa8c2fa 100644 --- a/Management/Services/Canvas/CanvasService.cs +++ b/Management/Services/Canvas/CanvasService.cs @@ -8,35 +8,26 @@ using RestSharp; namespace Management.Services.Canvas; -public class CanvasService -{ - private readonly IWebRequestor webRequestor; - private readonly CanvasServiceUtils utils; - private readonly MyLogger 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 logger +public class CanvasService( + IWebRequestor webRequestor, + CanvasServiceUtils utils, + CanvasAssignmentService Assignments, + CanvasAssignmentGroupService AssignmentGroups, + CanvasModuleService Modules, + CanvasQuizService Quizzes, + CanvasCoursePageService Pages, + MyLogger 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 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> GetTerms() { diff --git a/Management/Services/Files/FileStorageManager.cs b/Management/Services/Files/FileStorageManager.cs index 3c38ea1..7d79e46 100644 --- a/Management/Services/Files/FileStorageManager.cs +++ b/Management/Services/Files/FileStorageManager.cs @@ -1,7 +1,6 @@ using System.Diagnostics; using LocalModels; using Management.Services; -using YamlDotNet.Serialization; public class FileStorageManager { diff --git a/Management/Services/Files/LoadMarkdownCourse.cs b/Management/Services/Files/LoadMarkdownCourse.cs index c076d5f..6b3f802 100644 --- a/Management/Services/Files/LoadMarkdownCourse.cs +++ b/Management/Services/Files/LoadMarkdownCourse.cs @@ -131,7 +131,7 @@ public class CourseMarkdownLoader } private async Task> 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)) {