Investigate and fix issues discovered after Phase 12E Continuity Warnings.
This commit is contained in:
parent
7c7815642a
commit
279c8f182c
@ -2766,14 +2766,14 @@ public sealed class AssetRepository(ISqlConnectionFactory connectionFactory) : I
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<AssetCustodyEvent>("dbo.AssetCustodyEvent_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
return await WithValidCustodianSummariesAsync(connection, rows.ToList());
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<AssetCustodyEvent>> ListCustodyByAssetAsync(int storyAssetId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<AssetCustodyEvent>("dbo.AssetCustodyEvent_ListByAsset", new { StoryAssetID = storyAssetId }, commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
return await WithValidCustodianSummariesAsync(connection, rows.ToList());
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<AssetCustodyCharacter>> ListCustodyCharactersByProjectAsync(int projectId)
|
||||
@ -2795,12 +2795,62 @@ public sealed class AssetRepository(ISqlConnectionFactory connectionFactory) : I
|
||||
LEFT JOIN dbo.Characters c ON c.CharacterID = acec.CharacterID
|
||||
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
|
||||
WHERE sa.ProjectID = @ProjectID
|
||||
AND sa.IsArchived = 0;
|
||||
AND sa.IsArchived = 0
|
||||
AND acec.CharacterID IS NOT NULL
|
||||
AND NULLIF(LTRIM(RTRIM(c.CharacterName)), N'') IS NOT NULL;
|
||||
""",
|
||||
new { ProjectID = projectId });
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
private static async Task<IReadOnlyList<AssetCustodyEvent>> WithValidCustodianSummariesAsync(IDbConnection connection, List<AssetCustodyEvent> rows)
|
||||
{
|
||||
if (rows.Count == 0)
|
||||
{
|
||||
return rows;
|
||||
}
|
||||
|
||||
var eventIds = rows.Select(x => x.AssetCustodyEventID).Distinct().ToList();
|
||||
var validCustodians = await connection.QueryAsync<AssetCustodySummaryRow>(
|
||||
"""
|
||||
SELECT acec.AssetCustodyEventID,
|
||||
c.CharacterName,
|
||||
cr.RoleName
|
||||
FROM dbo.AssetCustodyEventCharacters acec
|
||||
INNER JOIN dbo.Characters c ON c.CharacterID = acec.CharacterID
|
||||
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
|
||||
WHERE acec.AssetCustodyEventID IN @AssetCustodyEventIDs
|
||||
AND acec.CharacterID IS NOT NULL
|
||||
AND NULLIF(LTRIM(RTRIM(c.CharacterName)), N'') IS NOT NULL;
|
||||
""",
|
||||
new { AssetCustodyEventIDs = eventIds });
|
||||
|
||||
var summaries = validCustodians
|
||||
.GroupBy(x => x.AssetCustodyEventID)
|
||||
.ToDictionary(
|
||||
x => x.Key,
|
||||
x => string.Join(", ", x
|
||||
.Select(row => string.IsNullOrWhiteSpace(row.RoleName)
|
||||
? row.CharacterName
|
||||
: $"{row.CharacterName} ({row.RoleName})")
|
||||
.Where(label => !string.IsNullOrWhiteSpace(label))
|
||||
.Distinct(StringComparer.OrdinalIgnoreCase)));
|
||||
|
||||
foreach (var row in rows)
|
||||
{
|
||||
row.CustodianSummary = summaries.GetValueOrDefault(row.AssetCustodyEventID);
|
||||
}
|
||||
|
||||
return rows.Where(x => !string.IsNullOrWhiteSpace(x.CustodianSummary)).ToList();
|
||||
}
|
||||
|
||||
private sealed class AssetCustodySummaryRow
|
||||
{
|
||||
public int AssetCustodyEventID { get; set; }
|
||||
public string CharacterName { get; set; } = string.Empty;
|
||||
public string? RoleName { get; set; }
|
||||
}
|
||||
|
||||
public async Task<int> SaveCustodyEventAsync(AssetCustodyEvent custodyEvent, string custodianNames, IEnumerable<int> custodianCharacterIds, int custodyRoleId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
|
||||
@ -2804,46 +2804,10 @@ public sealed class StoryStateService(
|
||||
IReadOnlySet<int> enabledCharacterIds,
|
||||
RangeContext context)
|
||||
{
|
||||
var warnings = new List<ContinuityWarningViewModel>();
|
||||
var rowsByCharacter = appearances
|
||||
.Where(x => x.LocationID.HasValue && (!enabledCharacterIds.Any() || enabledCharacterIds.Contains(x.CharacterID)))
|
||||
.GroupBy(x => x.CharacterID);
|
||||
|
||||
foreach (var group in rowsByCharacter)
|
||||
{
|
||||
var explicitLocations = group
|
||||
.GroupBy(x => x.SceneID)
|
||||
.Select(sceneGroup => sceneGroup.OrderByDescending(x => x.UpdatedDate).First())
|
||||
.OrderBy(x => context.SceneIndexes.GetValueOrDefault(x.SceneID, -1))
|
||||
.ToList();
|
||||
|
||||
for (var index = 1; index < explicitLocations.Count; index++)
|
||||
{
|
||||
var previous = explicitLocations[index - 1];
|
||||
var current = explicitLocations[index];
|
||||
if (previous.LocationID == current.LocationID || !context.AllowedSceneIds.Contains(current.SceneID))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var previousIndex = context.SceneIndexes.GetValueOrDefault(previous.SceneID, -1);
|
||||
var currentIndex = context.SceneIndexes.GetValueOrDefault(current.SceneID, -1);
|
||||
if (currentIndex - previousIndex != 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
warnings.Add(BuildWarning(
|
||||
"Impossible Character Movement",
|
||||
"Warning",
|
||||
$"{current.CharacterName} moved from {previous.LocationPath ?? previous.LocationName ?? "an unknown location"} to {current.LocationPath ?? current.LocationName ?? "an unknown location"} between consecutive scenes.",
|
||||
current.SceneID,
|
||||
context,
|
||||
characterId: current.CharacterID));
|
||||
}
|
||||
}
|
||||
|
||||
return warnings;
|
||||
// Disabled for now: consecutive-scene location changes are often ordinary story movement.
|
||||
// Re-enable when this warning can use scene dates/times, overlapping scene spans,
|
||||
// location distance, or author-configurable movement rules.
|
||||
return [];
|
||||
}
|
||||
|
||||
private static List<ContinuityWarningViewModel> BuildUnknownCharacterLocationWarnings(
|
||||
@ -2902,9 +2866,7 @@ public sealed class StoryStateService(
|
||||
foreach (var group in directLocationGroups)
|
||||
{
|
||||
var directLocationCount = group.Select(x => x.LocationID).Distinct().Count();
|
||||
custodyGroups.TryGetValue(group.Key, out var custodyRows);
|
||||
var hasCustody = custodyRows?.Any() == true;
|
||||
if (directLocationCount <= 1 && !hasCustody)
|
||||
if (directLocationCount <= 1)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user