mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 07:38:33 -06:00
Merge branch 'main' of https://github.com/alexmickelson/canvasmanagement
This commit is contained in:
@@ -2,6 +2,7 @@ using LocalModels;
|
|||||||
|
|
||||||
public class RubricMarkdownTests
|
public class RubricMarkdownTests
|
||||||
{
|
{
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCanParseOneItem()
|
public void TestCanParseOneItem()
|
||||||
{
|
{
|
||||||
@@ -15,6 +16,7 @@ public class RubricMarkdownTests
|
|||||||
rubric.First().Label.Should().Be("this is the task");
|
rubric.First().Label.Should().Be("this is the task");
|
||||||
rubric.First().Points.Should().Be(2);
|
rubric.First().Points.Should().Be(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCanParseMultipleItems()
|
public void TestCanParseMultipleItems()
|
||||||
{
|
{
|
||||||
@@ -29,6 +31,7 @@ public class RubricMarkdownTests
|
|||||||
rubric.ElementAt(1).Label.Should().Be("this is the other task");
|
rubric.ElementAt(1).Label.Should().Be("this is the other task");
|
||||||
rubric.ElementAt(1).Points.Should().Be(3);
|
rubric.ElementAt(1).Points.Should().Be(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCanParseSinglePoint()
|
public void TestCanParseSinglePoint()
|
||||||
{
|
{
|
||||||
@@ -41,6 +44,7 @@ public class RubricMarkdownTests
|
|||||||
rubric.First().Label.Should().Be("this is the task");
|
rubric.First().Label.Should().Be("this is the task");
|
||||||
rubric.First().Points.Should().Be(1);
|
rubric.First().Points.Should().Be(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
[Test]
|
[Test]
|
||||||
public void TestCanParseSingleExtraCredit_LowerCase()
|
public void TestCanParseSingleExtraCredit_LowerCase()
|
||||||
{
|
{
|
||||||
|
|||||||
14
Management.Web/ConfigurationSetup.cs
Normal file
14
Management.Web/ConfigurationSetup.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
public static class ConfigurationSetup
|
||||||
|
{
|
||||||
|
public static void Canvas(WebApplicationBuilder builder)
|
||||||
|
{
|
||||||
|
var canvas_token = builder.Configuration["CANVAS_TOKEN"] ?? throw new Exception("CANVAS_TOKEN is null");
|
||||||
|
|
||||||
|
var canvas_url = builder.Configuration["CANVAS_URL"];
|
||||||
|
if (canvas_url == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine("CANVAS_URL is null, defaulting to https://snow.instructure.com");
|
||||||
|
builder.Configuration["CANVAS_URL"] = "https://snow.instructure.com";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Management.Web/CustomConsoleExporter.cs
Normal file
20
Management.Web/CustomConsoleExporter.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using System.Diagnostics;
|
||||||
|
using OpenTelemetry;
|
||||||
|
|
||||||
|
public class CustomConsoleExporter : BaseExporter<Activity>
|
||||||
|
{
|
||||||
|
public override ExportResult Export(in Batch<Activity> batch)
|
||||||
|
{
|
||||||
|
using var scope = SuppressInstrumentationScope.Begin();
|
||||||
|
|
||||||
|
foreach (var activity in batch)
|
||||||
|
{
|
||||||
|
string[] ignoreOperations = [
|
||||||
|
"Microsoft.AspNetCore.Hosting.HttpRequestIn",
|
||||||
|
];
|
||||||
|
if (!ignoreOperations.Contains(activity.OperationName))
|
||||||
|
Console.WriteLine($"{activity.OperationName}: {activity.DisplayName}");
|
||||||
|
}
|
||||||
|
return ExportResult.Success;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -18,37 +18,30 @@ using OpenTelemetry.Logs;
|
|||||||
using OpenTelemetry.Metrics;
|
using OpenTelemetry.Metrics;
|
||||||
using OpenTelemetry.Resources;
|
using OpenTelemetry.Resources;
|
||||||
using OpenTelemetry.Trace;
|
using OpenTelemetry.Trace;
|
||||||
|
using OpenTelemetry;
|
||||||
|
|
||||||
DotEnv.Load();
|
DotEnv.Load();
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
var canvas_token = builder.Configuration["CANVAS_TOKEN"];
|
ConfigurationSetup.Canvas(builder);
|
||||||
if (canvas_token == null)
|
|
||||||
throw new Exception("CANVAS_TOKEN is null");
|
|
||||||
var canvas_url = builder.Configuration["CANVAS_URL"];
|
|
||||||
if (canvas_url == null)
|
|
||||||
{
|
|
||||||
Console.WriteLine("CANVAS_URL is null, defaulting to https://snow.instructure.com");
|
|
||||||
builder.Configuration["CANVAS_URL"] = "https://snow.instructure.com";
|
|
||||||
}
|
|
||||||
|
|
||||||
const string serviceName = "canvas-management";
|
const string serviceName = "canvas-management";
|
||||||
|
|
||||||
// builder.Logging.AddOpenTelemetry(options =>
|
builder.Logging.AddOpenTelemetry(options =>
|
||||||
// {
|
{
|
||||||
// options
|
options
|
||||||
// .SetResourceBuilder(
|
.SetResourceBuilder(
|
||||||
// ResourceBuilder
|
ResourceBuilder
|
||||||
// .CreateDefault()
|
.CreateDefault()
|
||||||
// .AddService(serviceName)
|
.AddService(serviceName)
|
||||||
// )
|
)
|
||||||
// .AddOtlpExporter(o =>
|
.AddOtlpExporter(o =>
|
||||||
// {
|
{
|
||||||
// o.Endpoint = new Uri("http://localhost:4317/");
|
o.Endpoint = new Uri("http://localhost:4317/");
|
||||||
// })
|
});
|
||||||
// .AddConsoleExporter();
|
// .AddConsoleExporter();
|
||||||
// });
|
});
|
||||||
|
|
||||||
builder.Services.AddOpenTelemetry()
|
builder.Services.AddOpenTelemetry()
|
||||||
.ConfigureResource(resource => resource.AddService(serviceName))
|
.ConfigureResource(resource => resource.AddService(serviceName))
|
||||||
@@ -59,15 +52,16 @@ builder.Services.AddOpenTelemetry()
|
|||||||
o.Endpoint = new Uri("http://localhost:4317/");
|
o.Endpoint = new Uri("http://localhost:4317/");
|
||||||
})
|
})
|
||||||
.AddAspNetCoreInstrumentation()
|
.AddAspNetCoreInstrumentation()
|
||||||
.AddConsoleExporter()
|
.AddProcessor(new BatchActivityExportProcessor(new CustomConsoleExporter()))
|
||||||
|
)
|
||||||
|
.WithMetrics(metrics => metrics
|
||||||
|
.AddOtlpExporter(o =>
|
||||||
|
{
|
||||||
|
o.Endpoint = new Uri("http://localhost:4317/");
|
||||||
|
}
|
||||||
|
)
|
||||||
|
.AddAspNetCoreInstrumentation()
|
||||||
);
|
);
|
||||||
// .WithMetrics(metrics => metrics
|
|
||||||
// .AddOtlpExporter(o => {
|
|
||||||
// o.Endpoint = new Uri("http://localhost:4317/");
|
|
||||||
// })
|
|
||||||
// .AddAspNetCoreInstrumentation()
|
|
||||||
// .AddConsoleExporter()
|
|
||||||
// );
|
|
||||||
|
|
||||||
// Add services to the container.
|
// Add services to the container.
|
||||||
builder.Services.AddRazorPages();
|
builder.Services.AddRazorPages();
|
||||||
@@ -133,3 +127,4 @@ foreach (var address in addresses)
|
|||||||
}
|
}
|
||||||
|
|
||||||
app.WaitForShutdown();
|
app.WaitForShutdown();
|
||||||
|
|
||||||
|
|||||||
@@ -34,6 +34,9 @@
|
|||||||
private string name { get; set; } = String.Empty;
|
private string name { get; set; } = String.Empty;
|
||||||
private bool addingAssignmentToCanvas = false;
|
private bool addingAssignmentToCanvas = false;
|
||||||
private bool deletingAssignmentFromCanvas = false;
|
private bool deletingAssignmentFromCanvas = false;
|
||||||
|
private bool showHelp = false;
|
||||||
|
|
||||||
|
private void toggleHelp() => showHelp = !showHelp;
|
||||||
|
|
||||||
private void submitHandler()
|
private void submitHandler()
|
||||||
{
|
{
|
||||||
@@ -157,9 +160,6 @@
|
|||||||
deletingAssignmentFromCanvas = false;
|
deletingAssignmentFromCanvas = false;
|
||||||
StateHasChanged();
|
StateHasChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private bool showHelp { get; set; } = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
<div class="d-flex flex-column p-2 h-100 w-100" style="height: 100%;" >
|
<div class="d-flex flex-column p-2 h-100 w-100" style="height: 100%;" >
|
||||||
@@ -170,7 +170,7 @@
|
|||||||
<section class="flex-grow-1 p-1 border rounded-4 bg-dark-subtle" style="min-height: 0;">
|
<section class="flex-grow-1 p-1 border rounded-4 bg-dark-subtle" style="min-height: 0;">
|
||||||
@if (assignmentContext.Assignment != null)
|
@if (assignmentContext.Assignment != null)
|
||||||
{
|
{
|
||||||
@* <AssignmentMarkdownEditor ShowHelp="@showhelp" /> *@
|
<AssignmentMarkdownEditor ShowHelp=@showHelp />
|
||||||
}
|
}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
@@ -182,7 +182,7 @@
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
|
||||||
<button class="btn btn-outline-secondary mx-3" @onclick="() => showHelp = !showHelp">
|
<button class="btn btn-outline-secondary mx-3" @onclick=toggleHelp>
|
||||||
Toggle Help
|
Toggle Help
|
||||||
</button>
|
</button>
|
||||||
<ConfirmationModal Label="Delete" Class="btn btn-danger" OnConfirmAsync="HandleDelete" />
|
<ConfirmationModal Label="Delete" Class="btn btn-danger" OnConfirmAsync="HandleDelete" />
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
@using Markdig
|
@using Markdig
|
||||||
|
@using Shared.Components.AssignmentForm
|
||||||
|
|
||||||
@inject CoursePlanner planner
|
@inject CoursePlanner planner
|
||||||
@inject AssignmentEditorContext assignmentContext
|
@inject AssignmentEditorContext assignmentContext
|
||||||
@@ -64,18 +65,28 @@
|
|||||||
private MarkupString preview { get => (MarkupString)Markdown.ToHtml(assignmentContext?.Assignment?.Description ?? ""); }
|
private MarkupString preview { get => (MarkupString)Markdown.ToHtml(assignmentContext?.Assignment?.Description ?? ""); }
|
||||||
private string HelpText()
|
private string HelpText()
|
||||||
{
|
{
|
||||||
var groupNames = string.Join(", " , planner.LocalCourse?.Settings.AssignmentGroups.Select(g => g.Name));
|
var groupNames = string.Join("\n- " , planner.LocalCourse?.Settings.AssignmentGroups.Select(g => g.Name) ?? []);
|
||||||
return $@"
|
return $@"
|
||||||
SubmissionTypes:
|
SubmissionTypes:
|
||||||
- {AssignmentSubmissionType.ONLINE_TEXT_ENTRY}
|
- {AssignmentSubmissionType.ONLINE_TEXT_ENTRY}
|
||||||
- {AssignmentSubmissionType.ONLINE_UPLOAD}
|
- {AssignmentSubmissionType.ONLINE_UPLOAD}
|
||||||
|
|
||||||
Assignment Group Names:
|
Assignment Group Names:
|
||||||
{groupNames}
|
- {groupNames}
|
||||||
";
|
";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<div class="d-flex w-100 h-100 flex-row">
|
||||||
|
@if(ShowHelp)
|
||||||
|
{
|
||||||
|
<div class=" rounded rounded-3 bg-black" >
|
||||||
|
<pre class=" me-3 pe-5 ps-3 rounded rounded-3">
|
||||||
|
@HelpText()
|
||||||
|
</pre>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
@if(assignmentContext.Assignment != null && planner.LocalCourse != null)
|
@if(assignmentContext.Assignment != null && planner.LocalCourse != null)
|
||||||
{
|
{
|
||||||
<div class="row h-100 w-100">
|
<div class="row h-100 w-100">
|
||||||
@@ -109,3 +120,4 @@ Assignment Group Names:
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
</div>
|
||||||
@@ -98,7 +98,12 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@if(existsInCanvas && NeedsToBeUpdatedInCanvas)
|
@if(
|
||||||
|
planner.LocalCourse != null
|
||||||
|
&& existsInCanvas
|
||||||
|
&& NeedsToBeUpdatedInCanvas
|
||||||
|
&& assignmentInCanvas != null
|
||||||
|
)
|
||||||
{
|
{
|
||||||
<div class="mx-3 text-body-tertiary">
|
<div class="mx-3 text-body-tertiary">
|
||||||
@Assignment.GetUpdateReason(
|
@Assignment.GetUpdateReason(
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
"DetailedErrors": true,
|
"DetailedErrors": true,
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Information",
|
"Default": "Error",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"Logging": {
|
"Logging": {
|
||||||
"LogLevel": {
|
"LogLevel": {
|
||||||
"Default": "Trace",
|
"Default": "Error",
|
||||||
"Microsoft.AspNetCore": "Warning"
|
"Microsoft.AspNetCore": "Warning"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
namespace LocalModels;
|
||||||
|
public class RubricMarkdownParseException : Exception
|
||||||
|
{
|
||||||
|
public RubricMarkdownParseException(string message) : base(message) { }
|
||||||
|
}
|
||||||
|
public class AssignmentMarkdownParseException : Exception
|
||||||
|
{
|
||||||
|
public AssignmentMarkdownParseException(string message) : base(message) { }
|
||||||
|
}
|
||||||
@@ -40,7 +40,6 @@ public record LocalAssignment
|
|||||||
public string GetDescriptionHtml()
|
public string GetDescriptionHtml()
|
||||||
{
|
{
|
||||||
var rubricHtml = GetRubricHtml();
|
var rubricHtml = GetRubricHtml();
|
||||||
|
|
||||||
return Markdig.Markdown.ToHtml(Description) + "<hr>" + rubricHtml;
|
return Markdig.Markdown.ToHtml(Description) + "<hr>" + rubricHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,170 +48,8 @@ public record LocalAssignment
|
|||||||
.FirstOrDefault(g => g.Name == LocalAssignmentGroupName)?
|
.FirstOrDefault(g => g.Name == LocalAssignmentGroupName)?
|
||||||
.CanvasId;
|
.CanvasId;
|
||||||
|
|
||||||
|
public string ToMarkdown() => this.AssignmentToMarkdown();
|
||||||
public string ToYaml()
|
public string RubricToMarkdown() => this.AssignmentRubricToMarkdown();
|
||||||
{
|
public static LocalAssignment ParseMarkdown(string input) => LocalAssignmentMarkdownParser.ParseMarkdown(input);
|
||||||
var serializer = new SerializerBuilder().DisableAliases().Build();
|
public static IEnumerable<RubricItem> ParseRubricMarkdown(string rawMarkdown) => LocalAssignmentMarkdownParser.ParseRubricMarkdown(rawMarkdown);
|
||||||
var yaml = serializer.Serialize(this);
|
|
||||||
return yaml;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static LocalAssignment ParseMarkdown(string input)
|
|
||||||
{
|
|
||||||
var settingsString = input.Split("---")[0];
|
|
||||||
var (name, localAssignmentGroupName, submissionTypes, dueAt, lockAt) = parseSettings(settingsString);
|
|
||||||
|
|
||||||
var description = input.Split("---\n")[1].Split("## Rubric")[0];
|
|
||||||
|
|
||||||
var rubricString = input.Split("## Rubric\n")[1];
|
|
||||||
var rubric = ParseRubricMarkdown(rubricString);
|
|
||||||
return new LocalAssignment()
|
|
||||||
{
|
|
||||||
Name = name.Trim(),
|
|
||||||
LocalAssignmentGroupName = localAssignmentGroupName.Trim(),
|
|
||||||
SubmissionTypes = submissionTypes,
|
|
||||||
DueAt = dueAt,
|
|
||||||
LockAt = lockAt,
|
|
||||||
Rubric = rubric,
|
|
||||||
Description = description.Trim()
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static (string name, string assignmentGroupName, List<string> submissionTypes, DateTime dueAt, DateTime? lockAt) parseSettings(string input)
|
|
||||||
{
|
|
||||||
var name = extractLabelValue(input, "Name");
|
|
||||||
var rawLockAt = extractLabelValue(input, "LockAt");
|
|
||||||
var rawDueAt = extractLabelValue(input, "DueAt");
|
|
||||||
var localAssignmentGroupName = extractLabelValue(input, "AssignmentGroupName");
|
|
||||||
var submissionTypes = parseSubmissionTypes(input);
|
|
||||||
|
|
||||||
DateTime? lockAt = DateTime.TryParse(rawLockAt, out DateTime parsedLockAt)
|
|
||||||
? parsedLockAt
|
|
||||||
: null;
|
|
||||||
var dueAt = DateTime.TryParse(rawDueAt, out DateTime parsedDueAt)
|
|
||||||
? parsedDueAt
|
|
||||||
: throw new QuizMarkdownParseException($"Error with DueAt: {rawDueAt}");
|
|
||||||
|
|
||||||
return (name, localAssignmentGroupName, submissionTypes, dueAt, lockAt);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
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 words = input.Split("SubmissionTypes:");
|
|
||||||
var inputAfterSubmissionTypes = words[1];
|
|
||||||
|
|
||||||
string[] lines = inputAfterSubmissionTypes.Split("\n", StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
|
|
||||||
foreach (string line in lines)
|
|
||||||
{
|
|
||||||
string trimmedLine = line.Trim();
|
|
||||||
Match match = regex.Match(trimmedLine);
|
|
||||||
|
|
||||||
if (!match.Success)
|
|
||||||
break;
|
|
||||||
|
|
||||||
string type = match.Groups[1].Value.Trim();
|
|
||||||
submissionTypes.Add(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
return submissionTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
static string extractLabelValue(string input, string label)
|
|
||||||
{
|
|
||||||
string pattern = $@"{label}: (.*?)\n";
|
|
||||||
Match match = Regex.Match(input, pattern);
|
|
||||||
|
|
||||||
if (match.Success)
|
|
||||||
{
|
|
||||||
return match.Groups[1].Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string ToMarkdown()
|
|
||||||
{
|
|
||||||
var settingsMarkdown = settingsToMarkdown();
|
|
||||||
var rubricMarkdown = RubricToMarkdown();
|
|
||||||
var assignmentMarkdown =
|
|
||||||
settingsMarkdown + "\n"
|
|
||||||
+ "---\n\n"
|
|
||||||
+ Description + "\n\n"
|
|
||||||
+ "## Rubric\n\n"
|
|
||||||
+ rubricMarkdown;
|
|
||||||
|
|
||||||
return assignmentMarkdown;
|
|
||||||
}
|
|
||||||
|
|
||||||
public string RubricToMarkdown()
|
|
||||||
{
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
foreach (var item in Rubric)
|
|
||||||
{
|
|
||||||
var pointLabel = item.Points > 1 ? "pts" : "pt";
|
|
||||||
builder.Append($"- {item.Points}{pointLabel}: {item.Label}" + "\n");
|
|
||||||
}
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string settingsToMarkdown()
|
|
||||||
{
|
|
||||||
var builder = new StringBuilder();
|
|
||||||
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}" + "\n");
|
|
||||||
}
|
|
||||||
return builder.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static IEnumerable<RubricItem> ParseRubricMarkdown(string rawMarkdown)
|
|
||||||
{
|
|
||||||
if (rawMarkdown.Trim() == string.Empty)
|
|
||||||
return [];
|
|
||||||
var lines = rawMarkdown.Trim().Split("\n");
|
|
||||||
var items = lines.Select(parseIndividualRubricItemMarkdown).ToArray();
|
|
||||||
return items;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static RubricItem parseIndividualRubricItemMarkdown(string rawMarkdown)
|
|
||||||
{
|
|
||||||
var pointsPattern = @"\s*-\s*(\d+)\s*pt(s)?:";
|
|
||||||
var match = Regex.Match(rawMarkdown, pointsPattern);
|
|
||||||
if (!match.Success)
|
|
||||||
throw new RubricMarkdownParseException($"points not found: {rawMarkdown}");
|
|
||||||
|
|
||||||
var points = int.Parse(match.Groups[1].Value);
|
|
||||||
|
|
||||||
var label = string.Join(": ", rawMarkdown.Split(": ").Skip(1));
|
|
||||||
|
|
||||||
return new RubricItem()
|
|
||||||
{
|
|
||||||
Points = points,
|
|
||||||
Label = label
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class RubricMarkdownParseException : Exception
|
|
||||||
{
|
|
||||||
public RubricMarkdownParseException(string message) : base(message) { }
|
|
||||||
}
|
|
||||||
public class AssignmentMarkdownParseException : Exception
|
|
||||||
{
|
|
||||||
public AssignmentMarkdownParseException(string message) : base(message) { }
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,50 @@
|
|||||||
|
using System.Text;
|
||||||
|
using LocalModels;
|
||||||
|
|
||||||
|
namespace LocalModels;
|
||||||
|
public static class LocalAssignmentMarkdownCreator
|
||||||
|
{
|
||||||
|
|
||||||
|
|
||||||
|
public static string AssignmentToMarkdown(this LocalAssignment assignment)
|
||||||
|
{
|
||||||
|
var settingsMarkdown = assignment.settingsToMarkdown();
|
||||||
|
var rubricMarkdown = assignment.RubricToMarkdown();
|
||||||
|
var assignmentMarkdown =
|
||||||
|
settingsMarkdown + "\n"
|
||||||
|
+ "---\n\n"
|
||||||
|
+ assignment.Description + "\n\n"
|
||||||
|
+ "## Rubric\n\n"
|
||||||
|
+ rubricMarkdown;
|
||||||
|
|
||||||
|
return assignmentMarkdown;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static string AssignmentRubricToMarkdown(this LocalAssignment assignment)
|
||||||
|
{
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
foreach (var item in assignment.Rubric)
|
||||||
|
{
|
||||||
|
var pointLabel = item.Points > 1 ? "pts" : "pt";
|
||||||
|
builder.Append($"- {item.Points}{pointLabel}: {item.Label}" + "\n");
|
||||||
|
}
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string settingsToMarkdown(this LocalAssignment assignment)
|
||||||
|
{
|
||||||
|
var printableDueDate = assignment.DueAt.ToString().Replace('\u202F', ' ');
|
||||||
|
var printableLockAt = assignment.LockAt?.ToString().Replace('\u202F', ' ') ?? "";
|
||||||
|
var builder = new StringBuilder();
|
||||||
|
builder.Append($"Name: {assignment.Name}" + "\n");
|
||||||
|
builder.Append($"LockAt: {printableLockAt}" + "\n");
|
||||||
|
builder.Append($"DueAt: {printableDueDate}" + "\n");
|
||||||
|
builder.Append($"AssignmentGroupName: {assignment.LocalAssignmentGroupName}" + "\n");
|
||||||
|
builder.Append($"SubmissionTypes:" + "\n");
|
||||||
|
foreach (var submissionType in assignment.SubmissionTypes)
|
||||||
|
{
|
||||||
|
builder.Append($"- {submissionType}" + "\n");
|
||||||
|
}
|
||||||
|
return builder.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
|
||||||
|
|
||||||
|
using System.Text.RegularExpressions;
|
||||||
|
|
||||||
|
namespace LocalModels;
|
||||||
|
public static class LocalAssignmentMarkdownParser
|
||||||
|
{
|
||||||
|
|
||||||
|
public static LocalAssignment ParseMarkdown(string input)
|
||||||
|
{
|
||||||
|
var settingsString = input.Split("---")[0];
|
||||||
|
var (name, localAssignmentGroupName, submissionTypes, dueAt, lockAt) = parseSettings(settingsString);
|
||||||
|
|
||||||
|
var description = input.Split("---\n")[1].Split("## Rubric")[0];
|
||||||
|
|
||||||
|
var rubricString = input.Split("## Rubric\n")[1];
|
||||||
|
var rubric = ParseRubricMarkdown(rubricString);
|
||||||
|
return new LocalAssignment()
|
||||||
|
{
|
||||||
|
Name = name.Trim(),
|
||||||
|
LocalAssignmentGroupName = localAssignmentGroupName.Trim(),
|
||||||
|
SubmissionTypes = submissionTypes,
|
||||||
|
DueAt = dueAt,
|
||||||
|
LockAt = lockAt,
|
||||||
|
Rubric = rubric,
|
||||||
|
Description = description.Trim()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private static (string name, string assignmentGroupName, List<string> submissionTypes, DateTime dueAt, DateTime? lockAt) parseSettings(string input)
|
||||||
|
{
|
||||||
|
var name = extractLabelValue(input, "Name");
|
||||||
|
var rawLockAt = extractLabelValue(input, "LockAt");
|
||||||
|
var rawDueAt = extractLabelValue(input, "DueAt");
|
||||||
|
var localAssignmentGroupName = extractLabelValue(input, "AssignmentGroupName");
|
||||||
|
var submissionTypes = parseSubmissionTypes(input);
|
||||||
|
|
||||||
|
DateTime? lockAt = DateTime.TryParse(rawLockAt, out DateTime parsedLockAt)
|
||||||
|
? parsedLockAt
|
||||||
|
: null;
|
||||||
|
var dueAt = DateTime.TryParse(rawDueAt, out DateTime parsedDueAt)
|
||||||
|
? parsedDueAt
|
||||||
|
: throw new QuizMarkdownParseException($"Error with DueAt: {rawDueAt}");
|
||||||
|
|
||||||
|
return (name, localAssignmentGroupName, submissionTypes, dueAt, lockAt);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static List<string> parseSubmissionTypes(string input)
|
||||||
|
{
|
||||||
|
input = input.Replace("\r\n", "\n");
|
||||||
|
List<string> submissionTypes = [];
|
||||||
|
|
||||||
|
// Define a regular expression pattern to match the bulleted list items
|
||||||
|
string startOfTypePattern = @"- (.+)";
|
||||||
|
Regex regex = new(startOfTypePattern);
|
||||||
|
|
||||||
|
var words = input.Split("SubmissionTypes:");
|
||||||
|
var inputAfterSubmissionTypes = words[1];
|
||||||
|
|
||||||
|
string[] lines = inputAfterSubmissionTypes.Split("\n", StringSplitOptions.RemoveEmptyEntries);
|
||||||
|
|
||||||
|
foreach (string line in lines)
|
||||||
|
{
|
||||||
|
string trimmedLine = line.Trim();
|
||||||
|
Match match = regex.Match(trimmedLine);
|
||||||
|
|
||||||
|
if (!match.Success)
|
||||||
|
break;
|
||||||
|
|
||||||
|
string type = match.Groups[1].Value.Trim();
|
||||||
|
submissionTypes.Add(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return submissionTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string extractLabelValue(string input, string label)
|
||||||
|
{
|
||||||
|
string pattern = $@"{label}: (.*?)\n";
|
||||||
|
Match match = Regex.Match(input, pattern);
|
||||||
|
|
||||||
|
if (match.Success)
|
||||||
|
{
|
||||||
|
return match.Groups[1].Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.Empty;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static IEnumerable<RubricItem> ParseRubricMarkdown(string rawMarkdown)
|
||||||
|
{
|
||||||
|
if (rawMarkdown.Trim() == string.Empty)
|
||||||
|
return [];
|
||||||
|
var lines = rawMarkdown.Trim().Split("\n");
|
||||||
|
var items = lines.Select(parseIndividualRubricItemMarkdown).ToArray();
|
||||||
|
return items;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static RubricItem parseIndividualRubricItemMarkdown(string rawMarkdown)
|
||||||
|
{
|
||||||
|
var pointsPattern = @"\s*-\s*(\d+)\s*pt(s)?:";
|
||||||
|
var match = Regex.Match(rawMarkdown, pointsPattern);
|
||||||
|
if (!match.Success)
|
||||||
|
throw new RubricMarkdownParseException($"points not found: {rawMarkdown}");
|
||||||
|
|
||||||
|
var points = int.Parse(match.Groups[1].Value);
|
||||||
|
|
||||||
|
var label = string.Join(": ", rawMarkdown.Split(": ").Skip(1));
|
||||||
|
|
||||||
|
return new RubricItem()
|
||||||
|
{
|
||||||
|
Points = points,
|
||||||
|
Label = label
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
2
build.sh
2
build.sh
@@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
|
|
||||||
MAJOR_VERSION="1"
|
MAJOR_VERSION="1"
|
||||||
MINOR_VERSION="3"
|
MINOR_VERSION="4"
|
||||||
VERSION="$MAJOR_VERSION.$MINOR_VERSION"
|
VERSION="$MAJOR_VERSION.$MINOR_VERSION"
|
||||||
|
|
||||||
dotnet publish Management.Web/ \
|
dotnet publish Management.Web/ \
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ services:
|
|||||||
- ~/projects/faculty/1400/2024_spring_alex/modules:/app/storage/1400
|
- ~/projects/faculty/1400/2024_spring_alex/modules:/app/storage/1400
|
||||||
- ~/projects/faculty/1405/2024_spring_alex/modules:/app/storage/1405
|
- ~/projects/faculty/1405/2024_spring_alex/modules:/app/storage/1405
|
||||||
- ~/projects/faculty/4620_Distributed/2024Spring/modules:/app/storage/distributed
|
- ~/projects/faculty/4620_Distributed/2024Spring/modules:/app/storage/distributed
|
||||||
|
- ~/projects/faculty/3840_Telemetry/2024Spring_alex/modules:/app/storage/telemetry_and_operations
|
||||||
|
|
||||||
collector:
|
collector:
|
||||||
image: otel/opentelemetry-collector-contrib
|
image: otel/opentelemetry-collector-contrib
|
||||||
@@ -30,22 +31,12 @@ services:
|
|||||||
- 4318:4318 # OTLP http receiver
|
- 4318:4318 # OTLP http receiver
|
||||||
- 55679:55679 # zpages extension
|
- 55679:55679 # zpages extension
|
||||||
|
|
||||||
# The zipkin process services the UI, and also exposes a POST endpoint that
|
|
||||||
# instrumentation can send trace data to.
|
|
||||||
zipkin:
|
zipkin:
|
||||||
image: ghcr.io/openzipkin/zipkin-slim
|
image: ghcr.io/openzipkin/zipkin-slim
|
||||||
container_name: zipkin
|
container_name: zipkin
|
||||||
# Environment settings are defined here https://github.com/openzipkin/zipkin/blob/master/zipkin-server/README.md#environment-variables
|
# Environment settings are defined here https://github.com/openzipkin/zipkin/blob/master/zipkin-server/README.md#environment-variables
|
||||||
environment:
|
environment:
|
||||||
- STORAGE_TYPE=mem
|
- STORAGE_TYPE=mem
|
||||||
# Point the zipkin at the storage backend
|
|
||||||
# - MYSQL_HOST=mysql
|
|
||||||
# Uncomment to enable self-tracing
|
|
||||||
# - SELF_TRACING_ENABLED=true
|
|
||||||
# Uncomment to increase heap size
|
|
||||||
# - JAVA_OPTS=-Xms128m -Xmx128m -XX:+ExitOnOutOfMemoryError
|
|
||||||
ports:
|
ports:
|
||||||
# Port used for the Zipkin UI and HTTP Api
|
|
||||||
- 9411:9411
|
- 9411:9411
|
||||||
# Uncomment to enable debug logging
|
|
||||||
# command: --logging.level.zipkin2=DEBUG
|
# command: --logging.level.zipkin2=DEBUG
|
||||||
|
|||||||
23
ops/local-dev/docker-compose.yml
Normal file
23
ops/local-dev/docker-compose.yml
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
services:
|
||||||
|
collector:
|
||||||
|
image: otel/opentelemetry-collector-contrib
|
||||||
|
volumes:
|
||||||
|
- ../otel-collector-config.yml:/etc/otelcol-contrib/config.yaml
|
||||||
|
ports:
|
||||||
|
- 1888:1888 # pprof extension
|
||||||
|
- 8888:8888 # Prometheus metrics exposed by the Collector
|
||||||
|
- 8889:8889 # Prometheus exporter metrics
|
||||||
|
- 13133:13133 # health_check extension
|
||||||
|
- 4317:4317 # OTLP gRPC receiver
|
||||||
|
- 4318:4318 # OTLP http receiver
|
||||||
|
- 55679:55679 # zpages extension
|
||||||
|
|
||||||
|
zipkin:
|
||||||
|
image: ghcr.io/openzipkin/zipkin-slim
|
||||||
|
container_name: zipkin
|
||||||
|
# Environment settings are defined here https://github.com/openzipkin/zipkin/blob/master/zipkin-server/README.md#environment-variables
|
||||||
|
environment:
|
||||||
|
- STORAGE_TYPE=mem
|
||||||
|
ports:
|
||||||
|
- 9411:9411
|
||||||
|
# command: --logging.level.zipkin2=DEBUG
|
||||||
Reference in New Issue
Block a user