referencing courses directly from canvas

This commit is contained in:
2023-07-15 23:19:39 -06:00
parent 5ece8b9d36
commit ed1963c67b
27 changed files with 370 additions and 249 deletions

5
.csharpierrc.json Normal file
View File

@@ -0,0 +1,5 @@
{
"printWidth": 100,
"useTabs": false,
"tabWidth": 2
}

View File

@@ -1,5 +1,6 @@
{ {
"recommendations": [ "recommendations": [
"alexkrechik.cucumberautocomplete" "alexkrechik.cucumberautocomplete",
"csharpier.csharpier-vscode"
] ]
} }

View File

@@ -1,6 +1,5 @@
{ {
"cucumberautocomplete.steps": [ "cucumberautocomplete.steps": [
"./Management.Gherkin/**/*.cs" "./Management.Gherkin/**/*.cs"
] ]
} }

View File

@@ -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;

View File

@@ -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);
} // }
} // }

View File

@@ -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
{ {

View File

@@ -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++;
}
}

View File

@@ -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>

View File

@@ -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;
@@ -83,42 +121,59 @@
@if (terms != null) @if (terms != null)
{ {
<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)
{ {
<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)))
{ {
<div class="col"> <div class="col">
<button class="@( <button class="@(
days.Contains(day) days.Contains(day)
? "btn btn-secondary" ? "btn btn-secondary"
: "btn btn-outline-secondary" : "btn btn-outline-secondary"
)" @onclick="() => { )" @onclick="() => {
if(days.Contains(day)) if(days.Contains(day))
days.Remove(day); days.Remove(day);
else else
days.Add(day); days.Add(day);
}" disabled="@saved"> }" disabled="@saved">
@day @day
</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>
} }

View File

@@ -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();

View File

@@ -4,7 +4,7 @@
[Parameter] [Parameter]
[Required] [Required]
public LocalAssignment assignment { get; set; } = new(); public LocalAssignment assignment { get; set; } = new();
private async Task HandleDragStart() private async Task HandleDragStart()
{ {
dragContainer.AssignmentBeingDragged = assignment; dragContainer.AssignmentBeingDragged = assignment;
System.Console.WriteLine("assignment set"); System.Console.WriteLine("assignment set");

View File

@@ -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();
} }
} }

View File

@@ -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);
} }
} }
} }

View File

@@ -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>

View File

@@ -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();
} }

View File

@@ -1,4 +1,5 @@
@inject AssignmentDragContainer dragContainer @inject AssignmentDragContainer dragContainer
@inject CoursePlanner configurationManagement
@code @code
{ {

View File

@@ -0,0 +1,4 @@
<div class="text-center m-3">
<span class="loader"></span>
</div>

View 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);
}
}

View 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);
}
}

View File

@@ -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;
} }

View File

@@ -1,8 +0,0 @@
using CanvasModel.EnrollmentTerms;
public interface IConfigurationManagement
{
SemesterCalendarConfig? SemesterCalendar { get; set; }
void SetConfiguration(EnrollmentTermModel canvasTerm, DayOfWeek[] daysOfWeek);
}

View File

@@ -1,6 +0,0 @@
public interface IModuleManager
{
IEnumerable<CourseModule> Modules { get; set; }
public void AddModule(CourseModule newModule);
public void AddAssignment(int moduleIndex, LocalAssignment assignment);
}

View File

@@ -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);
}
}

View File

@@ -1,31 +1,29 @@
public record RubricItem( public record RubricItem(int Points, string Label);
int Points,
string Label
);
public enum SubmissionType public enum SubmissionType
{ {
online_quiz, online_quiz,
none, none,
on_paper, on_paper,
discussion_topic, discussion_topic,
external_tool, external_tool,
online_upload, online_upload,
online_text_entry, online_text_entry,
online_url, online_url,
media_recording, media_recording,
student_annotation, student_annotation,
} }
public record LocalAssignment public record LocalAssignment
{ {
public string name { get; init; } = ""; public string id { get; init; } = "";
public string description { get; init; } = ""; public string name { get; init; } = "";
public bool published { get; init; } public string description { get; init; } = "";
public bool lock_at_due_date { get; init; } public bool published { get; init; }
public IEnumerable<RubricItem> rubric { get; init; } = new RubricItem[] { }; public bool lock_at_due_date { get; init; }
public DateTime? lock_at { get; init; } public IEnumerable<RubricItem> rubric { get; init; } = new RubricItem[] { };
public DateTime due_at { get; init; } public DateTime? lock_at { get; init; }
public int points_possible { get; init; } public DateTime due_at { get; init; }
public IEnumerable<SubmissionType> submission_types { get; init; } = new SubmissionType[] { }; public int points_possible { get; init; }
} public IEnumerable<SubmissionType> submission_types { get; init; } = new SubmissionType[] { };
}

View File

@@ -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,34 +77,33 @@ 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;
} }
} }

View File

@@ -5,21 +5,24 @@ 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);
} }
} }

View File

@@ -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}}