From aab38c3e9b2590e191ebf2d000bc0f38637c578a Mon Sep 17 00:00:00 2001 From: Alex Mickelson Date: Fri, 3 May 2024 16:12:24 -0600 Subject: [PATCH] only actors can save me --- Management.Test/Markdown/FileStorageTests.cs | 14 ++-- Management.Web/Program.cs | 7 +- .../Shared/InitializeNewCourse.razor | 1 + .../Features/Configuration/CoursePlanner.cs | 68 +++++++++++-------- .../Services/Files/FileStorageManager.cs | 16 +---- .../Files/FileStorageManagerCached.cs | 50 ++++++++++++++ .../Services/Files/IFileStorageManager.cs | 8 +++ .../Services/Files/LoadMarkdownCourse.cs | 2 +- 8 files changed, 114 insertions(+), 52 deletions(-) create mode 100644 Management/Services/Files/FileStorageManagerCached.cs create mode 100644 Management/Services/Files/IFileStorageManager.cs diff --git a/Management.Test/Markdown/FileStorageTests.cs b/Management.Test/Markdown/FileStorageTests.cs index 4d26e72..e72056e 100644 --- a/Management.Test/Markdown/FileStorageTests.cs +++ b/Management.Test/Markdown/FileStorageTests.cs @@ -63,7 +63,7 @@ public class FileStorageTests 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); loadedCourse.Should().BeEquivalentTo(testCourse); @@ -88,7 +88,7 @@ public class FileStorageTests 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); loadedCourse.Settings.Should().BeEquivalentTo(testCourse.Settings); @@ -113,7 +113,7 @@ public class FileStorageTests 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); loadedCourse.Modules.Should().BeEquivalentTo(testCourse.Modules); @@ -151,7 +151,7 @@ public class FileStorageTests 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 actualAssignments = loadedCourse.Modules.First().Assignments; @@ -197,7 +197,7 @@ public class FileStorageTests 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); loadedCourse.Modules.First().Quizzes.Should().BeEquivalentTo(testCourse.Modules.First().Quizzes); @@ -264,7 +264,7 @@ public class FileStorageTests 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); loadedCourse.Should().BeEquivalentTo(testCourse); @@ -303,7 +303,7 @@ public class FileStorageTests 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); loadedCourse.Should().BeEquivalentTo(testCourse); diff --git a/Management.Web/Program.cs b/Management.Web/Program.cs index 51b5ec3..1d6e128 100644 --- a/Management.Web/Program.cs +++ b/Management.Web/Program.cs @@ -82,7 +82,12 @@ builder.Services.AddScoped(); builder.Services.AddScoped(); builder.Services.AddScoped(); -builder.Services.AddScoped(); +builder.Services.AddScoped(sp => +{ + var manager = ActivatorUtilities.CreateInstance(sp); + var logger = sp.GetRequiredService>(); + return new FileStorageManagerCached(manager, logger); +}); builder.Services.AddScoped(); builder.Services.AddScoped(); diff --git a/Management.Web/Shared/InitializeNewCourse.razor b/Management.Web/Shared/InitializeNewCourse.razor index 01c3a7a..144b227 100644 --- a/Management.Web/Shared/InitializeNewCourse.razor +++ b/Management.Web/Shared/InitializeNewCourse.razor @@ -26,6 +26,7 @@ this.InvokeAsync(updateCourses); } } + private EnrollmentTermModel? selectedTerm { get => terms?.FirstOrDefault(t => t.Id == selectedTermId); diff --git a/Management/Features/Configuration/CoursePlanner.cs b/Management/Features/Configuration/CoursePlanner.cs index 0302120..a8897ce 100644 --- a/Management/Features/Configuration/CoursePlanner.cs +++ b/Management/Features/Configuration/CoursePlanner.cs @@ -39,6 +39,7 @@ public class CoursePlanner private int _debounceInterval = 1000; private LocalCourse? _localCourse { get; set; } private LocalCourse? _lastSavedCourse { get; set; } + private string loadedCourseName = ""; public LocalCourse? LocalCourse { get => _localCourse; @@ -48,10 +49,13 @@ public class CoursePlanner { _localCourse = null; StateHasChanged?.Invoke(); + + loadedCourseName = ""; return; } var verifiedCourse = value.GeneralCourseCleanup(); + loadedCourseName = verifiedCourse.Settings.Name; if (_localCourse == null) { @@ -61,46 +65,54 @@ public class CoursePlanner return; } - _debounceTimer?.Dispose(); - _debounceTimer = new Timer( - async (_) => await saveCourseToFile(verifiedCourse), - null, - _debounceInterval, - Timeout.Infinite - ); + saveCourseToFile(verifiedCourse); _localCourse = verifiedCourse; StateHasChanged?.Invoke(); } } - private async Task saveCourseToFile(LocalCourse courseAsOfDebounce) + public async Task LoadCourseByName(string courseName) + { + + } + + private void saveCourseToFile(LocalCourse courseAsOfDebounce) { _debounceTimer?.Dispose(); + _debounceTimer = new Timer( + async (_) => + { + _debounceTimer?.Dispose(); - // ignore initial load of course - if (LocalCourse == null) - { - logger.Trace("saving course as of debounce call time"); - await fileStorageManager.SaveCourseAsync(courseAsOfDebounce, null); - _lastSavedCourse = courseAsOfDebounce; - } - else - { - if (_lastSavedCourse == null) - { - logger.Trace("not saving course, no prevous saved course"); - _lastSavedCourse = LocalCourse ?? courseAsOfDebounce; - return; - } + // ignore initial load of course + if (LocalCourse == null) + { + logger.Trace("saving course as of debounce call time"); + await fileStorageManager.SaveCourseAsync(courseAsOfDebounce, null); + _lastSavedCourse = courseAsOfDebounce; + } + else + { + if (_lastSavedCourse == null) + { + logger.Trace("not saving course, no prevous saved course"); + _lastSavedCourse = LocalCourse ?? courseAsOfDebounce; + return; + } - logger.Trace("Saving latest version of file"); - var courseToSave = LocalCourse; - await fileStorageManager.SaveCourseAsync(courseToSave, _lastSavedCourse); - _lastSavedCourse = courseToSave; + logger.Trace("Saving latest version of file"); + var courseToSave = LocalCourse; + await fileStorageManager.SaveCourseAsync(courseToSave, _lastSavedCourse); + _lastSavedCourse = courseToSave; - } + } + }, + null, + _debounceInterval, + Timeout.Infinite + ); } public event Action? StateHasChanged; diff --git a/Management/Services/Files/FileStorageManager.cs b/Management/Services/Files/FileStorageManager.cs index cfd7a65..bd48c0e 100644 --- a/Management/Services/Files/FileStorageManager.cs +++ b/Management/Services/Files/FileStorageManager.cs @@ -1,14 +1,6 @@ using LocalModels; using Management.Services; -public interface IFileStorageManager -{ - Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse); - Task> LoadSavedCourses(); - Task> LoadSavedMarkdownCourses(); - IEnumerable GetEmptyDirectories(); -} - public class FileStorageManager : IFileStorageManager { private readonly MyLogger logger; @@ -44,13 +36,7 @@ public class FileStorageManager : IFileStorageManager public async Task> LoadSavedCourses() { - return await LoadSavedMarkdownCourses(); - } - - public async Task> LoadSavedMarkdownCourses() - { - - return await _courseMarkdownLoader.LoadSavedMarkdownCourses(); + return await _courseMarkdownLoader.LoadSavedCourses(); } public IEnumerable GetEmptyDirectories() diff --git a/Management/Services/Files/FileStorageManagerCached.cs b/Management/Services/Files/FileStorageManagerCached.cs new file mode 100644 index 0000000..be4ff02 --- /dev/null +++ b/Management/Services/Files/FileStorageManagerCached.cs @@ -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? cachedCourses { get; set; } = null; + private ILogger logger { get; } + + private readonly int cacheSeconds = 2; + public FileStorageManagerCached(FileStorageManager manager, ILogger logger) + { + this.manager = manager; + this.logger = logger; + } + public IEnumerable GetEmptyDirectories() + { + return manager.GetEmptyDirectories(); + } + + public async Task> 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); + } +} diff --git a/Management/Services/Files/IFileStorageManager.cs b/Management/Services/Files/IFileStorageManager.cs new file mode 100644 index 0000000..dfc5c11 --- /dev/null +++ b/Management/Services/Files/IFileStorageManager.cs @@ -0,0 +1,8 @@ +using LocalModels; + +public interface IFileStorageManager +{ + Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse); + Task> LoadSavedCourses(); + IEnumerable GetEmptyDirectories(); +} diff --git a/Management/Services/Files/LoadMarkdownCourse.cs b/Management/Services/Files/LoadMarkdownCourse.cs index e4aa5b1..1a4d16f 100644 --- a/Management/Services/Files/LoadMarkdownCourse.cs +++ b/Management/Services/Files/LoadMarkdownCourse.cs @@ -12,7 +12,7 @@ public class CourseMarkdownLoader _basePath = fileConfig.GetBasePath(); } - public async Task> LoadSavedMarkdownCourses() + public async Task> LoadSavedCourses() { var courseDirectories = Directory.GetDirectories(_basePath);