only actors can save me

This commit is contained in:
2024-05-03 16:12:24 -06:00
parent 7bb4744f5c
commit aab38c3e9b
8 changed files with 114 additions and 52 deletions

View File

@@ -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);

View File

@@ -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>();

View File

@@ -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);

View File

@@ -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;

View File

@@ -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()

View 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);
}
}

View File

@@ -0,0 +1,8 @@
using LocalModels;
public interface IFileStorageManager
{
Task SaveCourseAsync(LocalCourse course, LocalCourse? previouslyStoredCourse);
Task<IEnumerable<LocalCourse>> LoadSavedCourses();
IEnumerable<string> GetEmptyDirectories();
}

View File

@@ -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);