From d44217162cd28e0264a1bad68efe3e14d3ca44ea Mon Sep 17 00:00:00 2001 From: Nick Beckley Date: Thu, 9 Jul 2026 21:09:48 +0100 Subject: [PATCH] =?UTF-8?q?Phase=2020AP.1=20=E2=80=93=20Asset=20Intelligen?= =?UTF-8?q?ce=20Review=20and=20Import?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../StoryIntelligencePersistenceModels.cs | 4 +- .../OnboardingStoryIntelligenceService.cs | 58 ++++++++++--------- PlotLine/Services/StoryIntelligenceService.cs | 18 ++++++ PlotLine/Views/Books/Details.cshtml | 14 +++++ 4 files changed, 66 insertions(+), 28 deletions(-) diff --git a/PlotLine/Models/StoryIntelligencePersistenceModels.cs b/PlotLine/Models/StoryIntelligencePersistenceModels.cs index b36447b..228d2dc 100644 --- a/PlotLine/Models/StoryIntelligencePersistenceModels.cs +++ b/PlotLine/Models/StoryIntelligencePersistenceModels.cs @@ -60,7 +60,9 @@ public sealed class StoryIntelligenceBookPipelineState public DateTime CreatedUtc { get; init; } public DateTime UpdatedUtc { get; init; } - public bool IsComplete => string.Equals(Status, StoryIntelligencePipelineStatuses.Complete, StringComparison.OrdinalIgnoreCase); + public bool IsComplete + => string.Equals(Status, StoryIntelligencePipelineStatuses.Complete, StringComparison.OrdinalIgnoreCase) + && string.Equals(LastCompletedStage, StoryIntelligencePipelineStages.AssetImport, StringComparison.OrdinalIgnoreCase); } public sealed class StoryIntelligenceBookPipelineSaveRequest diff --git a/PlotLine/Services/OnboardingStoryIntelligenceService.cs b/PlotLine/Services/OnboardingStoryIntelligenceService.cs index d498e6b..65fd63e 100644 --- a/PlotLine/Services/OnboardingStoryIntelligenceService.cs +++ b/PlotLine/Services/OnboardingStoryIntelligenceService.cs @@ -525,41 +525,45 @@ public sealed class OnboardingStoryIntelligenceService( .ToList() }; - if (state.IsComplete) - { - batch.CharacterStageComplete = true; - batch.LocationStageComplete = true; - batch.AssetStageComplete = true; - } - else if (state.CurrentStage is StoryIntelligencePipelineStages.AssetReview or StoryIntelligencePipelineStages.AssetImport) - { - batch.CharacterStageComplete = true; - batch.LocationStageComplete = true; - } - else if (state.CurrentStage is StoryIntelligencePipelineStages.LocationReview or StoryIntelligencePipelineStages.LocationImport) - { - batch.CharacterStageComplete = true; - } + var characterStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.CharacterImport) + || state.CurrentStage is StoryIntelligencePipelineStages.LocationReview + or StoryIntelligencePipelineStages.LocationImport + or StoryIntelligencePipelineStages.AssetReview + or StoryIntelligencePipelineStages.AssetImport + or StoryIntelligencePipelineStages.Complete; + var locationStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.LocationImport) + || state.CurrentStage is StoryIntelligencePipelineStages.AssetReview + or StoryIntelligencePipelineStages.AssetImport + or StoryIntelligencePipelineStages.Complete; + var assetStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.AssetImport); + + batch.CharacterStageComplete = characterStageComplete; + batch.LocationStageComplete = locationStageComplete; + batch.AssetStageComplete = assetStageComplete; await batchStore.SaveAsync(batch); - var route = state.CurrentStage switch + var route = assetStageComplete + ? StoryIntelligenceResumeRoutes.Complete + : locationStageComplete + ? StoryIntelligenceResumeRoutes.Assets + : characterStageComplete + ? StoryIntelligenceResumeRoutes.Locations + : state.CurrentStage switch { - StoryIntelligencePipelineStages.Complete => StoryIntelligenceResumeRoutes.Complete, - StoryIntelligencePipelineStages.AssetReview => StoryIntelligenceResumeRoutes.Assets, - StoryIntelligencePipelineStages.AssetImport => StoryIntelligenceResumeRoutes.Assets, - StoryIntelligencePipelineStages.LocationReview => StoryIntelligenceResumeRoutes.Locations, - StoryIntelligencePipelineStages.LocationImport => StoryIntelligenceResumeRoutes.Locations, - StoryIntelligencePipelineStages.CharacterReview => StoryIntelligenceResumeRoutes.Characters, - StoryIntelligencePipelineStages.CharacterImport => StoryIntelligenceResumeRoutes.Characters, - StoryIntelligencePipelineStages.SceneReview => StoryIntelligenceResumeRoutes.SceneReview, - StoryIntelligencePipelineStages.SceneImport => StoryIntelligenceResumeRoutes.SceneReview, - _ => StoryIntelligenceResumeRoutes.SceneReview - }; + StoryIntelligencePipelineStages.CharacterReview => StoryIntelligenceResumeRoutes.Characters, + StoryIntelligencePipelineStages.CharacterImport => StoryIntelligenceResumeRoutes.Characters, + StoryIntelligencePipelineStages.SceneReview => StoryIntelligenceResumeRoutes.SceneReview, + StoryIntelligencePipelineStages.SceneImport => StoryIntelligenceResumeRoutes.SceneReview, + _ => StoryIntelligenceResumeRoutes.SceneReview + }; return new OnboardingStoryIntelligenceResumeTarget(batch.BatchID, route); } + private static bool HasCompletedStage(StoryIntelligenceBookPipelineState state, string stage) + => string.Equals(state.LastCompletedStage, stage, StringComparison.OrdinalIgnoreCase); + private async Task<(UserOnboardingState State, ManuscriptScanPreview Preview, ManuscriptScanReviewDecision Review, OnboardingManuscriptBuildResult? Build, OnboardingWizardViewModel Wizard)?> GetContextAsync(int userId) { var state = await onboardingRepository.GetAsync(userId); diff --git a/PlotLine/Services/StoryIntelligenceService.cs b/PlotLine/Services/StoryIntelligenceService.cs index 659f027..61b69fd 100644 --- a/PlotLine/Services/StoryIntelligenceService.cs +++ b/PlotLine/Services/StoryIntelligenceService.cs @@ -269,8 +269,12 @@ public sealed class StoryIntelligenceService( return state.CurrentReviewStage switch { + StoryIntelligencePipelineStages.AssetReview => "Waiting for Asset Review", + StoryIntelligencePipelineStages.LocationReview => "Waiting for Location Review", StoryIntelligencePipelineStages.CharacterReview => "Waiting for Character Review", StoryIntelligencePipelineStages.SceneReview => "Needs Scene Review", + _ when StoryIntelligenceReadyForAssets(state) => "Waiting for Asset Review", + _ when StoryIntelligenceReadyForLocations(state) => "Waiting for Location Review", _ => string.Equals(state.Status, StoryIntelligencePipelineStatuses.InProgress, StringComparison.OrdinalIgnoreCase) ? "Story Intelligence In Progress" : "Story Intelligence Needs Review" @@ -282,11 +286,25 @@ public sealed class StoryIntelligenceService( ? "All currently available Story Intelligence stages are complete." : state.CurrentReviewStage switch { + StoryIntelligencePipelineStages.AssetReview => "Review detected assets and decide what to create or link.", + StoryIntelligencePipelineStages.LocationReview => "Review detected locations and decide what to create or link.", StoryIntelligencePipelineStages.CharacterReview => "Review detected characters and decide what to create or link.", StoryIntelligencePipelineStages.SceneReview => "Review prepared scenes before creating them.", + _ when StoryIntelligenceReadyForAssets(state) => "Review detected assets and decide what to create or link.", + _ when StoryIntelligenceReadyForLocations(state) => "Review detected locations and decide what to create or link.", _ => "Continue from the next Story Intelligence stage." }; + private static bool StoryIntelligenceReadyForAssets(StoryIntelligenceBookPipelineState state) + => string.Equals(state.LastCompletedStage, StoryIntelligencePipelineStages.LocationImport, StringComparison.OrdinalIgnoreCase) + || state.CurrentStage is StoryIntelligencePipelineStages.AssetReview or StoryIntelligencePipelineStages.AssetImport + || (string.Equals(state.CurrentStage, StoryIntelligencePipelineStages.Complete, StringComparison.OrdinalIgnoreCase) + && !string.Equals(state.LastCompletedStage, StoryIntelligencePipelineStages.AssetImport, StringComparison.OrdinalIgnoreCase)); + + private static bool StoryIntelligenceReadyForLocations(StoryIntelligenceBookPipelineState state) + => string.Equals(state.LastCompletedStage, StoryIntelligencePipelineStages.CharacterImport, StringComparison.OrdinalIgnoreCase) + || state.CurrentStage is StoryIntelligencePipelineStages.LocationReview or StoryIntelligencePipelineStages.LocationImport; + private static string FormatElapsed(TimeSpan elapsed) => elapsed.TotalMinutes < 1 ? $"{Math.Max(0, (int)elapsed.TotalSeconds)} sec" diff --git a/PlotLine/Views/Books/Details.cshtml b/PlotLine/Views/Books/Details.cshtml index 725f074..73f3e59 100644 --- a/PlotLine/Views/Books/Details.cshtml +++ b/PlotLine/Views/Books/Details.cshtml @@ -200,9 +200,23 @@ return pipeline.CurrentReviewStage switch { + StoryIntelligencePipelineStages.AssetReview => "Review detected assets and decide what to create or link.", + StoryIntelligencePipelineStages.LocationReview => "Review detected locations and decide what to create or link.", StoryIntelligencePipelineStages.CharacterReview => "Scene creation is complete. Review detected characters to continue building the story database.", StoryIntelligencePipelineStages.SceneReview => "Review prepared scenes before creating them in PlotDirector.", + _ when StoryIntelligenceReadyForAssets(pipeline) => "Review detected assets and decide what to create or link.", + _ when StoryIntelligenceReadyForLocations(pipeline) => "Review detected locations and decide what to create or link.", _ => "PlotDirector will resume from the next Story Intelligence stage." }; } + + private static bool StoryIntelligenceReadyForAssets(StoryIntelligenceBookPipelineState pipeline) + => string.Equals(pipeline.LastCompletedStage, StoryIntelligencePipelineStages.LocationImport, StringComparison.OrdinalIgnoreCase) + || pipeline.CurrentStage is StoryIntelligencePipelineStages.AssetReview or StoryIntelligencePipelineStages.AssetImport + || (string.Equals(pipeline.CurrentStage, StoryIntelligencePipelineStages.Complete, StringComparison.OrdinalIgnoreCase) + && !string.Equals(pipeline.LastCompletedStage, StoryIntelligencePipelineStages.AssetImport, StringComparison.OrdinalIgnoreCase)); + + private static bool StoryIntelligenceReadyForLocations(StoryIntelligenceBookPipelineState pipeline) + => string.Equals(pipeline.LastCompletedStage, StoryIntelligencePipelineStages.CharacterImport, StringComparison.OrdinalIgnoreCase) + || pipeline.CurrentStage is StoryIntelligencePipelineStages.LocationReview or StoryIntelligencePipelineStages.LocationImport; }