mirror of
https://github.com/alexmickelson/canvasManagement.git
synced 2026-03-26 07:38:33 -06:00
only actors can save me
This commit is contained in:
@@ -63,7 +63,7 @@ public class FileStorageTests
|
|||||||
|
|
||||||
await fileManager.SaveCourseAsync(testCourse, null);
|
await fileManager.SaveCourseAsync(testCourse, null);
|
||||||
|
|
||||||
var loadedCourses = await fileManager.LoadSavedMarkdownCourses();
|
var loadedCourses = await fileManager.LoadSavedCourses();
|
||||||
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
||||||
|
|
||||||
loadedCourse.Should().BeEquivalentTo(testCourse);
|
loadedCourse.Should().BeEquivalentTo(testCourse);
|
||||||
@@ -88,7 +88,7 @@ public class FileStorageTests
|
|||||||
|
|
||||||
await fileManager.SaveCourseAsync(testCourse, null);
|
await fileManager.SaveCourseAsync(testCourse, null);
|
||||||
|
|
||||||
var loadedCourses = await fileManager.LoadSavedMarkdownCourses();
|
var loadedCourses = await fileManager.LoadSavedCourses();
|
||||||
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
||||||
|
|
||||||
loadedCourse.Settings.Should().BeEquivalentTo(testCourse.Settings);
|
loadedCourse.Settings.Should().BeEquivalentTo(testCourse.Settings);
|
||||||
@@ -113,7 +113,7 @@ public class FileStorageTests
|
|||||||
|
|
||||||
await fileManager.SaveCourseAsync(testCourse, null);
|
await fileManager.SaveCourseAsync(testCourse, null);
|
||||||
|
|
||||||
var loadedCourses = await fileManager.LoadSavedMarkdownCourses();
|
var loadedCourses = await fileManager.LoadSavedCourses();
|
||||||
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
||||||
|
|
||||||
loadedCourse.Modules.Should().BeEquivalentTo(testCourse.Modules);
|
loadedCourse.Modules.Should().BeEquivalentTo(testCourse.Modules);
|
||||||
@@ -151,7 +151,7 @@ public class FileStorageTests
|
|||||||
|
|
||||||
await fileManager.SaveCourseAsync(testCourse, null);
|
await fileManager.SaveCourseAsync(testCourse, null);
|
||||||
|
|
||||||
var loadedCourses = await fileManager.LoadSavedMarkdownCourses();
|
var loadedCourses = await fileManager.LoadSavedCourses();
|
||||||
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
||||||
|
|
||||||
var actualAssignments = loadedCourse.Modules.First().Assignments;
|
var actualAssignments = loadedCourse.Modules.First().Assignments;
|
||||||
@@ -197,7 +197,7 @@ public class FileStorageTests
|
|||||||
|
|
||||||
await fileManager.SaveCourseAsync(testCourse, null);
|
await fileManager.SaveCourseAsync(testCourse, null);
|
||||||
|
|
||||||
var loadedCourses = await fileManager.LoadSavedMarkdownCourses();
|
var loadedCourses = await fileManager.LoadSavedCourses();
|
||||||
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
||||||
|
|
||||||
loadedCourse.Modules.First().Quizzes.Should().BeEquivalentTo(testCourse.Modules.First().Quizzes);
|
loadedCourse.Modules.First().Quizzes.Should().BeEquivalentTo(testCourse.Modules.First().Quizzes);
|
||||||
@@ -264,7 +264,7 @@ public class FileStorageTests
|
|||||||
|
|
||||||
await fileManager.SaveCourseAsync(testCourse, null);
|
await fileManager.SaveCourseAsync(testCourse, null);
|
||||||
|
|
||||||
var loadedCourses = await fileManager.LoadSavedMarkdownCourses();
|
var loadedCourses = await fileManager.LoadSavedCourses();
|
||||||
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
||||||
|
|
||||||
loadedCourse.Should().BeEquivalentTo(testCourse);
|
loadedCourse.Should().BeEquivalentTo(testCourse);
|
||||||
@@ -303,7 +303,7 @@ public class FileStorageTests
|
|||||||
|
|
||||||
await fileManager.SaveCourseAsync(testCourse, null);
|
await fileManager.SaveCourseAsync(testCourse, null);
|
||||||
|
|
||||||
var loadedCourses = await fileManager.LoadSavedMarkdownCourses();
|
var loadedCourses = await fileManager.LoadSavedCourses();
|
||||||
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
var loadedCourse = loadedCourses.First(c => c.Settings.Name == testCourse.Settings.Name);
|
||||||
|
|
||||||
loadedCourse.Should().BeEquivalentTo(testCourse);
|
loadedCourse.Should().BeEquivalentTo(testCourse);
|
||||||
|
|||||||
@@ -82,7 +82,12 @@ builder.Services.AddScoped<ICanvasService, CanvasService>();
|
|||||||
|
|
||||||
builder.Services.AddScoped<MarkdownCourseSaver>();
|
builder.Services.AddScoped<MarkdownCourseSaver>();
|
||||||
builder.Services.AddScoped<CourseMarkdownLoader>();
|
builder.Services.AddScoped<CourseMarkdownLoader>();
|
||||||
builder.Services.AddScoped<IFileStorageManager,FileStorageManager>();
|
builder.Services.AddScoped<IFileStorageManager>(sp =>
|
||||||
|
{
|
||||||
|
var manager = ActivatorUtilities.CreateInstance<FileStorageManager>(sp);
|
||||||
|
var logger = sp.GetRequiredService<ILogger<FileStorageManagerCached>>();
|
||||||
|
return new FileStorageManagerCached(manager, logger);
|
||||||
|
});
|
||||||
|
|
||||||
builder.Services.AddScoped<CoursePlanner>();
|
builder.Services.AddScoped<CoursePlanner>();
|
||||||
builder.Services.AddScoped<AssignmentEditorContext>();
|
builder.Services.AddScoped<AssignmentEditorContext>();
|
||||||
|
|||||||
@@ -26,6 +26,7 @@
|
|||||||
this.InvokeAsync(updateCourses);
|
this.InvokeAsync(updateCourses);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private EnrollmentTermModel? selectedTerm
|
private EnrollmentTermModel? selectedTerm
|
||||||
{
|
{
|
||||||
get => terms?.FirstOrDefault(t => t.Id == selectedTermId);
|
get => terms?.FirstOrDefault(t => t.Id == selectedTermId);
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ public class CoursePlanner
|
|||||||
private int _debounceInterval = 1000;
|
private int _debounceInterval = 1000;
|
||||||
private LocalCourse? _localCourse { get; set; }
|
private LocalCourse? _localCourse { get; set; }
|
||||||
private LocalCourse? _lastSavedCourse { get; set; }
|
private LocalCourse? _lastSavedCourse { get; set; }
|
||||||
|
private string loadedCourseName = "";
|
||||||
public LocalCourse? LocalCourse
|
public LocalCourse? LocalCourse
|
||||||
{
|
{
|
||||||
get => _localCourse;
|
get => _localCourse;
|
||||||
@@ -48,10 +49,13 @@ public class CoursePlanner
|
|||||||
{
|
{
|
||||||
_localCourse = null;
|
_localCourse = null;
|
||||||
StateHasChanged?.Invoke();
|
StateHasChanged?.Invoke();
|
||||||
|
|
||||||
|
loadedCourseName = "";
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var verifiedCourse = value.GeneralCourseCleanup();
|
var verifiedCourse = value.GeneralCourseCleanup();
|
||||||
|
loadedCourseName = verifiedCourse.Settings.Name;
|
||||||
|
|
||||||
if (_localCourse == null)
|
if (_localCourse == null)
|
||||||
{
|
{
|
||||||
@@ -61,46 +65,54 @@ public class CoursePlanner
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
_debounceTimer?.Dispose();
|
saveCourseToFile(verifiedCourse);
|
||||||
_debounceTimer = new Timer(
|
|
||||||
async (_) => await saveCourseToFile(verifiedCourse),
|
|
||||||
null,
|
|
||||||
_debounceInterval,
|
|
||||||
Timeout.Infinite
|
|
||||||
);
|
|
||||||
|
|
||||||
_localCourse = verifiedCourse;
|
_localCourse = verifiedCourse;
|
||||||
StateHasChanged?.Invoke();
|
StateHasChanged?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task saveCourseToFile(LocalCourse courseAsOfDebounce)
|
public async Task LoadCourseByName(string courseName)
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveCourseToFile(LocalCourse courseAsOfDebounce)
|
||||||
{
|
{
|
||||||
_debounceTimer?.Dispose();
|
_debounceTimer?.Dispose();
|
||||||
|
_debounceTimer = new Timer(
|
||||||
|
async (_) =>
|
||||||
|
{
|
||||||
|
_debounceTimer?.Dispose();
|
||||||
|
|
||||||
// ignore initial load of course
|
// ignore initial load of course
|
||||||
if (LocalCourse == null)
|
if (LocalCourse == null)
|
||||||
{
|
{
|
||||||
logger.Trace("saving course as of debounce call time");
|
logger.Trace("saving course as of debounce call time");
|
||||||
await fileStorageManager.SaveCourseAsync(courseAsOfDebounce, null);
|
await fileStorageManager.SaveCourseAsync(courseAsOfDebounce, null);
|
||||||
_lastSavedCourse = courseAsOfDebounce;
|
_lastSavedCourse = courseAsOfDebounce;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (_lastSavedCourse == null)
|
if (_lastSavedCourse == null)
|
||||||
{
|
{
|
||||||
logger.Trace("not saving course, no prevous saved course");
|
logger.Trace("not saving course, no prevous saved course");
|
||||||
_lastSavedCourse = LocalCourse ?? courseAsOfDebounce;
|
_lastSavedCourse = LocalCourse ?? courseAsOfDebounce;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
logger.Trace("Saving latest version of file");
|
logger.Trace("Saving latest version of file");
|
||||||
var courseToSave = LocalCourse;
|
var courseToSave = LocalCourse;
|
||||||
await fileStorageManager.SaveCourseAsync(courseToSave, _lastSavedCourse);
|
await fileStorageManager.SaveCourseAsync(courseToSave, _lastSavedCourse);
|
||||||
_lastSavedCourse = courseToSave;
|
_lastSavedCourse = courseToSave;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
null,
|
||||||
|
_debounceInterval,
|
||||||
|
Timeout.Infinite
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public event Action? StateHasChanged;
|
public event Action? StateHasChanged;
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
using LocalModels;
|
using LocalModels;
|
||||||
using Management.Services;
|
using Management.Services;
|
||||||
|
|
||||||
public interface IFileStorageManager
|
|
||||||
{
|
|
||||||
Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse);
|
|
||||||
Task<IEnumerable<LocalCourse>> LoadSavedCourses();
|
|
||||||
Task<IEnumerable<LocalCourse>> LoadSavedMarkdownCourses();
|
|
||||||
IEnumerable<string> GetEmptyDirectories();
|
|
||||||
}
|
|
||||||
|
|
||||||
public class FileStorageManager : IFileStorageManager
|
public class FileStorageManager : IFileStorageManager
|
||||||
{
|
{
|
||||||
private readonly MyLogger<FileStorageManager> logger;
|
private readonly MyLogger<FileStorageManager> logger;
|
||||||
@@ -44,13 +36,7 @@ public class FileStorageManager : IFileStorageManager
|
|||||||
|
|
||||||
public async Task<IEnumerable<LocalCourse>> LoadSavedCourses()
|
public async Task<IEnumerable<LocalCourse>> LoadSavedCourses()
|
||||||
{
|
{
|
||||||
return await LoadSavedMarkdownCourses();
|
return await _courseMarkdownLoader.LoadSavedCourses();
|
||||||
}
|
|
||||||
|
|
||||||
public async Task<IEnumerable<LocalCourse>> LoadSavedMarkdownCourses()
|
|
||||||
{
|
|
||||||
|
|
||||||
return await _courseMarkdownLoader.LoadSavedMarkdownCourses();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IEnumerable<string> GetEmptyDirectories()
|
public IEnumerable<string> GetEmptyDirectories()
|
||||||
|
|||||||
50
Management/Services/Files/FileStorageManagerCached.cs
Normal file
50
Management/Services/Files/FileStorageManagerCached.cs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using LocalModels;
|
||||||
|
|
||||||
|
public class FileStorageManagerCached : IFileStorageManager
|
||||||
|
{
|
||||||
|
private readonly FileStorageManager manager;
|
||||||
|
|
||||||
|
private readonly object cacheLock = new object(); // Lock object for synchronization
|
||||||
|
|
||||||
|
|
||||||
|
private DateTime? cacheTime { get; set; } = null;
|
||||||
|
private IEnumerable<LocalCourse>? cachedCourses { get; set; } = null;
|
||||||
|
private ILogger<FileStorageManagerCached> logger { get; }
|
||||||
|
|
||||||
|
private readonly int cacheSeconds = 2;
|
||||||
|
public FileStorageManagerCached(FileStorageManager manager, ILogger<FileStorageManagerCached> logger)
|
||||||
|
{
|
||||||
|
this.manager = manager;
|
||||||
|
this.logger = logger;
|
||||||
|
}
|
||||||
|
public IEnumerable<string> GetEmptyDirectories()
|
||||||
|
{
|
||||||
|
return manager.GetEmptyDirectories();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<LocalCourse>> LoadSavedCourses()
|
||||||
|
{
|
||||||
|
|
||||||
|
var secondsFromLastLoad = (DateTime.Now - cacheTime)?.Seconds;
|
||||||
|
|
||||||
|
if (cachedCourses != null && secondsFromLastLoad < cacheSeconds)
|
||||||
|
{
|
||||||
|
logger.LogInformation("returning cached courses from file");
|
||||||
|
return cachedCourses;
|
||||||
|
}
|
||||||
|
|
||||||
|
cachedCourses = await manager.LoadSavedCourses();
|
||||||
|
cacheTime = DateTime.Now;
|
||||||
|
return cachedCourses;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse)
|
||||||
|
{
|
||||||
|
// race condition...
|
||||||
|
cacheTime = null;
|
||||||
|
cachedCourses = null;
|
||||||
|
await manager.SaveCourseAsync(course, previouslyStoredCourse);
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Management/Services/Files/IFileStorageManager.cs
Normal file
8
Management/Services/Files/IFileStorageManager.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using LocalModels;
|
||||||
|
|
||||||
|
public interface IFileStorageManager
|
||||||
|
{
|
||||||
|
Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse);
|
||||||
|
Task<IEnumerable<LocalCourse>> LoadSavedCourses();
|
||||||
|
IEnumerable<string> GetEmptyDirectories();
|
||||||
|
}
|
||||||
@@ -12,7 +12,7 @@ public class CourseMarkdownLoader
|
|||||||
_basePath = fileConfig.GetBasePath();
|
_basePath = fileConfig.GetBasePath();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<IEnumerable<LocalCourse>> LoadSavedMarkdownCourses()
|
public async Task<IEnumerable<LocalCourse>> LoadSavedCourses()
|
||||||
{
|
{
|
||||||
var courseDirectories = Directory.GetDirectories(_basePath);
|
var courseDirectories = Directory.GetDirectories(_basePath);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user