diff --git a/Management.Web/Pages/Index.razor b/Management.Web/Pages/Index.razor
index 7dc6434..2bfda58 100644
--- a/Management.Web/Pages/Index.razor
+++ b/Management.Web/Pages/Index.razor
@@ -103,6 +103,9 @@
>
View In Canvas
+
+ @planner.LocalCourse.Name
+
@if(planner.LoadingCanvasData)
diff --git a/Management.Web/Program.cs b/Management.Web/Program.cs
index 2f3bd64..0559d99 100644
--- a/Management.Web/Program.cs
+++ b/Management.Web/Program.cs
@@ -35,6 +35,7 @@ builder.Services.AddServerSideBlazor();
builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
+builder.Services.AddScoped();
builder.Services.AddScoped();
builder.Services.AddScoped();
diff --git a/Management.Web/Shared/Components/AssignmentForm/AssignmentForm.razor b/Management.Web/Shared/Components/AssignmentForm/AssignmentForm.razor
index ad04edf..ddcf275 100644
--- a/Management.Web/Shared/Components/AssignmentForm/AssignmentForm.razor
+++ b/Management.Web/Shared/Components/AssignmentForm/AssignmentForm.razor
@@ -25,9 +25,12 @@
assignmentContext.StateHasChanged -= reload;
}
-
- [Parameter]
- public Action OnHide { get; set; } = () => { };
+ private void OnHide()
+ {
+ assignmentContext.Assignment = null;
+ name = "";
+ lockAtDueDate = false;
+ }
public Modal? AssignmentModal { get; set; } = null;
private string name { get; set; } = String.Empty;
private bool lockAtDueDate { get; set; }
@@ -111,7 +114,7 @@
}
}
-
+
@assignmentContext.Assignment?.Name
diff --git a/Management.Web/Shared/Components/Modal.razor b/Management.Web/Shared/Components/Modal.razor
index dd6e676..ba1cc0c 100644
--- a/Management.Web/Shared/Components/Modal.razor
+++ b/Management.Web/Shared/Components/Modal.razor
@@ -14,6 +14,9 @@
[Parameter]
public Action OnHide { get; set; } = () => { };
+ [Parameter]
+ public string Size { get; set; } = "xl"; //sm, lg, xl, xxl
+
private string modalClass = "hide-modal";
private bool showBackdrop = false;
public void Show()
@@ -33,7 +36,7 @@
}
-
+
}
+
_assignment;
set
{
- Console.WriteLine("saving");
_assignment = value;
StateHasChanged?.Invoke();
}
diff --git a/Management/Features/Configuration/CoursePlanner.cs b/Management/Features/Configuration/CoursePlanner.cs
index 19bd0b1..9c5498a 100644
--- a/Management/Features/Configuration/CoursePlanner.cs
+++ b/Management/Features/Configuration/CoursePlanner.cs
@@ -72,6 +72,7 @@ public class CoursePlanner
public event Action? StateHasChanged;
public IEnumerable? CanvasAssignments { get; internal set; }
+ public IEnumerable? CanvasAssignmentGroups { get; internal set; }
public IEnumerable? CanvasQuizzes { get; internal set; }
public IEnumerable? CanvasModules { get; internal set; }
public Dictionary>? CanvasModulesItems { get; internal set; }
@@ -80,7 +81,8 @@ public class CoursePlanner
IEnumerable CanvasAssignments,
IEnumerable CanvasModules,
Dictionary> CanvasModulesItems,
- IEnumerable canvasQuizzes
+ IEnumerable canvasQuizzes,
+ IEnumerable canvasAssignmentGroups
)> LoadCanvasData()
{
LoadingCanvasData = true;
@@ -92,16 +94,18 @@ public class CoursePlanner
var assignmentsTask = canvas.Assignments.GetAll(canvasId);
var quizzesTask = canvas.Quizzes.GetAll(canvasId);
var modulesTask = canvas.GetModules(canvasId);
+ var assignmentGroupsTask = canvas.AssignmentGroups.GetAll(canvasId);
CanvasAssignments = await assignmentsTask;
CanvasQuizzes = await quizzesTask;
CanvasModules = await modulesTask;
+ CanvasAssignmentGroups = await assignmentGroupsTask;
CanvasModulesItems = await canvas.GetAllModulesItems(canvasId, CanvasModules);
LoadingCanvasData = false;
StateHasChanged?.Invoke();
- return (CanvasAssignments, CanvasModules, CanvasModulesItems, CanvasQuizzes);
+ return (CanvasAssignments, CanvasModules, CanvasModulesItems, CanvasQuizzes, CanvasAssignmentGroups);
}
public async Task SyncWithCanvas()
@@ -119,18 +123,31 @@ public class CoursePlanner
LoadingCanvasData = true;
StateHasChanged?.Invoke();
- var (canvasAssignments, canvasModules, canvasModuleItems, canvasQuizzes) = await LoadCanvasData();
+ var (
+ canvasAssignments,
+ canvasModules,
+ canvasModuleItems,
+ canvasQuizzes,
+ canvasAssignmentGroups
+ ) = await LoadCanvasData();
+
LoadingCanvasData = true;
StateHasChanged?.Invoke();
LocalCourse = LocalCourse.deleteCanvasIdsThatNoLongerExist(
canvasModules,
canvasAssignments,
+ canvasAssignmentGroups,
canvasQuizzes
);
var canvasId =
LocalCourse.CanvasId ?? throw new Exception("no course canvas id to sync with canvas");
+ var newAssignmentGroups = await LocalCourse.EnsureAllAssignmentGroupsExistInCanvas(
+ canvasId, canvasAssignmentGroups, canvas);
+ LocalCourse = LocalCourse with { AssignmentGroups = newAssignmentGroups };
+
+
var newModules = await LocalCourse.EnsureAllModulesExistInCanvas(
canvasId,
CanvasModules,
diff --git a/Management/Features/Configuration/CoursePlannerValidationExtensions.cs b/Management/Features/Configuration/CoursePlannerValidationExtensions.cs
index 96b5323..2850b91 100644
--- a/Management/Features/Configuration/CoursePlannerValidationExtensions.cs
+++ b/Management/Features/Configuration/CoursePlannerValidationExtensions.cs
@@ -47,6 +47,7 @@ public static class CoursePlannerExtensions
this LocalCourse localCourse,
IEnumerable canvasModules,
IEnumerable canvasAssignments,
+ IEnumerable canvasAssignmentGroups,
IEnumerable canvasQuizzes
)
{
@@ -56,9 +57,21 @@ public static class CoursePlannerExtensions
.Select((m) => m.validateCanvasIds(canvasModules, canvasAssignments, canvasQuizzes))
.ToArray();
+ var canvasAssignmentGroupIds = canvasAssignmentGroups.Select(g => g.Id).ToArray();
+ var correctAssignmentGroups = localCourse.AssignmentGroups.Select(
+ g =>
+ {
+ var groupCanvasId = g.CanvasId ?? 0;
+ return canvasAssignmentGroupIds.Contains(groupCanvasId)
+ ? g
+ : g with { CanvasId = null };
+ }
+ ).ToArray();
+
return localCourse with
{
- Modules = correctedModules
+ Modules = correctedModules,
+ AssignmentGroups = correctAssignmentGroups,
};
}
diff --git a/Management/Features/Configuration/Synchronization/AssignemntGroupSyncronizationExtensions.cs b/Management/Features/Configuration/Synchronization/AssignemntGroupSyncronizationExtensions.cs
new file mode 100644
index 0000000..717fa19
--- /dev/null
+++ b/Management/Features/Configuration/Synchronization/AssignemntGroupSyncronizationExtensions.cs
@@ -0,0 +1,40 @@
+using CanvasModel.Assignments;
+using CanvasModel.Modules;
+using LocalModels;
+using Management.Services.Canvas;
+
+namespace Management.Planner;
+
+public static partial class AssignmentGroupSyncronizationExtensions
+{
+ internal static async Task> EnsureAllAssignmentGroupsExistInCanvas(
+ this LocalCourse localCourse,
+ ulong courseCanvasId,
+ IEnumerable canvasAssignmentGroups,
+ CanvasService canvas
+ )
+ {
+ var canvasAssignmentGroupIds = canvasAssignmentGroups.Select(g => g.Id).ToArray();
+ var assignmentGroups = await Task.WhenAll((Task[])localCourse.AssignmentGroups.Select(
+ async assignmentGroup =>
+ {
+ var canvasGroupWithSameName = canvasAssignmentGroups.FirstOrDefault(
+ cg => cg.Name.Equals(assignmentGroup.Name)
+ );
+ if (canvasGroupWithSameName == null)
+ return await canvas.AssignmentGroups.Create(courseCanvasId, assignmentGroup);
+
+ var correctGroup = assignmentGroup with { CanvasId = canvasGroupWithSameName.Id };
+
+ var needsUpdate = canvasGroupWithSameName.GroupWeight != correctGroup.Weight;
+
+ if (needsUpdate)
+ await canvas.AssignmentGroups.Update(courseCanvasId, assignmentGroup);
+
+ return correctGroup;
+ }
+ ).ToArray());
+
+ return assignmentGroups;
+ }
+}
\ No newline at end of file
diff --git a/Management/Models/CanvasModels/Assignments/CanvasAssignmentGroup.cs b/Management/Models/CanvasModels/Assignments/CanvasAssignmentGroup.cs
new file mode 100644
index 0000000..d229a51
--- /dev/null
+++ b/Management/Models/CanvasModels/Assignments/CanvasAssignmentGroup.cs
@@ -0,0 +1,29 @@
+
+namespace CanvasModel.Assignments;
+
+public record CanvasAssignmentGroup
+{
+ [JsonPropertyName("id")]
+ public ulong Id { get; init; }
+
+ [JsonPropertyName("name")]
+ public required string Name { get; init; }
+
+ [JsonPropertyName("position")]
+ public int Position { get; init; }
+
+ [JsonPropertyName("group_weight")]
+ public double GroupWeight { get; init; }
+
+ // [JsonPropertyName("sis_source_id")]
+ // public string? SisSourceId { get; init; } = null;
+
+ // [JsonPropertyName("integration_data")]
+ // public Dictionary IntegrationData { get; init; } = new Dictionary();
+
+ // [JsonPropertyName("assignments")]
+ // public List Assignments { get; init; }
+
+ // [JsonPropertyName("rules")]
+ // public object Rules { get; init; } // The specific type for 'Rules' is not detailed in the spec, so using object for now.
+}
\ No newline at end of file
diff --git a/Management/Models/Local/LocalAssignmentGroup.cs b/Management/Models/Local/LocalAssignmentGroup.cs
new file mode 100644
index 0000000..8c13c66
--- /dev/null
+++ b/Management/Models/Local/LocalAssignmentGroup.cs
@@ -0,0 +1,9 @@
+namespace LocalModels;
+
+public record LocalAssignmentGroup
+{
+ public ulong? CanvasId { get; init; }
+ public string Id { get; init; } = string.Empty;
+ public required string Name { get; init; }
+ public double Weight { get; init; }
+}
\ No newline at end of file
diff --git a/Management/Models/Local/LocalCourse.cs b/Management/Models/Local/LocalCourse.cs
index 5749e4d..2e779fc 100644
--- a/Management/Models/Local/LocalCourse.cs
+++ b/Management/Models/Local/LocalCourse.cs
@@ -11,6 +11,8 @@ public record LocalCourse
public SimpleTimeOnly DefaultDueTime { get; init; } = new SimpleTimeOnly();
public IEnumerable AssignmentTemplates { get; init; } =
Enumerable.Empty();
+ public IEnumerable AssignmentGroups { get; init; } =
+ Enumerable.Empty();
}
public record SimpleTimeOnly
diff --git a/Management/Services/Canvas/CanvasAssignmentGroupService.cs b/Management/Services/Canvas/CanvasAssignmentGroupService.cs
new file mode 100644
index 0000000..050e450
--- /dev/null
+++ b/Management/Services/Canvas/CanvasAssignmentGroupService.cs
@@ -0,0 +1,70 @@
+using CanvasModel.Assignments;
+using LocalModels;
+using RestSharp;
+
+namespace Management.Services.Canvas;
+
+public class CanvasAssignmentGroupService
+{
+ private readonly IWebRequestor webRequestor;
+ private readonly CanvasServiceUtils utils;
+
+ public CanvasAssignmentGroupService(IWebRequestor webRequestor, CanvasServiceUtils utils)
+ {
+ this.webRequestor = webRequestor;
+ this.utils = utils;
+ }
+ public async Task> GetAll(ulong courseId)
+ {
+ var url = $"courses/{courseId}/assignment_groups";
+ var request = new RestRequest(url);
+ var assignmentResponse = await utils.PaginatedRequest>(request);
+ return assignmentResponse.SelectMany(
+ assignments => assignments
+ );
+ }
+
+ public async Task Create(
+ ulong canvasCourseId,
+ LocalAssignmentGroup localAssignmentGroup
+ )
+ {
+ Console.WriteLine($"creating assignment group: {localAssignmentGroup.Name}");
+ var url = $"courses/{canvasCourseId}/assignment_groups";
+ var request = new RestRequest(url);
+ var body = new
+ {
+ name = localAssignmentGroup.Name,
+ group_weight = localAssignmentGroup.Weight,
+ };
+ request.AddBody(body);
+
+ var (canvasAssignmentGroup, response) = await webRequestor.PostAsync(request);
+ if (canvasAssignmentGroup == null)
+ throw new Exception("created canvas assignment group was null");
+
+ return localAssignmentGroup with
+ {
+ CanvasId = canvasAssignmentGroup.Id
+ };
+ }
+ public async Task Update(
+ ulong canvasCourseId,
+ LocalAssignmentGroup localAssignmentGroup
+ )
+ {
+ Console.WriteLine($"updating assignment group: {localAssignmentGroup.Name}");
+ if (localAssignmentGroup.CanvasId == null)
+ throw new Exception("cannot update assignment group if canvas id is null");
+ var url = $"courses/{canvasCourseId}/assignment_groups/{localAssignmentGroup.CanvasId}";
+ var request = new RestRequest(url);
+ var body = new
+ {
+ name = localAssignmentGroup.Name,
+ group_weight = localAssignmentGroup.Weight,
+ };
+ request.AddBody(body);
+
+ await webRequestor.PutAsync(request);
+ }
+}
\ No newline at end of file
diff --git a/Management/Services/Canvas/CanvasService.cs b/Management/Services/Canvas/CanvasService.cs
index 2edf73e..e231cd5 100644
--- a/Management/Services/Canvas/CanvasService.cs
+++ b/Management/Services/Canvas/CanvasService.cs
@@ -13,18 +13,21 @@ public class CanvasService
private readonly CanvasServiceUtils utils;
public CanvasAssignmentService Assignments { get; }
+ public CanvasAssignmentGroupService AssignmentGroups { get; }
public CanvasQuizService Quizzes { get; }
public CanvasService(
IWebRequestor webRequestor,
CanvasServiceUtils utils,
CanvasAssignmentService Assignments,
+ CanvasAssignmentGroupService AssignmentGroups,
CanvasQuizService Quizzes
)
{
this.webRequestor = webRequestor;
this.utils = utils;
this.Assignments = Assignments;
+ this.AssignmentGroups = AssignmentGroups;
this.Quizzes = Quizzes;
}
diff --git a/README.md b/README.md
index 8b79366..3f89611 100644
--- a/README.md
+++ b/README.md
@@ -17,3 +17,5 @@ Apparently the VSCode razor extension was compiled with a preview of dotnet 6...
The issue can be tracked [here](https://github.com/dotnet/razor/issues/6241)
+
+I am looking for a TA and grader for my CS 1410/1415 class. The labs are tuesdays at 7:30 - 9:30 am. Anyone who is interested, please message me on teams or send an email to alex.mickelson@snow.edu.
\ No newline at end of file
diff --git a/requests/assignment.http b/requests/assignment.http
index 8ba6c7d..6fcb690 100644
--- a/requests/assignment.http
+++ b/requests/assignment.http
@@ -128,4 +128,11 @@ Content-Type: application/json
###
GET https://snow.instructure.com/api/v1/courses/872095/assignments/12676639?include[]=overrides
+Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
+
+###
+GET https://snow.instructure.com/api/v1/courses/871954/assignment_groups
+Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
+###
+GET https://snow.instructure.com/api/v1/courses/871954
Authorization: Bearer {{$dotenv CANVAS_TOKEN}}
\ No newline at end of file