mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 07:38:33 -06:00
hooks
This commit is contained in:
@@ -79,4 +79,4 @@ public class CalendarMonth
|
||||
Year = year;
|
||||
Month = month;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
using CanvasModel.EnrollmentTerms;
|
||||
using CanvasModel.Courses;
|
||||
using CanvasModel;
|
||||
using LocalModels;
|
||||
using CanvasModel.Assignments;
|
||||
using CanvasModel.Modules;
|
||||
using Management.Services.Canvas;
|
||||
using System.Text.RegularExpressions;
|
||||
using CanvasModel.Quizzes;
|
||||
using Management.Services;
|
||||
using CanvasModel;
|
||||
using CanvasModel.Assignments;
|
||||
using CanvasModel.Courses;
|
||||
using CanvasModel.EnrollmentTerms;
|
||||
using CanvasModel.Modules;
|
||||
using CanvasModel.Pages;
|
||||
using CanvasModel.Quizzes;
|
||||
using LocalModels;
|
||||
using Management.Services;
|
||||
using Management.Services.Canvas;
|
||||
|
||||
namespace Management.Planner;
|
||||
|
||||
@@ -181,13 +181,17 @@ public class CoursePlanner
|
||||
|
||||
CanvasAssignmentGroups = await canvas.AssignmentGroups.GetAll(canvasCourseId);
|
||||
|
||||
LocalCourse = LocalCourse with {Settings = LocalCourse.Settings with {
|
||||
AssignmentGroups = LocalCourse.Settings.AssignmentGroups.Select(g => {
|
||||
var canvasGroup = CanvasAssignmentGroups.FirstOrDefault(c => c.Name == g.Name);
|
||||
return canvasGroup == null
|
||||
? g
|
||||
: g with {CanvasId = canvasGroup.Id};
|
||||
})
|
||||
LocalCourse = LocalCourse with
|
||||
{
|
||||
Settings = LocalCourse.Settings with
|
||||
{
|
||||
AssignmentGroups = LocalCourse.Settings.AssignmentGroups.Select(g =>
|
||||
{
|
||||
var canvasGroup = CanvasAssignmentGroups.FirstOrDefault(c => c.Name == g.Name);
|
||||
return canvasGroup == null
|
||||
? g
|
||||
: g with { CanvasId = canvasGroup.Id };
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -112,7 +112,7 @@ public class PageEditorContext(
|
||||
|
||||
var canvasModule = getCurrentCanvasModule(Page, planner.LocalCourse);
|
||||
|
||||
if(canvasPage != null)
|
||||
if (canvasPage != null)
|
||||
{
|
||||
await canvas.CreatePageModuleItem(
|
||||
(ulong)courseCanvasId,
|
||||
|
||||
@@ -37,4 +37,4 @@ public static partial class AssignmentGroupSyncronizationExtensions
|
||||
|
||||
return assignmentGroups;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -140,13 +140,13 @@ public static partial class AssignmentSyncronizationExtensions
|
||||
second: localAssignment.LockAt.Value.Second
|
||||
)
|
||||
: null;
|
||||
|
||||
|
||||
if (canvasComparisonLockDate != localComparisonLockDate)
|
||||
{
|
||||
var printableLocal = localComparisonLockDate?.ToString() ?? "null";
|
||||
var printableCanvas = canvasComparisonLockDate?.ToString() ?? "null";
|
||||
var reason = $"Lock dates different for assignment {localAssignment.Name}, local: {printableLocal}, in canvas {printableCanvas}";
|
||||
|
||||
|
||||
if (!quiet)
|
||||
{
|
||||
// Console.WriteLine(JsonSerializer.Serialize(canvasAssignment));
|
||||
|
||||
@@ -39,14 +39,15 @@ public static partial class ModuleSyncronizationExtensions
|
||||
{
|
||||
var canvasModuleItems = await canvas.Modules.GetModuleItems(canvasId, moduleCanvasId);
|
||||
var moduleItemsInCorrectOrder = canvasModuleItems
|
||||
.OrderBy(canvasItem => {
|
||||
.OrderBy(canvasItem =>
|
||||
{
|
||||
|
||||
if(canvasItem.Type == "Page")
|
||||
if (canvasItem.Type == "Page")
|
||||
{
|
||||
var localPage = localModule.Pages.FirstOrDefault(p => p.Name == canvasItem.Title);
|
||||
Console.WriteLine(JsonSerializer.Serialize(localModule.Pages));
|
||||
|
||||
if(localPage != null)
|
||||
if (localPage != null)
|
||||
return localPage.DueAt.Date;
|
||||
}
|
||||
return canvasItem.ContentDetails?.DueAt?.Date;
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
global using System.Text.Json.Serialization;
|
||||
global using System.Text.Json;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
global using System.Text.Json.Serialization;
|
||||
global using Microsoft.Extensions.Logging;
|
||||
|
||||
@@ -70,7 +70,7 @@ public record CanvasAssignment
|
||||
|
||||
[property: JsonPropertyName("allowed_attempts")]
|
||||
int AllowedAttempts,
|
||||
|
||||
|
||||
[property: JsonPropertyName("is_quiz_assignment")]
|
||||
bool IsQuizAssignment,
|
||||
|
||||
@@ -208,4 +208,4 @@ public record CanvasAssignment
|
||||
|
||||
[property: JsonPropertyName("anonymous_grading")]
|
||||
bool? AnonymousGrading = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -19,4 +19,4 @@ public record CanvasAssignmentDate
|
||||
|
||||
[property: JsonPropertyName("lock_at")]
|
||||
DateTime? LockAt = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -26,4 +26,4 @@ public record CanvasAssignmentGroup
|
||||
|
||||
// [JsonPropertyName("rules")]
|
||||
// public object Rules { get; init; } // The specific type for 'Rules' is not detailed in the spec, so using object for now.
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,4 +35,4 @@ public record CanvasAssignmentOverride
|
||||
|
||||
[property: JsonPropertyName("lock_at")]
|
||||
DateTime? LockAt = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -10,4 +10,4 @@ public record CanvasExternalToolTagAttributes
|
||||
|
||||
[property: JsonPropertyName("new_tab")]
|
||||
bool? NewTab = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -16,4 +16,4 @@ public record CanvasLockInfo
|
||||
|
||||
[property: JsonPropertyName("manually_locked")]
|
||||
bool? ManuallyLocked = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -7,4 +7,4 @@ public record CanvasNeedsGradingCount
|
||||
|
||||
[property: JsonPropertyName("needs_grading_count")]
|
||||
uint NeedsGradingCount
|
||||
);
|
||||
);
|
||||
|
||||
@@ -34,4 +34,4 @@ public record CanvasRubric
|
||||
|
||||
// assessments
|
||||
// associations
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,4 +32,4 @@ public record CanvasRubricAssociation
|
||||
[JsonPropertyName("hide_outcome-results")]
|
||||
public bool HideOUtcomeResult { get; set; }
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -27,4 +27,4 @@ public record CanvasRubricCriteria
|
||||
|
||||
[property: JsonPropertyName("ignore_for_scoring")]
|
||||
bool? IgnoreForScoring = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -13,4 +13,4 @@ public record CanvasRubricRating
|
||||
|
||||
[property: JsonPropertyName("long_description")]
|
||||
string LongDescription
|
||||
);
|
||||
);
|
||||
|
||||
@@ -25,4 +25,4 @@ public record CanvasTurnitinSettings
|
||||
|
||||
[property: JsonPropertyName("exclude_small_matches_value")]
|
||||
uint? ExcludeSmallMatchesValue = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -2,4 +2,4 @@ namespace CanvasModel.Courses;
|
||||
public record CalendarLinkModel
|
||||
(
|
||||
[property: JsonPropertyName("ics")] string Ics
|
||||
);
|
||||
);
|
||||
|
||||
@@ -2,15 +2,15 @@
|
||||
namespace CanvasModel.Courses;
|
||||
public record CourseProgressModel
|
||||
(
|
||||
[property: JsonPropertyName("requirement_count")]
|
||||
[property: JsonPropertyName("requirement_count")]
|
||||
uint? RequirementCount = null,
|
||||
|
||||
[property: JsonPropertyName("requirement_completed_count")]
|
||||
|
||||
[property: JsonPropertyName("requirement_completed_count")]
|
||||
uint? RequirementCompletedCount = null,
|
||||
|
||||
[property: JsonPropertyName("next_requirement_url")]
|
||||
[property: JsonPropertyName("next_requirement_url")]
|
||||
string? NextRequirementUrl = null,
|
||||
|
||||
[property: JsonPropertyName("completed_at")]
|
||||
[property: JsonPropertyName("completed_at")]
|
||||
DateTime? CompletedAt = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -108,4 +108,4 @@ public record DiscussionTopicModel
|
||||
|
||||
[property: JsonPropertyName("sort_by_rating")]
|
||||
bool? SortByRating = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -13,4 +13,4 @@ public record FileAttachmentModel
|
||||
|
||||
[property: JsonPropertyName("display_name")]
|
||||
string DisplayName
|
||||
);
|
||||
);
|
||||
|
||||
@@ -37,4 +37,4 @@ public record TopicEntryModel
|
||||
|
||||
[property: JsonPropertyName("has_more_replies")]
|
||||
bool? HasMoreReplies = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -25,4 +25,4 @@ public record TopicReplyModel
|
||||
|
||||
[property: JsonPropertyName("forced_read_state")]
|
||||
bool? ForcedReadState = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -2,6 +2,6 @@ namespace CanvasModel.EnrollmentTerms;
|
||||
|
||||
public record RedundantEnrollmentTermsResponse
|
||||
(
|
||||
[property: JsonPropertyName("enrollment_terms")]
|
||||
[property: JsonPropertyName("enrollment_terms")]
|
||||
IEnumerable<EnrollmentTermModel> EnrollmentTerms
|
||||
);
|
||||
|
||||
@@ -132,4 +132,4 @@ public record EnrollmentModel
|
||||
|
||||
[property: JsonPropertyName("current_period_unposted_final_grade")]
|
||||
string? CurrentPeriodUnpostedFinalGrade = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -30,4 +30,4 @@ public record GradeModel
|
||||
|
||||
[property: JsonPropertyName("unposted_final_score")]
|
||||
string? UnpostedFinalScore = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -15,7 +15,7 @@ public record CanvasModule(
|
||||
[property: JsonPropertyName("items")]
|
||||
IEnumerable<CanvasModuleItem>? Items,
|
||||
[property: JsonPropertyName("state")] string? State, // todo make sure this,
|
||||
// [OptIn]
|
||||
// [OptIn]
|
||||
[property: JsonPropertyName("completed_at")]
|
||||
DateTime? CompletedAt,
|
||||
[property: JsonPropertyName("publish_final_grade")] bool? PublishFinalGrade,
|
||||
|
||||
@@ -25,4 +25,4 @@ public record CanvasModuleItemContentDetails(
|
||||
[property: JsonPropertyName("lock_at")] DateTime? LockAt,
|
||||
[property: JsonPropertyName("points_possible")] double PointsPossible,
|
||||
[property: JsonPropertyName("locked_for_user")] bool LockedForUser
|
||||
);
|
||||
);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
namespace CanvasModel.Pages;
|
||||
public record CanvasPage (
|
||||
public record CanvasPage(
|
||||
[property: JsonPropertyName("page_id")] ulong PageId,
|
||||
[property: JsonPropertyName("url")] string Url,
|
||||
[property: JsonPropertyName("title")] string Title,
|
||||
|
||||
@@ -16,4 +16,4 @@ public record MediaCommentModel
|
||||
|
||||
[property: JsonPropertyName("url")]
|
||||
string Url
|
||||
);
|
||||
);
|
||||
|
||||
@@ -26,4 +26,4 @@ public record SubmissionCommentModel
|
||||
|
||||
[property: JsonPropertyName("media_comment")]
|
||||
MediaCommentModel? MediaComment = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -88,4 +88,4 @@ public record SubmissionModel
|
||||
|
||||
[property: JsonPropertyName("anonymous_id")]
|
||||
string? AnonymousId = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -160,4 +160,4 @@ public record ActivityStreamObjectModel
|
||||
|
||||
[property: JsonPropertyName("assignment_request_id")]
|
||||
ulong? AssignmentRequestId = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -12,4 +12,4 @@ public record ActivityStreamSummaryEntryModel
|
||||
|
||||
[property: JsonPropertyName("count")]
|
||||
uint Count
|
||||
);
|
||||
);
|
||||
|
||||
@@ -7,4 +7,4 @@ public record AnonymousUserDisplayModel
|
||||
|
||||
[property: JsonPropertyName("avatar_image_url")]
|
||||
string AvatarImageUrl
|
||||
);
|
||||
);
|
||||
|
||||
@@ -25,4 +25,4 @@ public record AvatarModel
|
||||
|
||||
[property: JsonPropertyName("size")]
|
||||
ulong Size
|
||||
);
|
||||
);
|
||||
|
||||
@@ -10,4 +10,4 @@ public record CourseNicknameModel
|
||||
|
||||
[property: JsonPropertyName("nickname")]
|
||||
string Nickname
|
||||
);
|
||||
);
|
||||
|
||||
@@ -16,4 +16,4 @@ public record PageViewLinksModel
|
||||
|
||||
[property: JsonPropertyName("account")]
|
||||
ulong? Account = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -50,4 +50,4 @@ public record PageViewModel
|
||||
|
||||
[property: JsonPropertyName("participated")]
|
||||
bool? Participated = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -43,4 +43,4 @@ public record ProfileModel
|
||||
|
||||
[property: JsonPropertyName("locale")]
|
||||
string Locale
|
||||
);
|
||||
);
|
||||
|
||||
@@ -22,4 +22,4 @@ public record UserDisplayModel
|
||||
|
||||
[property: JsonPropertyName("pronouns")]
|
||||
string? Pronouns = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -53,4 +53,4 @@ public record UserModel
|
||||
|
||||
[property: JsonPropertyName("last_login")]
|
||||
DateTime? LastLogin = null
|
||||
);
|
||||
);
|
||||
|
||||
@@ -7,4 +7,4 @@ public class RubricMarkdownParseException : Exception
|
||||
public class AssignmentMarkdownParseException : Exception
|
||||
{
|
||||
public AssignmentMarkdownParseException(string message) : base(message) { }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,5 +26,5 @@ public record AssignmentTemplate
|
||||
// }
|
||||
// return html;
|
||||
// }
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@ using YamlDotNet.Serialization;
|
||||
|
||||
namespace LocalModels;
|
||||
|
||||
public record LocalAssignment: IModuleItem
|
||||
public record LocalAssignment : IModuleItem
|
||||
{
|
||||
private string _name = "";
|
||||
public string Name
|
||||
|
||||
@@ -6,4 +6,4 @@ public record LocalAssignmentGroup
|
||||
public string Id { get; init; } = string.Empty;
|
||||
public required string Name { get; init; }
|
||||
public double Weight { get; init; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace LocalModels;
|
||||
|
||||
public record LocalCoursePage: IModuleItem
|
||||
public record LocalCoursePage : IModuleItem
|
||||
{
|
||||
public required string Name { get; init; }
|
||||
public required string Text { get; set; }
|
||||
|
||||
@@ -3,7 +3,7 @@ using YamlDotNet.Serialization;
|
||||
|
||||
namespace LocalModels;
|
||||
|
||||
public record LocalQuiz: IModuleItem
|
||||
public record LocalQuiz : IModuleItem
|
||||
{
|
||||
|
||||
public required string Name { get; init; }
|
||||
@@ -171,7 +171,7 @@ Description: {Description}
|
||||
|
||||
public class QuizMarkdownParseException : Exception
|
||||
{
|
||||
public QuizMarkdownParseException(string message): base(message)
|
||||
public QuizMarkdownParseException(string message) : base(message)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ public record LocalQuizQuestion
|
||||
|
||||
return $"{questionTypeIndicator}{multilineMarkdownCompatibleText}";
|
||||
}
|
||||
else if(QuestionType == "matching")
|
||||
else if (QuestionType == "matching")
|
||||
{
|
||||
return $"^ {answer.Text} - {answer.MatchedText}";
|
||||
}
|
||||
|
||||
@@ -63,10 +63,12 @@ public class CanvasModuleService
|
||||
if (items == null)
|
||||
throw new Exception($"Error getting canvas module items for {url}");
|
||||
return items.Select(i =>
|
||||
i with {
|
||||
i with
|
||||
{
|
||||
ContentDetails = i.ContentDetails == null
|
||||
? null
|
||||
: i.ContentDetails with {
|
||||
: i.ContentDetails with
|
||||
{
|
||||
DueAt = i.ContentDetails.DueAt?.ToLocalTime(),
|
||||
LockAt = i.ContentDetails.LockAt?.ToLocalTime(),
|
||||
}
|
||||
|
||||
@@ -117,14 +117,16 @@ public class CanvasQuizService(
|
||||
await Task.WhenAll(tasks);
|
||||
}
|
||||
|
||||
private async Task hackFixQuestionOrdering(ulong canvasCourseId, ulong canvasQuizId, IEnumerable<(CanvasQuizQuestion question, int position)> questionAndPositions )
|
||||
private async Task hackFixQuestionOrdering(ulong canvasCourseId, ulong canvasQuizId, IEnumerable<(CanvasQuizQuestion question, int position)> questionAndPositions)
|
||||
{
|
||||
using var activity = DiagnosticsConfig.Source.StartActivity("hack fixing question ordering with reorder");
|
||||
activity?.SetCustomProperty("canvasQuizId", canvasQuizId);
|
||||
activity?.SetTag("canvas syncronization", true);
|
||||
|
||||
var order = questionAndPositions.OrderBy(t => t.position).Select(tuple => {
|
||||
return new {
|
||||
var order = questionAndPositions.OrderBy(t => t.position).Select(tuple =>
|
||||
{
|
||||
return new
|
||||
{
|
||||
type = "question",
|
||||
id = tuple.question.Id.ToString(),
|
||||
};
|
||||
@@ -182,9 +184,10 @@ public class CanvasQuizService(
|
||||
|
||||
private static object[] getAnswers(LocalQuizQuestion q)
|
||||
{
|
||||
if(q.QuestionType == QuestionType.MATCHING)
|
||||
if (q.QuestionType == QuestionType.MATCHING)
|
||||
return q.Answers
|
||||
.Select(a => new {
|
||||
.Select(a => new
|
||||
{
|
||||
answer_match_left = a.Text,
|
||||
answer_match_right = a.MatchedText
|
||||
})
|
||||
|
||||
@@ -1,11 +1,11 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using CanvasModel;
|
||||
using CanvasModel.Assignments;
|
||||
using CanvasModel.Courses;
|
||||
using CanvasModel.EnrollmentTerms;
|
||||
using CanvasModel.Modules;
|
||||
using RestSharp;
|
||||
using CanvasModel.Pages;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using RestSharp;
|
||||
|
||||
namespace Management.Services.Canvas;
|
||||
|
||||
|
||||
@@ -14,4 +14,4 @@ public class FileConfiguration(IConfiguration config)
|
||||
return basePath;
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ public class FileStorageManager
|
||||
|
||||
public IEnumerable<string> GetEmptyDirectories()
|
||||
{
|
||||
if(!Directory.Exists(_basePath))
|
||||
if (!Directory.Exists(_basePath))
|
||||
throw new DirectoryNotFoundException($"Cannot get empty directories, {_basePath} does not exist");
|
||||
|
||||
return Directory
|
||||
|
||||
@@ -25,7 +25,7 @@ public class CourseMarkdownLoader
|
||||
})
|
||||
.Select(async d => await LoadCourseByPath(d))
|
||||
);
|
||||
return courses.OrderBy(c=>c.Settings.Name);
|
||||
return courses.OrderBy(c => c.Settings.Name);
|
||||
}
|
||||
|
||||
public async Task<LocalCourse> LoadCourseByPath(string courseDirectory)
|
||||
|
||||
@@ -45,12 +45,15 @@ public class WebRequestor : IWebRequestor
|
||||
|
||||
var response = await client.ExecutePostAsync(request);
|
||||
|
||||
if (isRateLimited(response)) {
|
||||
if(retryCount < rateLimitRetryCount){
|
||||
if (isRateLimited(response))
|
||||
{
|
||||
if (retryCount < rateLimitRetryCount)
|
||||
{
|
||||
logger.LogInformation($"hit rate limit on post, retry count is {retryCount} / {rateLimitRetryCount}, retrying");
|
||||
Console.WriteLine($"hit rate limit on post, retry count is {retryCount} / {rateLimitRetryCount}, retrying");
|
||||
Thread.Sleep(rateLimitSleepInterval);
|
||||
return await rateLimitAwarePostAsync(request, retryCount + 1);}
|
||||
return await rateLimitAwarePostAsync(request, retryCount + 1);
|
||||
}
|
||||
}
|
||||
|
||||
if (!response.IsSuccessful)
|
||||
@@ -63,7 +66,7 @@ public class WebRequestor : IWebRequestor
|
||||
|
||||
private static bool isRateLimited(RestResponse response)
|
||||
{
|
||||
if(response.Content == null)
|
||||
if (response.Content == null)
|
||||
return false;
|
||||
return response.StatusCode == HttpStatusCode.Forbidden
|
||||
&& response.Content.Contains("403 Forbidden (Rate Limit Exceeded)");
|
||||
@@ -110,7 +113,7 @@ public class WebRequestor : IWebRequestor
|
||||
{
|
||||
if (e.StatusCode == HttpStatusCode.Forbidden) // && response.Content == "403 Forbidden (Rate Limit Exceeded)"
|
||||
{
|
||||
if(retryCount < rateLimitRetryCount)
|
||||
if (retryCount < rateLimitRetryCount)
|
||||
{
|
||||
logger.LogInformation($"hit rate limit in delete, retry count is {retryCount} / {rateLimitRetryCount}, retrying");
|
||||
Console.WriteLine($"hit rate limit in delete, retry count is {retryCount} / {rateLimitRetryCount}, retrying");
|
||||
|
||||
Reference in New Issue
Block a user