refactor(bootstrap): finalize load-index migration; GlobalsImporter is now a stub
Stage 9C of the bootstrap-seed-refactor: - Add 6 seed DTOs for the card-id-keyed load-index tables (SpotCard, ReprintedCard, UnlimitedRestriction, LoadingExclusionCard, MaintenanceCard, FeatureMaintenance). - Add CardListsImporter: idempotent upsert of the 6 tables, sharing one Cards FK set for orphan-warning. FeatureMaintenances clear-and-rewrites (synthetic ordinal Id; no natural key). - Add RotationFlagUpdater: reads RotationConfig.RotationCardSetIds from the GameConfigs section (populated by RotationConfigImporter) and flips CardSet.IsInRotation to match. - Add RotationConfig.RotationCardSetIds list property + wire it through RotationConfigImporter. No migration needed (sections are JSON blobs). - RotationConfigImporter: use legacy local-kind DateTime parse for schedule windows so the JSON round-trip stays byte-equivalent to GlobalsImporter. - Strip GlobalsImporter down to a no-op stub (Task 10 will delete it). - Wire all 9 new importers into Program.cs and SVSimTestFactory.SeedGlobalsAsync, in the order RotationConfigImporter -> ... -> CardListsImporter -> RotationFlagUpdater. - Delete prod-captures/load-index-2026-05-23.json. - Add CardListsImporterTests covering each sub-table, idempotency, empty-seed handling, orphan-warning, and the clear-and-rewrite path. Tests: 391 passing (382 baseline + 9 new). Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
64
SVSim.Bootstrap/Importers/RotationFlagUpdater.cs
Normal file
64
SVSim.Bootstrap/Importers/RotationFlagUpdater.cs
Normal file
@@ -0,0 +1,64 @@
|
||||
using System.Text.Json;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using SVSim.Database;
|
||||
using SVSim.Database.Models.Config;
|
||||
|
||||
namespace SVSim.Bootstrap.Importers;
|
||||
|
||||
/// <summary>
|
||||
/// Reads <see cref="RotationConfig"/> from the GameConfigs table (populated by
|
||||
/// <see cref="RotationConfigImporter"/>) and flips <c>CardSet.IsInRotation</c> to match.
|
||||
/// Must run after RotationConfigImporter and CardImporter — CardSets missing from the DB
|
||||
/// can't be promoted (the original GlobalsImporter behavior; we log a warning instead of failing).
|
||||
/// </summary>
|
||||
public class RotationFlagUpdater
|
||||
{
|
||||
public async Task<int> UpdateAsync(SVSimDbContext context)
|
||||
{
|
||||
var sectionName = typeof(RotationConfig).GetCustomAttributes(typeof(ConfigSectionAttribute), inherit: false)
|
||||
.Cast<ConfigSectionAttribute>().FirstOrDefault()?.Name
|
||||
?? throw new InvalidOperationException("RotationConfig missing [ConfigSection]");
|
||||
|
||||
var row = await context.GameConfigs.FirstOrDefaultAsync(s => s.SectionName == sectionName);
|
||||
if (row is null)
|
||||
{
|
||||
Console.WriteLine("[RotationFlagUpdater] No Rotation section in GameConfigs; skipping.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var cfg = JsonSerializer.Deserialize<RotationConfig>(row.ValueJson);
|
||||
if (cfg is null)
|
||||
{
|
||||
Console.WriteLine("[RotationFlagUpdater] Failed to deserialize RotationConfig; skipping.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var rotationSet = (cfg.RotationCardSetIds ?? new List<int>()).ToHashSet();
|
||||
if (rotationSet.Count == 0)
|
||||
{
|
||||
Console.WriteLine("[RotationFlagUpdater] RotationCardSetIds empty; no flag changes.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
var allSets = await context.CardSets.ToListAsync();
|
||||
int updated = 0, missing = 0;
|
||||
foreach (var rid in rotationSet)
|
||||
{
|
||||
var set = allSets.FirstOrDefault(s => s.Id == rid);
|
||||
if (set is null) { missing++; continue; }
|
||||
if (!set.IsInRotation) { set.IsInRotation = true; updated++; }
|
||||
}
|
||||
// Demote sets not in the current rotation.
|
||||
foreach (var s in allSets.Where(s => s.IsInRotation && !rotationSet.Contains(s.Id)))
|
||||
{
|
||||
s.IsInRotation = false;
|
||||
updated++;
|
||||
}
|
||||
if (missing > 0)
|
||||
Console.Error.WriteLine($"[RotationFlagUpdater] Warning: {missing} rotation card_set_id(s) missing from CardSets — run CardImporter first.");
|
||||
|
||||
await context.SaveChangesAsync();
|
||||
Console.WriteLine($"[RotationFlagUpdater] CardSet.IsInRotation ~{updated}");
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user