Implement critical editor override state
This commit is contained in:
@@ -296,7 +296,7 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
public async Task<CriticalCellEditorResponse?> ReparseCriticalCellAsync(
|
||||
string slug,
|
||||
int resultId,
|
||||
string rawCellText,
|
||||
CriticalCellUpdateRequest currentState,
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
await using var dbContext = await dbContextFactory.CreateDbContextAsync(cancellationToken);
|
||||
@@ -319,8 +319,10 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
}
|
||||
|
||||
var affixLegend = await BuildSharedAffixLegendAsync(dbContext, result.CriticalTableId, cancellationToken);
|
||||
var content = SharedParsing.CriticalCellTextParser.Parse(rawCellText, affixLegend);
|
||||
return CreateCellEditorResponse(result, content);
|
||||
var content = SharedParsing.CriticalCellTextParser.Parse(currentState.RawCellText, affixLegend);
|
||||
var generatedState = CreateGeneratedEditorState(content);
|
||||
var mergedState = MergeGeneratedState(currentState, generatedState);
|
||||
return CreateCellEditorResponse(result, mergedState, content.ValidationErrors);
|
||||
}
|
||||
|
||||
public async Task<CriticalCellEditorResponse?> UpdateCriticalCellAsync(
|
||||
@@ -354,14 +356,14 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
result.DescriptionText = request.DescriptionText.Trim();
|
||||
result.RawAffixText = NormalizeOptionalText(request.RawAffixText);
|
||||
result.ParseStatus = request.ParseStatus.Trim();
|
||||
result.ParsedJson = string.IsNullOrWhiteSpace(request.ParsedJson) ? "{}" : request.ParsedJson.Trim();
|
||||
result.ParsedJson = CriticalCellEditorSnapshot.FromRequest(request).ToJson();
|
||||
|
||||
ReplaceBaseEffects(dbContext, result, request.Effects);
|
||||
ReplaceBranches(dbContext, result, request.Branches);
|
||||
|
||||
await dbContext.SaveChangesAsync(cancellationToken);
|
||||
|
||||
return CreateCellEditorResponse(result);
|
||||
return CreateCellEditorResponse(result, request, []);
|
||||
}
|
||||
|
||||
private static IReadOnlyList<CriticalTableLegendEntry> BuildLegend(IReadOnlyList<CriticalTableCellDetail> cells)
|
||||
@@ -422,37 +424,20 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
effect.SourceType,
|
||||
effect.SourceText);
|
||||
|
||||
private static CriticalCellEditorResponse CreateCellEditorResponse(CriticalResult result) =>
|
||||
new(
|
||||
result.Id,
|
||||
result.CriticalTable.Slug,
|
||||
result.CriticalTable.DisplayName,
|
||||
result.CriticalTable.SourceDocument,
|
||||
result.CriticalRollBand.Label,
|
||||
result.CriticalGroup?.GroupKey,
|
||||
result.CriticalGroup?.Label,
|
||||
result.CriticalColumn.ColumnKey,
|
||||
result.CriticalColumn.Label,
|
||||
result.CriticalColumn.Role,
|
||||
result.RawCellText,
|
||||
result.DescriptionText,
|
||||
result.RawAffixText,
|
||||
result.ParseStatus,
|
||||
result.ParsedJson,
|
||||
[],
|
||||
result.Effects
|
||||
.OrderBy(effect => effect.Id)
|
||||
.Select(CreateEffectEditorItem)
|
||||
.ToList(),
|
||||
result.Branches
|
||||
.OrderBy(branch => branch.SortOrder)
|
||||
.Select(CreateBranchEditorItem)
|
||||
.ToList());
|
||||
private static CriticalCellEditorResponse CreateCellEditorResponse(CriticalResult result)
|
||||
{
|
||||
var state = CreateCurrentEditorState(result);
|
||||
return CreateCellEditorResponse(result, state, []);
|
||||
}
|
||||
|
||||
private static CriticalCellEditorResponse CreateCellEditorResponse(
|
||||
CriticalResult result,
|
||||
SharedParsing.CriticalCellParseContent content) =>
|
||||
new(
|
||||
CriticalCellUpdateRequest state,
|
||||
IReadOnlyList<string> validationMessages)
|
||||
{
|
||||
var snapshotJson = CriticalCellEditorSnapshot.FromRequest(state).ToJson();
|
||||
|
||||
return new(
|
||||
result.Id,
|
||||
result.CriticalTable.Slug,
|
||||
result.CriticalTable.DisplayName,
|
||||
@@ -463,19 +448,19 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
result.CriticalColumn.ColumnKey,
|
||||
result.CriticalColumn.Label,
|
||||
result.CriticalColumn.Role,
|
||||
content.RawCellText,
|
||||
content.DescriptionText,
|
||||
content.RawAffixText,
|
||||
ResolveParseStatus(content.Effects, content.Branches),
|
||||
SerializeParsedEffects(content.Effects),
|
||||
content.ValidationErrors.ToList(),
|
||||
content.Effects
|
||||
.Select(CreateEffectEditorItem)
|
||||
.ToList(),
|
||||
content.Branches
|
||||
.OrderBy(branch => branch.SortOrder)
|
||||
.Select(CreateBranchEditorItem)
|
||||
.ToList());
|
||||
state.RawCellText,
|
||||
state.DescriptionText,
|
||||
state.RawAffixText,
|
||||
state.ParseStatus,
|
||||
snapshotJson,
|
||||
state.IsDescriptionOverridden,
|
||||
state.IsRawAffixTextOverridden,
|
||||
state.AreEffectsOverridden,
|
||||
state.AreBranchesOverridden,
|
||||
validationMessages.ToList(),
|
||||
state.Effects.ToList(),
|
||||
state.Branches.ToList());
|
||||
}
|
||||
|
||||
private static CriticalBranchLookupResponse CreateBranchLookupResponse(CriticalBranch branch) =>
|
||||
new(
|
||||
@@ -491,8 +476,11 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
branch.RawText,
|
||||
branch.SortOrder);
|
||||
|
||||
private static CriticalBranchEditorItem CreateBranchEditorItem(CriticalBranch branch) =>
|
||||
new(
|
||||
private static CriticalBranchEditorItem CreateBranchEditorItem(CriticalBranch branch, int branchIndex)
|
||||
{
|
||||
var originKey = CreateBranchOriginKey(branchIndex, branch.BranchKind, branch.ConditionKey, branch.ConditionText);
|
||||
|
||||
return new(
|
||||
branch.BranchKind,
|
||||
branch.ConditionKey,
|
||||
branch.ConditionText,
|
||||
@@ -502,13 +490,20 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
branch.RawAffixText,
|
||||
branch.ParsedJson,
|
||||
branch.SortOrder,
|
||||
originKey,
|
||||
false,
|
||||
false,
|
||||
(branch.Effects ?? Enumerable.Empty<CriticalEffect>())
|
||||
.OrderBy(effect => effect.Id)
|
||||
.Select(CreateEffectEditorItem)
|
||||
.Select((effect, effectIndex) => CreateEffectEditorItem(effect, CreateBranchEffectOriginKey(originKey, effectIndex, effect.EffectCode)))
|
||||
.ToList());
|
||||
}
|
||||
|
||||
private static CriticalBranchEditorItem CreateBranchEditorItem(SharedParsing.ParsedCriticalBranch branch) =>
|
||||
new(
|
||||
private static CriticalBranchEditorItem CreateBranchEditorItem(SharedParsing.ParsedCriticalBranch branch, int branchIndex)
|
||||
{
|
||||
var originKey = CreateBranchOriginKey(branchIndex, branch.BranchKind, branch.ConditionKey, branch.ConditionText);
|
||||
|
||||
return new(
|
||||
branch.BranchKind,
|
||||
branch.ConditionKey,
|
||||
branch.ConditionText,
|
||||
@@ -518,11 +513,15 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
branch.RawAffixText,
|
||||
SerializeParsedEffects(branch.Effects),
|
||||
branch.SortOrder,
|
||||
originKey,
|
||||
false,
|
||||
false,
|
||||
branch.Effects
|
||||
.Select(CreateEffectEditorItem)
|
||||
.Select((effect, effectIndex) => CreateEffectEditorItem(effect, CreateBranchEffectOriginKey(originKey, effectIndex, effect.EffectCode)))
|
||||
.ToList());
|
||||
}
|
||||
|
||||
private static CriticalEffectEditorItem CreateEffectEditorItem(CriticalEffect effect) =>
|
||||
private static CriticalEffectEditorItem CreateEffectEditorItem(CriticalEffect effect, string originKey) =>
|
||||
new(
|
||||
effect.EffectCode,
|
||||
effect.Target,
|
||||
@@ -535,9 +534,11 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
effect.BodyPart,
|
||||
effect.IsPermanent,
|
||||
effect.SourceType,
|
||||
effect.SourceText);
|
||||
effect.SourceText,
|
||||
originKey,
|
||||
false);
|
||||
|
||||
private static CriticalEffectEditorItem CreateEffectEditorItem(SharedParsing.ParsedCriticalEffect effect) =>
|
||||
private static CriticalEffectEditorItem CreateEffectEditorItem(SharedParsing.ParsedCriticalEffect effect, string originKey) =>
|
||||
new(
|
||||
effect.EffectCode,
|
||||
effect.Target,
|
||||
@@ -550,7 +551,192 @@ public sealed class LookupService(IDbContextFactory<RolemasterDbContext> dbConte
|
||||
effect.BodyPart,
|
||||
effect.IsPermanent,
|
||||
effect.SourceType,
|
||||
effect.SourceText);
|
||||
effect.SourceText,
|
||||
originKey,
|
||||
false);
|
||||
|
||||
private static CriticalCellUpdateRequest CreateCurrentEditorState(CriticalResult result)
|
||||
{
|
||||
if (CriticalCellEditorSnapshot.TryParse(result.ParsedJson, out var snapshot) && snapshot is not null)
|
||||
{
|
||||
return new CriticalCellUpdateRequest(
|
||||
result.RawCellText,
|
||||
result.DescriptionText,
|
||||
result.RawAffixText,
|
||||
result.ParseStatus,
|
||||
result.ParsedJson,
|
||||
snapshot.IsDescriptionOverridden,
|
||||
snapshot.IsRawAffixTextOverridden,
|
||||
snapshot.AreEffectsOverridden,
|
||||
snapshot.AreBranchesOverridden,
|
||||
snapshot.Effects.ToList(),
|
||||
snapshot.Branches.ToList());
|
||||
}
|
||||
|
||||
var effects = result.Effects
|
||||
.OrderBy(effect => effect.Id)
|
||||
.Select((effect, effectIndex) => CreateEffectEditorItem(effect, CreateBaseEffectOriginKey(effectIndex, effect.EffectCode)))
|
||||
.ToList();
|
||||
var branches = result.Branches
|
||||
.OrderBy(branch => branch.SortOrder)
|
||||
.Select((branch, branchIndex) => CreateBranchEditorItem(branch, branchIndex))
|
||||
.ToList();
|
||||
|
||||
return new CriticalCellUpdateRequest(
|
||||
result.RawCellText,
|
||||
result.DescriptionText,
|
||||
result.RawAffixText,
|
||||
result.ParseStatus,
|
||||
result.ParsedJson,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
effects,
|
||||
branches);
|
||||
}
|
||||
|
||||
private static CriticalCellUpdateRequest CreateGeneratedEditorState(SharedParsing.CriticalCellParseContent content)
|
||||
{
|
||||
var effects = content.Effects
|
||||
.Select((effect, effectIndex) => CreateEffectEditorItem(effect, CreateBaseEffectOriginKey(effectIndex, effect.EffectCode)))
|
||||
.ToList();
|
||||
var branches = content.Branches
|
||||
.OrderBy(branch => branch.SortOrder)
|
||||
.Select((branch, branchIndex) => CreateBranchEditorItem(branch, branchIndex))
|
||||
.ToList();
|
||||
|
||||
return new CriticalCellUpdateRequest(
|
||||
content.RawCellText,
|
||||
content.DescriptionText,
|
||||
content.RawAffixText,
|
||||
ResolveParseStatus(content.Effects, content.Branches),
|
||||
SerializeParsedEffects(content.Effects),
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
effects,
|
||||
branches);
|
||||
}
|
||||
|
||||
private static CriticalCellUpdateRequest MergeGeneratedState(
|
||||
CriticalCellUpdateRequest currentState,
|
||||
CriticalCellUpdateRequest generatedState) =>
|
||||
new(
|
||||
currentState.RawCellText,
|
||||
currentState.IsDescriptionOverridden ? currentState.DescriptionText : generatedState.DescriptionText,
|
||||
currentState.IsRawAffixTextOverridden ? currentState.RawAffixText : generatedState.RawAffixText,
|
||||
generatedState.ParseStatus,
|
||||
generatedState.ParsedJson,
|
||||
currentState.IsDescriptionOverridden,
|
||||
currentState.IsRawAffixTextOverridden,
|
||||
currentState.AreEffectsOverridden,
|
||||
currentState.AreBranchesOverridden,
|
||||
currentState.AreEffectsOverridden
|
||||
? currentState.Effects.ToList()
|
||||
: MergeEffectItems(currentState.Effects, generatedState.Effects),
|
||||
currentState.AreBranchesOverridden
|
||||
? currentState.Branches
|
||||
.OrderBy(branch => branch.SortOrder)
|
||||
.ToList()
|
||||
: MergeBranchItems(currentState.Branches, generatedState.Branches));
|
||||
|
||||
private static List<CriticalBranchEditorItem> MergeBranchItems(
|
||||
IReadOnlyList<CriticalBranchEditorItem> currentBranches,
|
||||
IReadOnlyList<CriticalBranchEditorItem> generatedBranches)
|
||||
{
|
||||
var currentByOrigin = currentBranches
|
||||
.Where(branch => !string.IsNullOrWhiteSpace(branch.OriginKey))
|
||||
.ToDictionary(branch => branch.OriginKey!, StringComparer.Ordinal);
|
||||
var merged = new List<CriticalBranchEditorItem>(generatedBranches.Count);
|
||||
var matchedOrigins = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
foreach (var generatedBranch in generatedBranches)
|
||||
{
|
||||
if (generatedBranch.OriginKey is not null &&
|
||||
currentByOrigin.TryGetValue(generatedBranch.OriginKey, out var currentBranch))
|
||||
{
|
||||
matchedOrigins.Add(generatedBranch.OriginKey);
|
||||
|
||||
if (currentBranch.IsOverridden)
|
||||
{
|
||||
merged.Add(currentBranch);
|
||||
continue;
|
||||
}
|
||||
|
||||
merged.Add(generatedBranch with
|
||||
{
|
||||
AreEffectsOverridden = currentBranch.AreEffectsOverridden,
|
||||
Effects = currentBranch.AreEffectsOverridden
|
||||
? currentBranch.Effects.ToList()
|
||||
: MergeEffectItems(currentBranch.Effects, generatedBranch.Effects)
|
||||
});
|
||||
continue;
|
||||
}
|
||||
|
||||
merged.Add(generatedBranch);
|
||||
}
|
||||
|
||||
merged.AddRange(currentBranches.Where(branch =>
|
||||
branch.IsOverridden &&
|
||||
branch.OriginKey is not null &&
|
||||
!matchedOrigins.Contains(branch.OriginKey)));
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private static List<CriticalEffectEditorItem> MergeEffectItems(
|
||||
IReadOnlyList<CriticalEffectEditorItem> currentEffects,
|
||||
IReadOnlyList<CriticalEffectEditorItem> generatedEffects)
|
||||
{
|
||||
var currentByOrigin = currentEffects
|
||||
.Where(effect => !string.IsNullOrWhiteSpace(effect.OriginKey))
|
||||
.ToDictionary(effect => effect.OriginKey!, StringComparer.Ordinal);
|
||||
var merged = new List<CriticalEffectEditorItem>(generatedEffects.Count);
|
||||
var matchedOrigins = new HashSet<string>(StringComparer.Ordinal);
|
||||
|
||||
foreach (var generatedEffect in generatedEffects)
|
||||
{
|
||||
if (generatedEffect.OriginKey is not null &&
|
||||
currentByOrigin.TryGetValue(generatedEffect.OriginKey, out var currentEffect))
|
||||
{
|
||||
matchedOrigins.Add(generatedEffect.OriginKey);
|
||||
merged.Add(currentEffect.IsOverridden ? currentEffect : generatedEffect);
|
||||
continue;
|
||||
}
|
||||
|
||||
merged.Add(generatedEffect);
|
||||
}
|
||||
|
||||
merged.AddRange(currentEffects.Where(effect =>
|
||||
effect.IsOverridden &&
|
||||
effect.OriginKey is not null &&
|
||||
!matchedOrigins.Contains(effect.OriginKey)));
|
||||
|
||||
return merged;
|
||||
}
|
||||
|
||||
private static string CreateBaseEffectOriginKey(int effectIndex, string effectCode) =>
|
||||
$"base:{NormalizeOriginSegment(effectCode)}:{effectIndex + 1}";
|
||||
|
||||
private static string CreateBranchOriginKey(int branchIndex, string branchKind, string? conditionKey, string conditionText) =>
|
||||
$"branch:{NormalizeOriginSegment(branchKind)}:{NormalizeOriginSegment(conditionKey ?? conditionText)}:{branchIndex + 1}";
|
||||
|
||||
private static string CreateBranchEffectOriginKey(string branchOriginKey, int effectIndex, string effectCode) =>
|
||||
$"{branchOriginKey}:effect:{NormalizeOriginSegment(effectCode)}:{effectIndex + 1}";
|
||||
|
||||
private static string NormalizeOriginSegment(string value)
|
||||
{
|
||||
var normalized = new string(value
|
||||
.Trim()
|
||||
.ToLowerInvariant()
|
||||
.Select(character => char.IsLetterOrDigit(character) ? character : '_')
|
||||
.ToArray())
|
||||
.Trim('_');
|
||||
|
||||
return string.IsNullOrWhiteSpace(normalized) ? "empty" : normalized;
|
||||
}
|
||||
|
||||
private static void ReplaceBaseEffects(
|
||||
RolemasterDbContext dbContext,
|
||||
|
||||
Reference in New Issue
Block a user