mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-25 23:28:33 -06:00
Always use \n, not Environment.NewLine
This commit is contained in:
35
.vscode/launch.json
vendored
Normal file
35
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
// Use IntelliSense to find out which attributes exist for C# debugging
|
||||
// Use hover for the description of the existing attributes
|
||||
// For further information visit https://github.com/dotnet/vscode-csharp/blob/main/debugger-launchjson.md.
|
||||
"name": ".NET Core Launch (web)",
|
||||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/Management.Web/bin/Debug/net8.0/Management.Web.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/Management.Web",
|
||||
"stopAtEntry": false,
|
||||
// Enable launching a web browser when ASP.NET Core starts. For more information: https://aka.ms/VSCode-CS-LaunchJson-WebBrowser
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"sourceFileMap": {
|
||||
"/Views": "${workspaceFolder}/Views"
|
||||
}
|
||||
},
|
||||
{
|
||||
"name": ".NET Core Attach",
|
||||
"type": "coreclr",
|
||||
"request": "attach"
|
||||
}
|
||||
]
|
||||
}
|
||||
41
.vscode/tasks.json
vendored
Normal file
41
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,41 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "build",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/canvasManagement.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/canvasManagement.sln",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary;ForceNoAlign"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/canvasManagement.sln"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
using System.Configuration;
|
||||
using LocalModels;
|
||||
using Management.Services;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
using NSubstitute;
|
||||
using NUnit.Framework.Internal;
|
||||
@@ -37,11 +40,16 @@ public class FileStorageTests
|
||||
var fileManagerLogger = new MyLogger<FileStorageManager>(NullLogger<FileStorageManager>.Instance);
|
||||
var markdownLoaderLogger = new MyLogger<CourseMarkdownLoader>(NullLogger<CourseMarkdownLoader>.Instance);
|
||||
var markdownSaverLogger = new MyLogger<MarkdownCourseSaver>(NullLogger<MarkdownCourseSaver>.Instance);
|
||||
|
||||
var otherLogger = NullLoggerFactory.Instance.CreateLogger<FileStorageManager>();
|
||||
Environment.SetEnvironmentVariable("storageDirectory", storageDirectory);
|
||||
var markdownLoader = new CourseMarkdownLoader(markdownLoaderLogger);
|
||||
var markdownSaver = new MarkdownCourseSaver(markdownSaverLogger);
|
||||
fileManager = new FileStorageManager(fileManagerLogger, markdownLoader, markdownSaver);
|
||||
var config = new ConfigurationBuilder()
|
||||
.AddEnvironmentVariables()
|
||||
.Build();
|
||||
var fileConfiguration = new FileConfiguration(config);
|
||||
|
||||
var markdownLoader = new CourseMarkdownLoader(markdownLoaderLogger, fileConfiguration);
|
||||
var markdownSaver = new MarkdownCourseSaver(markdownSaverLogger, fileConfiguration);
|
||||
fileManager = new FileStorageManager(fileManagerLogger, markdownLoader, markdownSaver, otherLogger, fileConfiguration);
|
||||
}
|
||||
|
||||
[Test]
|
||||
@@ -139,7 +147,9 @@ public class FileStorageTests
|
||||
var loadedCourses = await fileManager.LoadSavedMarkdownCourses();
|
||||
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
||||
|
||||
loadedCourse.Modules.First().Assignments.Should().BeEquivalentTo(testCourse.Modules.First().Assignments);
|
||||
var actualAssignments = loadedCourse.Modules.First().Assignments;
|
||||
var expectedAssignments = testCourse.Modules.First().Assignments;
|
||||
actualAssignments.Should().BeEquivalentTo(expectedAssignments);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -32,7 +32,7 @@ lines
|
||||
Answers = new LocalQuizQuestionAnswer[]
|
||||
{
|
||||
new LocalQuizQuestionAnswer() { Correct = true, Text = "true" },
|
||||
new LocalQuizQuestionAnswer() { Correct = false, Text = "false" + Environment.NewLine +Environment.NewLine + "endline" },
|
||||
new LocalQuizQuestionAnswer() { Correct = false, Text = "false\n\nendline" },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
@inject CanvasService canvas
|
||||
@inject CoursePlanner planner
|
||||
@inject NavigationManager navigtion
|
||||
@inject IConfiguration config
|
||||
|
||||
@code {
|
||||
[Parameter]
|
||||
@@ -55,7 +56,7 @@
|
||||
</button>
|
||||
<CourseSettings />
|
||||
<a class="btn btn-outline-secondary" target="_blank"
|
||||
href="@($"{Environment.GetEnvironmentVariable("CANVAS_URL")}/courses/{planner.LocalCourse.Settings.CanvasId}")">
|
||||
href="@($"{config["CANVAS_URL"]}/courses/{planner.LocalCourse.Settings.CanvasId}")">
|
||||
View In Canvas
|
||||
</a>
|
||||
<div class="my-auto ms-2 d-inline">
|
||||
|
||||
@@ -23,14 +23,14 @@ DotEnv.Load();
|
||||
|
||||
var builder = WebApplication.CreateBuilder(args);
|
||||
|
||||
var canvas_token = Environment.GetEnvironmentVariable("CANVAS_TOKEN");
|
||||
var canvas_token = builder.Configuration["CANVAS_TOKEN"];
|
||||
if (canvas_token == null)
|
||||
throw new Exception("CANVAS_TOKEN is null");
|
||||
var canvas_url = Environment.GetEnvironmentVariable("CANVAS_URL");
|
||||
var canvas_url = builder.Configuration["CANVAS_URL"];
|
||||
if (canvas_url == null)
|
||||
{
|
||||
Console.WriteLine("CANVAS_URL is null, defaulting to https://snow.instructure.com");
|
||||
Environment.SetEnvironmentVariable("CANVAS_URL", "https://snow.instructure.com");
|
||||
builder.Configuration["CANVAS_URL"] = "https://snow.instructure.com";
|
||||
}
|
||||
|
||||
const string serviceName = "canvas-management";
|
||||
@@ -94,6 +94,8 @@ builder.Services.AddScoped<AssignmentEditorContext>();
|
||||
builder.Services.AddScoped<QuizEditorContext>();
|
||||
builder.Services.AddScoped<DragContainer>();
|
||||
|
||||
builder.Services.AddSingleton<FileConfiguration>();
|
||||
|
||||
builder.Services.AddSignalR(e =>
|
||||
{
|
||||
e.MaximumReceiveMessageSize = 102400000;
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Markdig" Version="0.31.0" />
|
||||
<PackageReference Include="microsoft.extensions.configuration.abstractions" Version="8.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging" Version="7.0.0" />
|
||||
<PackageReference Include="RestSharp" Version="108.0.3" />
|
||||
<PackageReference Include="YamlDotNet" Version="13.1.1" />
|
||||
|
||||
@@ -62,9 +62,9 @@ public record LocalAssignment
|
||||
var settingsString = input.Split("---")[0];
|
||||
var (name, localAssignmentGroupName, submissionTypes, dueAt, lockAt) = parseSettings(settingsString);
|
||||
|
||||
var description = input.Split("---" + Environment.NewLine)[1].Split("## Rubric")[0];
|
||||
var description = input.Split("---\n")[1].Split("## Rubric")[0];
|
||||
|
||||
var rubricString = input.Split("## Rubric" + Environment.NewLine)[1];
|
||||
var rubricString = input.Split("## Rubric\n")[1];
|
||||
var rubric = ParseRubricMarkdown(rubricString);
|
||||
return new LocalAssignment()
|
||||
{
|
||||
@@ -100,15 +100,17 @@ public record LocalAssignment
|
||||
|
||||
private static List<string> parseSubmissionTypes(string input)
|
||||
{
|
||||
input = input.Replace("\r\n", "\n");
|
||||
List<string> submissionTypes = new List<string>();
|
||||
|
||||
// Define a regular expression pattern to match the bulleted list items
|
||||
string startOfTypePattern = @"- (.+)";
|
||||
Regex regex = new Regex(startOfTypePattern);
|
||||
|
||||
var inputAfterSubmissionTypes = input.Split("SubmissionTypes:" + Environment.NewLine)[1];
|
||||
var words = input.Split("SubmissionTypes:");
|
||||
var inputAfterSubmissionTypes = words[1];
|
||||
|
||||
string[] lines = inputAfterSubmissionTypes.Split(Environment.NewLine, StringSplitOptions.RemoveEmptyEntries);
|
||||
string[] lines = inputAfterSubmissionTypes.Split("\n", StringSplitOptions.RemoveEmptyEntries);
|
||||
|
||||
foreach (string line in lines)
|
||||
{
|
||||
@@ -143,10 +145,10 @@ public record LocalAssignment
|
||||
var settingsMarkdown = settingsToMarkdown();
|
||||
var rubricMarkdown = RubricToMarkdown();
|
||||
var assignmentMarkdown =
|
||||
settingsMarkdown + Environment.NewLine
|
||||
+ "---" + Environment.NewLine + Environment.NewLine
|
||||
+ Description + Environment.NewLine
|
||||
+ Environment.NewLine + "## Rubric" + Environment.NewLine + Environment.NewLine
|
||||
settingsMarkdown + "\n"
|
||||
+ "---\n\n"
|
||||
+ Description + "\n\n"
|
||||
+ "## Rubric\n\n"
|
||||
+ rubricMarkdown;
|
||||
|
||||
return assignmentMarkdown;
|
||||
@@ -158,7 +160,7 @@ public record LocalAssignment
|
||||
foreach (var item in Rubric)
|
||||
{
|
||||
var pointLabel = item.Points > 1 ? "pts" : "pt";
|
||||
builder.Append($"- {item.Points}{pointLabel}: {item.Label}" + Environment.NewLine);
|
||||
builder.Append($"- {item.Points}{pointLabel}: {item.Label}" + "\n");
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
@@ -166,14 +168,14 @@ public record LocalAssignment
|
||||
private string settingsToMarkdown()
|
||||
{
|
||||
var builder = new StringBuilder();
|
||||
builder.Append($"Name: {Name}" + Environment.NewLine);
|
||||
builder.Append($"LockAt: {LockAt}" + Environment.NewLine);
|
||||
builder.Append($"DueAt: {DueAt}" + Environment.NewLine);
|
||||
builder.Append($"AssignmentGroupName: {LocalAssignmentGroupName}" + Environment.NewLine);
|
||||
builder.Append($"SubmissionTypes:" + Environment.NewLine);
|
||||
builder.Append($"Name: {Name}" + "\n");
|
||||
builder.Append($"LockAt: {LockAt}" + "\n");
|
||||
builder.Append($"DueAt: {DueAt}" + "\n");
|
||||
builder.Append($"AssignmentGroupName: {LocalAssignmentGroupName}" + "\n");
|
||||
builder.Append($"SubmissionTypes:" + "\n");
|
||||
foreach (var submissionType in SubmissionTypes)
|
||||
{
|
||||
builder.Append($"- {submissionType}" + Environment.NewLine);
|
||||
builder.Append($"- {submissionType}" + "\n");
|
||||
}
|
||||
return builder.ToString();
|
||||
}
|
||||
@@ -182,7 +184,7 @@ public record LocalAssignment
|
||||
{
|
||||
if (rawMarkdown.Trim() == string.Empty)
|
||||
return [];
|
||||
var lines = rawMarkdown.Trim().Split(Environment.NewLine);
|
||||
var lines = rawMarkdown.Trim().Split("\n");
|
||||
var items = lines.Select(parseIndividualRubricItemMarkdown).ToArray();
|
||||
return items;
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public record LocalQuiz
|
||||
public string ToMarkdown()
|
||||
{
|
||||
var questionMarkdownArray = Questions.Select(q => q.ToMarkdown()).ToArray();
|
||||
var questionDelimiter = Environment.NewLine + Environment.NewLine + "---" + Environment.NewLine + Environment.NewLine;
|
||||
var questionDelimiter = "\n\n---\n\n";
|
||||
var questionMarkdown = string.Join(questionDelimiter, questionMarkdownArray);
|
||||
|
||||
return $@"Name: {Name}
|
||||
@@ -57,7 +57,7 @@ Description: {Description}
|
||||
public static LocalQuiz ParseMarkdown(string input)
|
||||
{
|
||||
|
||||
var splitInput = input.Split("---" + Environment.NewLine);
|
||||
var splitInput = input.Split("---\n");
|
||||
var settings = splitInput[0];
|
||||
var quizWithoutQuestions = getQuizWithOnlySettings(settings);
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ public record LocalQuizQuestion
|
||||
public string ToMarkdown()
|
||||
{
|
||||
var answerArray = Answers.Select(getAnswerMarkdown);
|
||||
var answersText = string.Join(Environment.NewLine, answerArray);
|
||||
var answersText = string.Join("\n", answerArray);
|
||||
var questionTypeIndicator = QuestionType == "essay" || QuestionType == "short_answer" ? QuestionType : "";
|
||||
|
||||
return $@"Points: {Points}
|
||||
@@ -25,7 +25,7 @@ public record LocalQuizQuestion
|
||||
private string getAnswerMarkdown(LocalQuizQuestionAnswer answer, int index)
|
||||
{
|
||||
var multilineMarkdownCompatibleText = answer.Text.StartsWith("```")
|
||||
? Environment.NewLine + answer.Text
|
||||
? "\n" + answer.Text
|
||||
: answer.Text;
|
||||
|
||||
if (QuestionType == "multiple_answers")
|
||||
@@ -53,7 +53,7 @@ public record LocalQuizQuestion
|
||||
|
||||
public static LocalQuizQuestion ParseMarkdown(string input, int questionIndex)
|
||||
{
|
||||
var lines = input.Trim().Split(Environment.NewLine);
|
||||
var lines = input.Trim().Split("\n");
|
||||
var firstLineIsPoints = lines.First().Contains("points: ", StringComparison.CurrentCultureIgnoreCase);
|
||||
|
||||
var textHasPoints = lines.Length > 0
|
||||
@@ -84,7 +84,7 @@ public record LocalQuizQuestion
|
||||
)
|
||||
.ToArray()
|
||||
: linesWithoutAnswers;
|
||||
var description = string.Join(Environment.NewLine, descriptionLines);
|
||||
var description = string.Join("\n", descriptionLines);
|
||||
|
||||
|
||||
|
||||
@@ -164,7 +164,7 @@ public record LocalQuizQuestion
|
||||
}
|
||||
|
||||
if (acc.Count != 0) // Append to the previous line if there is one
|
||||
acc[^1] += Environment.NewLine + line;
|
||||
acc[^1] += "\n" + line;
|
||||
else
|
||||
acc.Add(line);
|
||||
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
using Management.Services;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
public class FileConfiguration
|
||||
public class FileConfiguration(IConfiguration config)
|
||||
{
|
||||
public static string GetBasePath()
|
||||
public string GetBasePath()
|
||||
{
|
||||
string? storageDirectory = Environment.GetEnvironmentVariable("storageDirectory");
|
||||
string? storageDirectory = config["storageDirectory"];
|
||||
var basePath = storageDirectory ?? Path.GetFullPath("../storage");
|
||||
|
||||
if (!Directory.Exists(basePath))
|
||||
|
||||
@@ -15,7 +15,8 @@ public class FileStorageManager
|
||||
MyLogger<FileStorageManager> logger,
|
||||
CourseMarkdownLoader courseMarkdownLoader,
|
||||
MarkdownCourseSaver saveMarkdownCourse,
|
||||
ILogger<FileStorageManager> otherLogger
|
||||
ILogger<FileStorageManager> otherLogger,
|
||||
FileConfiguration fileConfig
|
||||
)
|
||||
{
|
||||
using var activity = DiagnosticsConfig.Source.StartActivity("loading storage directory");
|
||||
@@ -23,7 +24,7 @@ public class FileStorageManager
|
||||
_courseMarkdownLoader = courseMarkdownLoader;
|
||||
_saveMarkdownCourse = saveMarkdownCourse;
|
||||
_otherLogger = otherLogger;
|
||||
_basePath = FileConfiguration.GetBasePath();
|
||||
_basePath = fileConfig.GetBasePath();
|
||||
|
||||
this.logger.Log("Using storage directory: " + _basePath);
|
||||
}
|
||||
|
||||
@@ -6,10 +6,10 @@ public class CourseMarkdownLoader
|
||||
private readonly MyLogger<CourseMarkdownLoader> logger;
|
||||
private readonly string _basePath;
|
||||
|
||||
public CourseMarkdownLoader(MyLogger<CourseMarkdownLoader> logger)
|
||||
public CourseMarkdownLoader(MyLogger<CourseMarkdownLoader> logger, FileConfiguration fileConfig)
|
||||
{
|
||||
this.logger = logger;
|
||||
_basePath = FileConfiguration.GetBasePath();
|
||||
_basePath = fileConfig.GetBasePath();
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<LocalCourse>> LoadSavedMarkdownCourses()
|
||||
@@ -101,7 +101,7 @@ public class CourseMarkdownLoader
|
||||
var assignmentPromises = assignmentFiles
|
||||
.Select(async filePath =>
|
||||
{
|
||||
var rawFile = await File.ReadAllTextAsync(filePath);
|
||||
var rawFile = (await File.ReadAllTextAsync(filePath)).Replace("\r\n", "\n");
|
||||
return LocalAssignment.ParseMarkdown(rawFile);
|
||||
})
|
||||
.ToArray();
|
||||
@@ -122,7 +122,7 @@ public class CourseMarkdownLoader
|
||||
var quizPromises = quizFiles
|
||||
.Select(async path =>
|
||||
{
|
||||
var rawQuiz = await File.ReadAllTextAsync(path);
|
||||
var rawQuiz = (await File.ReadAllTextAsync(path)).Replace("\r\n", "\n");
|
||||
return LocalQuiz.ParseMarkdown(rawQuiz);
|
||||
});
|
||||
|
||||
|
||||
@@ -2,10 +2,10 @@ using System.Threading.Tasks.Sources;
|
||||
using LocalModels;
|
||||
namespace Management.Services;
|
||||
|
||||
public class MarkdownCourseSaver(MyLogger<MarkdownCourseSaver> logger)
|
||||
public class MarkdownCourseSaver(MyLogger<MarkdownCourseSaver> logger, FileConfiguration fileConfig)
|
||||
{
|
||||
private readonly MyLogger<MarkdownCourseSaver> _logger = logger;
|
||||
private readonly string _basePath = FileConfiguration.GetBasePath();
|
||||
private readonly string _basePath = fileConfig.GetBasePath();
|
||||
|
||||
public async Task Save(LocalCourse course, LocalCourse? previouslyStoredCourse)
|
||||
{
|
||||
|
||||
@@ -1,19 +1,22 @@
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using RestSharp;
|
||||
|
||||
public class WebRequestor : IWebRequestor
|
||||
{
|
||||
private string BaseUrl = Environment.GetEnvironmentVariable("CANVAS_URL") + "/api/v1/";
|
||||
private string BaseUrl = "";
|
||||
private string token;
|
||||
private RestClient client;
|
||||
private readonly IConfiguration _config;
|
||||
|
||||
public WebRequestor()
|
||||
public WebRequestor(IConfiguration config)
|
||||
{
|
||||
_config = config;
|
||||
token =
|
||||
Environment.GetEnvironmentVariable("CANVAS_TOKEN")
|
||||
_config["CANVAS_TOKEN"]
|
||||
?? throw new Exception("CANVAS_TOKEN not in environment");
|
||||
BaseUrl = _config["CANVAS_URL"] + "/api/v1/";
|
||||
client = new RestClient(BaseUrl);
|
||||
client.AddDefaultHeader("Authorization", $"Bearer {token}");
|
||||
|
||||
}
|
||||
|
||||
public async Task<(T[]?, RestResponse)> GetManyAsync<T>(RestRequest request)
|
||||
|
||||
Reference in New Issue
Block a user