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();
|
using var connection = connectionFactory.CreateConnection();
|
||||||
var rows = await connection.QueryAsync<AssetCustodyEvent>("dbo.AssetCustodyEvent_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
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)
|
public async Task<IReadOnlyList<AssetCustodyEvent>> ListCustodyByAssetAsync(int storyAssetId)
|
||||||
{
|
{
|
||||||
using var connection = connectionFactory.CreateConnection();
|
using var connection = connectionFactory.CreateConnection();
|
||||||
var rows = await connection.QueryAsync<AssetCustodyEvent>("dbo.AssetCustodyEvent_ListByAsset", new { StoryAssetID = storyAssetId }, commandType: CommandType.StoredProcedure);
|
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)
|
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.Characters c ON c.CharacterID = acec.CharacterID
|
||||||
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
|
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
|
||||||
WHERE sa.ProjectID = @ProjectID
|
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 });
|
new { ProjectID = projectId });
|
||||||
return rows.ToList();
|
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)
|
public async Task<int> SaveCustodyEventAsync(AssetCustodyEvent custodyEvent, string custodianNames, IEnumerable<int> custodianCharacterIds, int custodyRoleId)
|
||||||
{
|
{
|
||||||
using var connection = connectionFactory.CreateConnection();
|
using var connection = connectionFactory.CreateConnection();
|
||||||
|
|||||||
@ -2804,46 +2804,10 @@ public sealed class StoryStateService(
|
|||||||
IReadOnlySet<int> enabledCharacterIds,
|
IReadOnlySet<int> enabledCharacterIds,
|
||||||
RangeContext context)
|
RangeContext context)
|
||||||
{
|
{
|
||||||
var warnings = new List<ContinuityWarningViewModel>();
|
// Disabled for now: consecutive-scene location changes are often ordinary story movement.
|
||||||
var rowsByCharacter = appearances
|
// Re-enable when this warning can use scene dates/times, overlapping scene spans,
|
||||||
.Where(x => x.LocationID.HasValue && (!enabledCharacterIds.Any() || enabledCharacterIds.Contains(x.CharacterID)))
|
// location distance, or author-configurable movement rules.
|
||||||
.GroupBy(x => x.CharacterID);
|
return [];
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<ContinuityWarningViewModel> BuildUnknownCharacterLocationWarnings(
|
private static List<ContinuityWarningViewModel> BuildUnknownCharacterLocationWarnings(
|
||||||
@ -2902,9 +2866,7 @@ public sealed class StoryStateService(
|
|||||||
foreach (var group in directLocationGroups)
|
foreach (var group in directLocationGroups)
|
||||||
{
|
{
|
||||||
var directLocationCount = group.Select(x => x.LocationID).Distinct().Count();
|
var directLocationCount = group.Select(x => x.LocationID).Distinct().Count();
|
||||||
custodyGroups.TryGetValue(group.Key, out var custodyRows);
|
if (directLocationCount <= 1)
|
||||||
var hasCustody = custodyRows?.Any() == true;
|
|
||||||
if (directLocationCount <= 1 && !hasCustody)
|
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user