mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
referencing courses directly from canvas
This commit is contained in:
5
.csharpierrc.json
Normal file
5
.csharpierrc.json
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 100,
|
||||||
|
"useTabs": false,
|
||||||
|
"tabWidth": 2
|
||||||
|
}
|
||||||
3
.vscode/extensions.json
vendored
3
.vscode/extensions.json
vendored
@@ -1,5 +1,6 @@
|
|||||||
{
|
{
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
"alexkrechik.cucumberautocomplete"
|
"alexkrechik.cucumberautocomplete",
|
||||||
|
"csharpier.csharpier-vscode"
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
1
.vscode/settings.json
vendored
1
.vscode/settings.json
vendored
@@ -1,6 +1,5 @@
|
|||||||
{
|
{
|
||||||
"cucumberautocomplete.steps": [
|
"cucumberautocomplete.steps": [
|
||||||
"./Management.Gherkin/**/*.cs"
|
"./Management.Gherkin/**/*.cs"
|
||||||
|
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
@@ -14,7 +14,7 @@ public class ConfigurationTests
|
|||||||
EndAt: endAt
|
EndAt: endAt
|
||||||
);
|
);
|
||||||
var daysOfWeek = new DayOfWeek[] { DayOfWeek.Monday };
|
var daysOfWeek = new DayOfWeek[] { DayOfWeek.Monday };
|
||||||
var management = new ConfigurationManagement();
|
var management = new CoursePlanner();
|
||||||
management.SetConfiguration(canvasTerm, daysOfWeek);
|
management.SetConfiguration(canvasTerm, daysOfWeek);
|
||||||
var config = management.SemesterCalendar;
|
var config = management.SemesterCalendar;
|
||||||
|
|
||||||
|
|||||||
@@ -1,40 +1,40 @@
|
|||||||
public class ModuleTests
|
// public class ModuleTests
|
||||||
{
|
// {
|
||||||
[Test]
|
// [Test]
|
||||||
public void CanAddModule()
|
// public void CanAddModule()
|
||||||
{
|
// {
|
||||||
var manager = new ModuleManager();
|
// var manager = new ModuleManager();
|
||||||
var module = new CourseModule("First Module", new LocalAssignment[] { });
|
// var module = new CourseModule("First Module", new LocalAssignment[] { });
|
||||||
manager.AddModule(module);
|
// manager.AddModule(module);
|
||||||
|
|
||||||
manager.Modules.Count().Should().Be(1);
|
// manager.Modules.Count().Should().Be(1);
|
||||||
manager.Modules.First().Should().Be(module);
|
// manager.Modules.First().Should().Be(module);
|
||||||
}
|
// }
|
||||||
|
|
||||||
[Test]
|
// [Test]
|
||||||
public void CanAddAssignmentToCorrectModule()
|
// public void CanAddAssignmentToCorrectModule()
|
||||||
{
|
// {
|
||||||
var manager = new ModuleManager();
|
// var manager = new ModuleManager();
|
||||||
manager.AddModule(new CourseModule("First Module", new LocalAssignment[] { }));
|
// manager.AddModule(new CourseModule("First Module", new LocalAssignment[] { }));
|
||||||
manager.AddModule(new CourseModule("Second Module", new LocalAssignment[] { }));
|
// manager.AddModule(new CourseModule("Second Module", new LocalAssignment[] { }));
|
||||||
manager.AddModule(new CourseModule("Third Module", new LocalAssignment[] { }));
|
// manager.AddModule(new CourseModule("Third Module", new LocalAssignment[] { }));
|
||||||
manager.AddModule(new CourseModule("Fourth Module", new LocalAssignment[] { }));
|
// manager.AddModule(new CourseModule("Fourth Module", new LocalAssignment[] { }));
|
||||||
|
|
||||||
var assignment = new LocalAssignment
|
// var assignment = new LocalAssignment
|
||||||
{
|
// {
|
||||||
name = "testname",
|
// name = "testname",
|
||||||
description = "testDescription",
|
// description = "testDescription",
|
||||||
published = false,
|
// published = false,
|
||||||
lock_at_due_date = true,
|
// lock_at_due_date = true,
|
||||||
rubric = new RubricItem[] { },
|
// rubric = new RubricItem[] { },
|
||||||
lock_at = null,
|
// lock_at = null,
|
||||||
due_at = DateTime.Now,
|
// due_at = DateTime.Now,
|
||||||
points_possible = 10,
|
// points_possible = 10,
|
||||||
submission_types = new SubmissionType[] { SubmissionType.online_text_entry }
|
// submission_types = new SubmissionType[] { SubmissionType.online_text_entry }
|
||||||
};
|
// };
|
||||||
|
|
||||||
manager.AddAssignment(3, assignment);
|
// manager.AddAssignment(3, assignment);
|
||||||
manager.Modules.Count().Should().Be(4);
|
// manager.Modules.Count().Should().Be(4);
|
||||||
manager.Modules.ElementAt(3).Assignments.Count().Should().Be(1);
|
// manager.Modules.ElementAt(3).Assignments.Count().Should().Be(1);
|
||||||
}
|
// }
|
||||||
}
|
// }
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
@using Management.Web.Shared.Module
|
@using Management.Web.Shared.Module
|
||||||
@using Management.Web.Shared.Semester
|
@using Management.Web.Shared.Semester
|
||||||
|
|
||||||
@inject IConfigurationManagement configurationManagement
|
@inject CoursePlanner configurationManagement
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
@page "/counter"
|
|
||||||
|
|
||||||
<PageTitle>Counter</PageTitle>
|
|
||||||
|
|
||||||
<h1>Counter</h1>
|
|
||||||
|
|
||||||
<p role="status">Current count: @currentCount</p>
|
|
||||||
|
|
||||||
<button class="btn btn-primary" @onclick="IncrementCount">Click me</button>
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private int currentCount = 0;
|
|
||||||
|
|
||||||
private void IncrementCount()
|
|
||||||
{
|
|
||||||
currentCount++;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
@page "/drag"
|
|
||||||
@inject IJSRuntime JSRuntime
|
|
||||||
|
|
||||||
@code {
|
|
||||||
private string dragClass = "bg-light";
|
|
||||||
|
|
||||||
void OnDragStart()
|
|
||||||
{
|
|
||||||
Console.WriteLine("on drag start");
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task OnDrop()
|
|
||||||
{
|
|
||||||
Console.WriteLine("on drop");
|
|
||||||
dragClass="bg-light";
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task OnDragEnter() {
|
|
||||||
dragClass="bg-dark";
|
|
||||||
}
|
|
||||||
async Task OnDragLeave() {
|
|
||||||
dragClass="bg-light";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
<div draggable="true"
|
|
||||||
@ondragstart="OnDragStart"
|
|
||||||
>
|
|
||||||
Drag me!
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
@ondrop="@(() => OnDrop())"
|
|
||||||
@ondragenter="OnDragEnter"
|
|
||||||
@ondragleave="OnDragLeave"
|
|
||||||
ondragover="event.preventDefault();"
|
|
||||||
ondragstart="event.dataTransfer.setData('', event.target.id);"
|
|
||||||
|
|
||||||
style="width: 100px;height: 100px;"
|
|
||||||
class="@dragClass"
|
|
||||||
>
|
|
||||||
Drop here!
|
|
||||||
</div>
|
|
||||||
@@ -1,23 +1,42 @@
|
|||||||
@page "/"
|
@page "/"
|
||||||
@using CanvasModel.EnrollmentTerms
|
@using CanvasModel.EnrollmentTerms
|
||||||
@using Management.Web.Shared.Semester
|
@using Management.Web.Shared.Semester
|
||||||
|
@using CanvasModel.Courses
|
||||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||||
|
|
||||||
@inject ICanvasService canvasService
|
@inject CanvasService canvasService
|
||||||
@inject IConfigurationManagement configurationManagement
|
@inject CoursePlanner configurationManagement
|
||||||
@inject ProtectedLocalStorage BrowserStorage
|
@inject ProtectedLocalStorage BrowserStorage
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
private string semesterConfigurationKey = "semesterCalendarConfiguration";
|
private string semesterConfigurationKey = "semesterCalendarConfiguration";
|
||||||
private IEnumerable<EnrollmentTermModel>? terms { get; set; } = null;
|
private IEnumerable<EnrollmentTermModel>? terms { get; set; } = null;
|
||||||
private ulong? selectedTermId { get; set; }
|
private bool loadingCourses = false;
|
||||||
|
private IEnumerable<CourseModel>? courses {get; set;} = null;
|
||||||
|
|
||||||
|
private ulong? _selectedTermId { get; set; }
|
||||||
|
private ulong? selectedTermId {
|
||||||
|
get => _selectedTermId;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_selectedTermId = value;
|
||||||
|
updateCourses();
|
||||||
|
}
|
||||||
|
}
|
||||||
private EnrollmentTermModel? selectedTerm
|
private EnrollmentTermModel? selectedTerm
|
||||||
{
|
{
|
||||||
get => terms?.FirstOrDefault(t => t.Id == selectedTermId);
|
get => terms?.FirstOrDefault(t => t.Id == selectedTermId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ulong? selectedCourseId {
|
||||||
|
get => configurationManagement.Course?.Id;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
configurationManagement.Course = courses?.First(c => c.Id == value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private List<DayOfWeek> days { get; set; } = new();
|
private List<DayOfWeek> days { get; set; } = new();
|
||||||
private bool saved { get; set; } = false;
|
private bool saved { get; set; } = false;
|
||||||
|
|
||||||
@@ -33,16 +52,35 @@
|
|||||||
if(firstRender)
|
if(firstRender)
|
||||||
{
|
{
|
||||||
var storedConfiguration = await BrowserStorage.GetAsync<SemesterCalendarConfig>(semesterConfigurationKey);
|
var storedConfiguration = await BrowserStorage.GetAsync<SemesterCalendarConfig>(semesterConfigurationKey);
|
||||||
if (storedConfiguration.Success) {
|
if (storedConfiguration.Success)
|
||||||
|
{
|
||||||
Console.WriteLine(JsonSerializer.Serialize(storedConfiguration.Value));
|
Console.WriteLine(JsonSerializer.Serialize(storedConfiguration.Value));
|
||||||
configurationManagement.SemesterCalendar = storedConfiguration.Value;
|
configurationManagement.SemesterCalendar = storedConfiguration.Value;
|
||||||
} else {
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
Console.WriteLine("no stored configuration");
|
Console.WriteLine("no stored configuration");
|
||||||
}
|
}
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task updateCourses()
|
||||||
|
{
|
||||||
|
if(selectedTermId != null)
|
||||||
|
{
|
||||||
|
loadingCourses = true;
|
||||||
|
|
||||||
|
courses = await canvasService.GetCourses((ulong)selectedTermId);
|
||||||
|
System.Console.WriteLine(courses);
|
||||||
|
System.Console.WriteLine(selectedTermId);
|
||||||
|
loadingCourses = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
courses = null;
|
||||||
|
|
||||||
|
StateHasChanged();
|
||||||
|
}
|
||||||
private void readTermFromConfig()
|
private void readTermFromConfig()
|
||||||
{
|
{
|
||||||
if (terms == null || configurationManagement.SemesterCalendar == null) return;
|
if (terms == null || configurationManagement.SemesterCalendar == null) return;
|
||||||
@@ -84,8 +122,6 @@
|
|||||||
{
|
{
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
|
|
||||||
<form>
|
|
||||||
<label for="termselect">Select Term:</label>
|
<label for="termselect">Select Term:</label>
|
||||||
<select id="termselect" class="form-select" @bind="selectedTermId">
|
<select id="termselect" class="form-select" @bind="selectedTermId">
|
||||||
@foreach (var term in terms)
|
@foreach (var term in terms)
|
||||||
@@ -93,13 +129,12 @@
|
|||||||
<option value="@term.Id">@term.Name</option>
|
<option value="@term.Id">@term.Name</option>
|
||||||
}
|
}
|
||||||
</select>
|
</select>
|
||||||
</form>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
@if (selectedTerm is not null)
|
@if (selectedTerm is not null)
|
||||||
{
|
{
|
||||||
<h5>Select Days Of Week</h5>
|
<h5 class="text-center mt-3">Select Days Of Week</h5>
|
||||||
<div class="row m-3">
|
<div class="row m-3">
|
||||||
@foreach (DayOfWeek day in (DayOfWeek[])Enum.GetValues(typeof(DayOfWeek)))
|
@foreach (DayOfWeek day in (DayOfWeek[])Enum.GetValues(typeof(DayOfWeek)))
|
||||||
{
|
{
|
||||||
@@ -118,7 +153,27 @@
|
|||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
@if(loadingCourses)
|
||||||
|
{
|
||||||
|
<Spinner />
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@if(courses != null)
|
||||||
|
{
|
||||||
|
<div class="row justify-content-center m-3">
|
||||||
|
<div class="col-auto">
|
||||||
|
<label for="courseselect">Select Course:</label>
|
||||||
|
<select id="courseselect" class="form-select" @bind="selectedCourseId">
|
||||||
|
@foreach (var course in courses)
|
||||||
|
{
|
||||||
|
<option value="@course.Id">@course.Name</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
<div class="row justify-content-center">
|
<div class="row justify-content-center">
|
||||||
<div class="col-auto">
|
<div class="col-auto">
|
||||||
<button @onclick="@HandleSave" class="btn btn-primary">
|
<button @onclick="@HandleSave" class="btn btn-primary">
|
||||||
@@ -130,5 +185,5 @@
|
|||||||
|
|
||||||
@if (configurationManagement.SemesterCalendar is not null)
|
@if (configurationManagement.SemesterCalendar is not null)
|
||||||
{
|
{
|
||||||
<div>Config complete</div>
|
<div class="text-center">Config complete</div>
|
||||||
}
|
}
|
||||||
@@ -14,10 +14,10 @@ var builder = WebApplication.CreateBuilder(args);
|
|||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
builder.Services.AddServerSideBlazor();
|
builder.Services.AddServerSideBlazor();
|
||||||
builder.Services.AddSingleton<IWebRequestor, WebRequestor>();
|
builder.Services.AddSingleton<IWebRequestor, WebRequestor>();
|
||||||
builder.Services.AddSingleton<ICanvasService, CanvasService>();
|
builder.Services.AddSingleton<CanvasService, CanvasService>();
|
||||||
builder.Services.AddSingleton<IConfigurationManagement, ConfigurationManagement>();
|
builder.Services.AddSingleton<CoursePlanner>();
|
||||||
builder.Services.AddSingleton<IModuleManager, ModuleManager>();
|
|
||||||
builder.Services.AddSingleton<AssignmentDragContainer>();
|
builder.Services.AddSingleton<AssignmentDragContainer>();
|
||||||
|
builder.Services.AddScoped<StorageManagement>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@inject IModuleManager moduleManager
|
@inject CoursePlanner configurationManagement
|
||||||
|
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
@@ -16,6 +17,7 @@
|
|||||||
{
|
{
|
||||||
var newAssignment = new LocalAssignment
|
var newAssignment = new LocalAssignment
|
||||||
{
|
{
|
||||||
|
id = Guid.NewGuid().ToString(),
|
||||||
name = Name,
|
name = Name,
|
||||||
description = "testDescription",
|
description = "testDescription",
|
||||||
published = false,
|
published = false,
|
||||||
@@ -26,7 +28,7 @@
|
|||||||
points_possible = 10,
|
points_possible = 10,
|
||||||
submission_types = new SubmissionType[] { SubmissionType.online_text_entry }
|
submission_types = new SubmissionType[] { SubmissionType.online_text_entry }
|
||||||
};
|
};
|
||||||
moduleManager.AddAssignment(ModuleIndex, newAssignment);
|
configurationManagement.Assignments = configurationManagement.Assignments.Append(newAssignment);
|
||||||
await OnSubmit.InvokeAsync();
|
await OnSubmit.InvokeAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
@using Management.Web.Shared.Module.Assignment
|
@using Management.Web.Shared.Module.Assignment
|
||||||
@inject IModuleManager moduleManager
|
@inject CoursePlanner configurationManagement
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
[Parameter, EditorRequired]
|
[Parameter, EditorRequired]
|
||||||
@@ -11,7 +11,7 @@
|
|||||||
{
|
{
|
||||||
get
|
get
|
||||||
{
|
{
|
||||||
return moduleManager.Modules.ElementAtOrDefault(ModuleIndex);
|
return configurationManagement.Modules.ElementAtOrDefault(ModuleIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,31 +2,22 @@
|
|||||||
@using System.Linq
|
@using System.Linq
|
||||||
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
@using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage
|
||||||
|
|
||||||
@inject IModuleManager moduleManager
|
@inject CoursePlanner configurationManagement
|
||||||
@inject ProtectedLocalStorage BrowserStorage
|
@inject ProtectedLocalStorage BrowserStorage
|
||||||
|
@inject StorageManagement storage
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
private bool showNewModule { get; set; } = false;
|
private bool showNewModule { get; set; } = false;
|
||||||
private string moduleStorageKey = "module storage key";
|
|
||||||
|
|
||||||
private async Task Save()
|
|
||||||
{
|
|
||||||
await BrowserStorage.SetAsync(moduleStorageKey, moduleManager.Modules);
|
|
||||||
|
|
||||||
}
|
|
||||||
protected override async Task OnAfterRenderAsync(bool firstRender)
|
protected override async Task OnAfterRenderAsync(bool firstRender)
|
||||||
{
|
{
|
||||||
if(firstRender)
|
if(firstRender)
|
||||||
{
|
{
|
||||||
var storedModules = await BrowserStorage.GetAsync<IEnumerable<CourseModule>>(moduleStorageKey);
|
await storage.LoadStoredConfig();
|
||||||
if (storedModules.Success) {
|
|
||||||
moduleManager.Modules = storedModules.Value ?? throw new Exception("stored modules was null, it shouldn't have been");
|
|
||||||
} else {
|
|
||||||
Console.WriteLine("no stored modules");
|
|
||||||
}
|
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@if (!showNewModule)
|
@if (!showNewModule)
|
||||||
@@ -43,7 +34,7 @@ else
|
|||||||
<NewModule OnSubmit="() => showNewModule = false" />
|
<NewModule OnSubmit="() => showNewModule = false" />
|
||||||
}
|
}
|
||||||
|
|
||||||
@foreach (var i in moduleManager.Modules.Select((_value, i) => i))
|
@foreach (var i in configurationManagement.Modules.Select((_value, i) => i))
|
||||||
{
|
{
|
||||||
<hr>
|
<hr>
|
||||||
<ModuleDetail ModuleIndex="i" />
|
<ModuleDetail ModuleIndex="i" />
|
||||||
@@ -53,7 +44,7 @@ else
|
|||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<button
|
<button
|
||||||
class="btn btn-primary"
|
class="btn btn-primary"
|
||||||
@onclick="Save"
|
@onclick="@(async () => await storage.Save())"
|
||||||
>
|
>
|
||||||
Save Modules
|
Save Modules
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
@inject IModuleManager moduleManager
|
@inject CoursePlanner configurationManagement
|
||||||
|
|
||||||
@code {
|
@code {
|
||||||
|
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
private async Task submitHandler()
|
private async Task submitHandler()
|
||||||
{
|
{
|
||||||
var module = new CourseModule(Name: Name, Assignments: new LocalAssignment[] { });
|
var module = new CourseModule(Name: Name, Assignments: new LocalAssignment[] { });
|
||||||
moduleManager.AddModule(module);
|
configurationManagement.Modules = configurationManagement.Modules.Append(module);
|
||||||
Name = "";
|
Name = "";
|
||||||
await OnSubmit.InvokeAsync();
|
await OnSubmit.InvokeAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@inject AssignmentDragContainer dragContainer
|
@inject AssignmentDragContainer dragContainer
|
||||||
|
@inject CoursePlanner configurationManagement
|
||||||
|
|
||||||
@code
|
@code
|
||||||
{
|
{
|
||||||
|
|||||||
4
Management.Web/Shared/Spinner.razor
Normal file
4
Management.Web/Shared/Spinner.razor
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
|
||||||
|
<div class="text-center m-3">
|
||||||
|
<span class="loader"></span>
|
||||||
|
</div>
|
||||||
56
Management.Web/Shared/Spinner.razor.css
Normal file
56
Management.Web/Shared/Spinner.razor.css
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
.loader {
|
||||||
|
width: 48px;
|
||||||
|
height: 48px;
|
||||||
|
border-radius: 50%;
|
||||||
|
display: inline-block;
|
||||||
|
position: relative;
|
||||||
|
border: 3px solid;
|
||||||
|
border-color: #6c757d #6c757d transparent transparent;
|
||||||
|
box-sizing: border-box;
|
||||||
|
animation: rotation 2s linear infinite;
|
||||||
|
}
|
||||||
|
.loader::after,
|
||||||
|
.loader::before {
|
||||||
|
content: '';
|
||||||
|
box-sizing: border-box;
|
||||||
|
position: absolute;
|
||||||
|
left: 0;
|
||||||
|
right: 0;
|
||||||
|
top: 0;
|
||||||
|
bottom: 0;
|
||||||
|
margin: auto;
|
||||||
|
border: 3px solid;
|
||||||
|
border-color: transparent transparent #092565 #092565;
|
||||||
|
width: 40px;
|
||||||
|
height: 40px;
|
||||||
|
border-radius: 50%;
|
||||||
|
box-sizing: border-box;
|
||||||
|
animation: rotationBack 1s linear infinite;
|
||||||
|
transform-origin: center center;
|
||||||
|
}
|
||||||
|
/* #092565 */
|
||||||
|
/* #3a0647 */
|
||||||
|
.loader::before {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-color: #6c757d #6c757d transparent transparent;
|
||||||
|
animation: rotation 3s linear infinite;
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes rotation {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@keyframes rotationBack {
|
||||||
|
0% {
|
||||||
|
transform: rotate(0deg);
|
||||||
|
}
|
||||||
|
100% {
|
||||||
|
transform: rotate(-360deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
72
Management.Web/Utils/StorageManagement.cs
Normal file
72
Management.Web/Utils/StorageManagement.cs
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
using Microsoft.AspNetCore.Components.Server.ProtectedBrowserStorage;
|
||||||
|
|
||||||
|
public class StorageManagement
|
||||||
|
{
|
||||||
|
private string moduleStorageKey = "module storage key";
|
||||||
|
private string assignmentStorageKey = "assignment storage key";
|
||||||
|
private string courseIdKey = "course id storage key";
|
||||||
|
|
||||||
|
private CoursePlanner planner { get; }
|
||||||
|
private ProtectedLocalStorage storage { get; }
|
||||||
|
private CanvasService canvas { get; }
|
||||||
|
|
||||||
|
public StorageManagement(
|
||||||
|
CoursePlanner configurationManagement,
|
||||||
|
ProtectedLocalStorage BrowserStorage,
|
||||||
|
CanvasService canvasService
|
||||||
|
)
|
||||||
|
{
|
||||||
|
planner = configurationManagement;
|
||||||
|
storage = BrowserStorage;
|
||||||
|
canvas = canvasService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task LoadStoredConfig()
|
||||||
|
{
|
||||||
|
// var storedModules = await storage.GetAsync<IEnumerable<CourseModule>>(moduleStorageKey);
|
||||||
|
// if (storedModules.Success)
|
||||||
|
// {
|
||||||
|
// planner.Modules =
|
||||||
|
// storedModules.Value
|
||||||
|
// ?? throw new Exception("stored modules was null, it shouldn't have been");
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Console.WriteLine("no stored modules");
|
||||||
|
// }
|
||||||
|
|
||||||
|
// var storedAssignments = await storage.GetAsync<IEnumerable<CourseModule>>(assignmentStorageKey);
|
||||||
|
// if (storedAssignments.Success)
|
||||||
|
// {
|
||||||
|
// planner.Modules =
|
||||||
|
// storedAssignments.Value
|
||||||
|
// ?? throw new Exception("stored assignments are null, it shouldn't have been");
|
||||||
|
// }
|
||||||
|
// else
|
||||||
|
// {
|
||||||
|
// Console.WriteLine("no stored assignments");
|
||||||
|
// }
|
||||||
|
|
||||||
|
var storedCourseId = await storage.GetAsync<ulong>(courseIdKey);
|
||||||
|
if (storedCourseId.Success)
|
||||||
|
{
|
||||||
|
// var courses =
|
||||||
|
planner.Course = await canvas.GetCourse(storedCourseId.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Console.WriteLine("no stored assignments");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task Save()
|
||||||
|
{
|
||||||
|
// await storage.SetAsync(moduleStorageKey, planner.Modules);
|
||||||
|
// await storage.SetAsync(assignmentStorageKey, planner.Assignments);
|
||||||
|
|
||||||
|
if (planner.Course != null)
|
||||||
|
await storage.SetAsync(courseIdKey, planner.Course.Id);
|
||||||
|
else
|
||||||
|
await storage.DeleteAsync(courseIdKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
using CanvasModel.EnrollmentTerms;
|
using CanvasModel.EnrollmentTerms;
|
||||||
|
using CanvasModel.Courses;
|
||||||
|
|
||||||
public class ConfigurationManagement : IConfigurationManagement
|
public class CoursePlanner
|
||||||
{
|
{
|
||||||
public void SetConfiguration(
|
public void SetConfiguration(
|
||||||
EnrollmentTermModel canvasTerm,
|
EnrollmentTermModel canvasTerm,
|
||||||
@@ -18,8 +19,8 @@ public class ConfigurationManagement : IConfigurationManagement
|
|||||||
}
|
}
|
||||||
|
|
||||||
public SemesterCalendarConfig? SemesterCalendar { get; set; } = null;
|
public SemesterCalendarConfig? SemesterCalendar { get; set; } = null;
|
||||||
public IModuleManager ModuleManager {get; private set;} = new ModuleManager();
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
public IEnumerable<CourseModule> Modules { get; set; } = new CourseModule[] { };
|
||||||
|
public IEnumerable<LocalAssignment> Assignments { get; set; } = new LocalAssignment[] { };
|
||||||
|
public CourseModel? Course { get; set; } = null;
|
||||||
}
|
}
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using CanvasModel.EnrollmentTerms;
|
|
||||||
|
|
||||||
public interface IConfigurationManagement
|
|
||||||
{
|
|
||||||
SemesterCalendarConfig? SemesterCalendar { get; set; }
|
|
||||||
|
|
||||||
void SetConfiguration(EnrollmentTermModel canvasTerm, DayOfWeek[] daysOfWeek);
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
public interface IModuleManager
|
|
||||||
{
|
|
||||||
IEnumerable<CourseModule> Modules { get; set; }
|
|
||||||
public void AddModule(CourseModule newModule);
|
|
||||||
public void AddAssignment(int moduleIndex, LocalAssignment assignment);
|
|
||||||
}
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
public class ModuleManager : IModuleManager
|
|
||||||
{
|
|
||||||
public IEnumerable<CourseModule> Modules { get; set; } = new CourseModule[] { };
|
|
||||||
|
|
||||||
public void AddAssignment(int moduleIndex, LocalAssignment assignment)
|
|
||||||
{
|
|
||||||
var newAssignments = Modules.ElementAt(moduleIndex).Assignments.Append(assignment);
|
|
||||||
var newModule = Modules.ElementAt(moduleIndex) with { Assignments = newAssignments };
|
|
||||||
if (newModule == null)
|
|
||||||
throw new Exception($"cannot get module at index {moduleIndex}");
|
|
||||||
|
|
||||||
Modules = Modules
|
|
||||||
.Take(moduleIndex)
|
|
||||||
.Append(newModule)
|
|
||||||
.Concat(Modules.Skip(moduleIndex + 1));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddModule(CourseModule newModule)
|
|
||||||
{
|
|
||||||
Modules = Modules.Append(newModule);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,7 +1,4 @@
|
|||||||
public record RubricItem(
|
public record RubricItem(int Points, string Label);
|
||||||
int Points,
|
|
||||||
string Label
|
|
||||||
);
|
|
||||||
|
|
||||||
public enum SubmissionType
|
public enum SubmissionType
|
||||||
{
|
{
|
||||||
@@ -19,6 +16,7 @@ public enum SubmissionType
|
|||||||
|
|
||||||
public record LocalAssignment
|
public record LocalAssignment
|
||||||
{
|
{
|
||||||
|
public string id { get; init; } = "";
|
||||||
public string name { get; init; } = "";
|
public string name { get; init; } = "";
|
||||||
public string description { get; init; } = "";
|
public string description { get; init; } = "";
|
||||||
public bool published { get; init; }
|
public bool published { get; init; }
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ public class CanvasService : ICanvasService
|
|||||||
private const string BaseUrl = "https://snow.instructure.com/api/v1/";
|
private const string BaseUrl = "https://snow.instructure.com/api/v1/";
|
||||||
private readonly IWebRequestor webRequestor;
|
private readonly IWebRequestor webRequestor;
|
||||||
private string courseid { get; }
|
private string courseid { get; }
|
||||||
|
|
||||||
public CanvasService(IWebRequestor webRequestor)
|
public CanvasService(IWebRequestor webRequestor)
|
||||||
{
|
{
|
||||||
courseid = "774898";
|
courseid = "774898";
|
||||||
@@ -25,20 +26,40 @@ public class CanvasService : ICanvasService
|
|||||||
|
|
||||||
var request = new RestRequest(url);
|
var request = new RestRequest(url);
|
||||||
var termResponses = await PaginatedRequest<RedundantEnrollmentTermsResponse>(request);
|
var termResponses = await PaginatedRequest<RedundantEnrollmentTermsResponse>(request);
|
||||||
var terms = termResponses
|
var terms = termResponses.Select(r => r.EnrollmentTerms).SelectMany(s => s).ToArray();
|
||||||
.Select(r => r.EnrollmentTerms)
|
|
||||||
.SelectMany(s => s).ToArray();
|
|
||||||
return terms;
|
return terms;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<CourseModel>> GetCourses(ulong termId)
|
||||||
|
{
|
||||||
|
var url = $"courses";
|
||||||
|
var request = new RestRequest(url);
|
||||||
|
var coursesResponse = await PaginatedRequest<IEnumerable<CourseModel>>(request);
|
||||||
|
return coursesResponse.SelectMany(c => c).Where(c => c.EnrollmentTermId == termId).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<CourseModel> GetCourse(ulong courseId)
|
||||||
|
{
|
||||||
|
var url = $"course/${courseId}";
|
||||||
|
var request = new RestRequest(url);
|
||||||
|
var response = await webRequestor.GetAsync<CourseModel>(request);
|
||||||
|
|
||||||
|
if (response.Data == null)
|
||||||
|
{
|
||||||
|
System.Console.WriteLine(response.Content);
|
||||||
|
System.Console.WriteLine(response.ResponseUri);
|
||||||
|
throw new Exception("error getting course from canvas");
|
||||||
|
}
|
||||||
|
return response.Data;
|
||||||
|
}
|
||||||
|
|
||||||
private async Task<IEnumerable<T>> PaginatedRequest<T>(RestRequest request)
|
private async Task<IEnumerable<T>> PaginatedRequest<T>(RestRequest request)
|
||||||
{
|
{
|
||||||
var requestCount = 1;
|
var requestCount = 1;
|
||||||
request.AddQueryParameter("per_page", "100");
|
request.AddQueryParameter("per_page", "100");
|
||||||
RestResponse<T> response = await webRequestor.GetAsync<T>(request);
|
RestResponse<T> response = await webRequestor.GetAsync<T>(request);
|
||||||
var returnData = response.Data != null
|
|
||||||
? new T[] { response.Data }
|
var returnData = response.Data != null ? new T[] { response.Data } : new T[] { };
|
||||||
: new T[] { };
|
|
||||||
var nextUrl = getNextUrl(response.Headers);
|
var nextUrl = getNextUrl(response.Headers);
|
||||||
|
|
||||||
while (nextUrl is not null)
|
while (nextUrl is not null)
|
||||||
@@ -56,33 +77,32 @@ public class CanvasService : ICanvasService
|
|||||||
return returnData;
|
return returnData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static string? getNextUrl(IEnumerable<HeaderParameter>? headers) =>
|
||||||
private static string? getNextUrl(IEnumerable<HeaderParameter>? headers) => headers?
|
headers
|
||||||
.ToList()
|
?.ToList()
|
||||||
.Find(h => h.Name == "Link")?
|
.Find(h => h.Name == "Link")
|
||||||
.Value?
|
?.Value?.ToString()
|
||||||
.ToString()?
|
?.Split(",")
|
||||||
.Split(",")
|
|
||||||
.Where(url => url.Contains("rel=\"next\""))
|
.Where(url => url.Contains("rel=\"next\""))
|
||||||
.FirstOrDefault()?
|
.FirstOrDefault()
|
||||||
.Split(";")
|
?.Split(";")
|
||||||
.FirstOrDefault()?
|
.FirstOrDefault()
|
||||||
.TrimEnd('>')
|
?.TrimEnd('>')
|
||||||
.TrimStart('<')
|
.TrimStart('<')
|
||||||
.Replace(" ", "")
|
.Replace(" ", "")
|
||||||
.Replace(BaseUrl, "");
|
.Replace(BaseUrl, "");
|
||||||
|
|
||||||
public async Task<IEnumerable<EnrollmentTermModel>> GetCurrentTermsFor(DateTime? _queryDate = null)
|
public async Task<IEnumerable<EnrollmentTermModel>> GetCurrentTermsFor(
|
||||||
|
DateTime? _queryDate = null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
DateTime queryDate = _queryDate ?? DateTime.Now;
|
DateTime queryDate = _queryDate ?? DateTime.Now;
|
||||||
|
|
||||||
var terms = await GetTerms();
|
var terms = await GetTerms();
|
||||||
|
|
||||||
var currentTerms = terms.Where(t =>
|
var currentTerms = terms
|
||||||
t.EndAt != null
|
.Where(t => t.EndAt != null && t.EndAt > queryDate && t.EndAt < queryDate.AddYears(1))
|
||||||
&& t.EndAt > queryDate
|
.Take(3);
|
||||||
&& t.EndAt < queryDate.AddYears(1)
|
|
||||||
).Take(3);
|
|
||||||
|
|
||||||
return currentTerms;
|
return currentTerms;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,19 +5,22 @@ 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 string token;
|
private string token;
|
||||||
private RestClient client;
|
private RestClient client;
|
||||||
private string courseid { get; }
|
|
||||||
public WebRequestor()
|
public WebRequestor()
|
||||||
{
|
{
|
||||||
token = Environment.GetEnvironmentVariable("CANVAS_TOKEN") ?? throw new Exception("CANVAS_TOKEN not in environment");
|
token =
|
||||||
|
Environment.GetEnvironmentVariable("CANVAS_TOKEN")
|
||||||
|
?? throw new Exception("CANVAS_TOKEN not in environment");
|
||||||
client = new RestClient(BaseUrl);
|
client = new RestClient(BaseUrl);
|
||||||
client.AddDefaultHeader("Authorization", $"Bearer {token}");
|
client.AddDefaultHeader("Authorization", $"Bearer {token}");
|
||||||
|
|
||||||
courseid = "774898";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RestResponse<T[]>> GetManyAsync<T>(RestRequest request)
|
public async Task<RestResponse<T[]>> GetManyAsync<T>(RestRequest request)
|
||||||
{
|
{
|
||||||
return await client.ExecuteGetAsync<T[]>(request);
|
return await client.ExecuteGetAsync<T[]>(request);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<RestResponse<T>> GetAsync<T>(RestRequest request)
|
public async Task<RestResponse<T>> GetAsync<T>(RestRequest request)
|
||||||
{
|
{
|
||||||
return await client.ExecuteGetAsync<T>(request);
|
return await client.ExecuteGetAsync<T>(request);
|
||||||
|
|||||||
@@ -4,6 +4,16 @@ GET https://snow.instructure.com/api/v1/account_calendars
|
|||||||
Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
|
Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
|
||||||
|
|
||||||
###
|
###
|
||||||
GET https://snow.instructure.com/api/v1/accounts/10/terms
|
GET https://snow.instructure.com/api/v1/accounts/10/terms?per_page=100
|
||||||
Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
|
Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
GET https://snow.instructure.com/api/v1/courses?enrollment_term_id=751
|
||||||
|
Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
|
||||||
|
|
||||||
|
|
||||||
|
###
|
||||||
|
|
||||||
|
GET https://snow.instructure.com/api/v1/courses?enrollment_term_id=751&per_page=100
|
||||||
|
Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
|
||||||
Reference in New Issue
Block a user