Files
poprhythmandCursor 4cfd85b902 feat: import leftover student fields into notes and show them on the roster
Store leftover CSV columns on hidden student notes, move catalog import to /events/import, and persist Students index columns from Chapter Settings.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-08-29 23:55:03 -04:00

80 lines
2.6 KiB
C#

using System.Text.Json;
using System.Text.Json.Nodes;
using WebApp.Models;
namespace WebApp.Services;
/// <summary>
/// Persists chapter settings to <c>Data/appsettings.json</c>.
/// </summary>
public class ChapterSettingsWriter : IChapterSettingsWriter
{
private static readonly JsonSerializerOptions JsonOptions = new()
{
WriteIndented = true
};
private readonly IWebHostEnvironment _environment;
private readonly IConfiguration _configuration;
private readonly ILogger<ChapterSettingsWriter> _logger;
public ChapterSettingsWriter(
IWebHostEnvironment environment,
IConfiguration configuration,
ILogger<ChapterSettingsWriter> logger)
{
_environment = environment;
_configuration = configuration;
_logger = logger;
}
public async Task WriteAsync(ChapterSettings settings, CancellationToken cancellationToken = default)
{
ArgumentNullException.ThrowIfNull(settings);
var appSettingsPath = GetAppSettingsPath();
var dataDir = Path.GetDirectoryName(appSettingsPath);
if (dataDir != null && !Directory.Exists(dataDir))
Directory.CreateDirectory(dataDir);
JsonObject root;
if (File.Exists(appSettingsPath))
{
var existingJson = await File.ReadAllTextAsync(appSettingsPath, cancellationToken);
root = JsonNode.Parse(existingJson) as JsonObject ?? [];
}
else
{
root = [];
}
var incoming = JsonSerializer.SerializeToNode(settings, JsonOptions) as JsonObject ?? [];
if (root["ChapterSettings"] is JsonObject existing)
{
foreach (var property in incoming)
existing[property.Key] = property.Value?.DeepClone();
}
else
{
root["ChapterSettings"] = incoming;
}
await File.WriteAllTextAsync(appSettingsPath, root.ToJsonString(JsonOptions), cancellationToken);
if (_configuration is IConfigurationRoot configurationRoot)
configurationRoot.Reload();
_logger.LogInformation("Chapter settings written to {Path}", appSettingsPath);
}
public async Task UpdateCompetitionYearAsync(string competitionYear, CancellationToken cancellationToken = default)
{
var settings = ChapterSettings.FromConfiguration(_configuration);
settings.CompetitionYear = competitionYear;
await WriteAsync(settings, cancellationToken);
}
private string GetAppSettingsPath() =>
Path.Combine(_environment.ContentRootPath, "Data", "appsettings.json");
}