Fix independent review centre state
This commit is contained in:
parent
794606f723
commit
5a7b8c79c7
@ -270,6 +270,7 @@ var tests = new (string Name, Action Test)[]
|
||||
("Story Intelligence Review Centre uses independent tile grid", StoryIntelligenceReviewCentreUsesIndependentTileGrid),
|
||||
("Story Intelligence Review Centre opens with character gate", StoryIntelligenceReviewCentreOpensWithCharacterGate),
|
||||
("Story Intelligence review modules are independent after character gate", StoryIntelligenceReviewModulesAreIndependentAfterCharacterGate),
|
||||
("Story Intelligence review module completion is durable and independent", StoryIntelligenceReviewModuleCompletionIsDurableAndIndependent),
|
||||
("Story Intelligence review module pages omit wizard chrome", StoryIntelligenceReviewModulePagesOmitWizardChrome),
|
||||
("Story Intelligence downstream review uses completed scene analysis", StoryIntelligenceDownstreamReviewUsesCompletedSceneAnalysis),
|
||||
("Story Intelligence review posts do not use success interstitials", StoryIntelligenceReviewPostsDoNotUseSuccessInterstitials),
|
||||
@ -802,6 +803,77 @@ static void StoryIntelligenceReviewModulesAreIndependentAfterCharacterGate()
|
||||
Assert(locationsAction.Contains("RedirectToReviewCentreWithCharacterGate", StringComparison.Ordinal), "Direct dependent module URLs should return to Review Centre when the character gate is closed.");
|
||||
}
|
||||
|
||||
static void StoryIntelligenceReviewModuleCompletionIsDurableAndIndependent()
|
||||
{
|
||||
var root = PlotLineRoot();
|
||||
var model = File.ReadAllText(Path.Combine(root, "Models/StoryIntelligencePersistenceModels.cs"));
|
||||
var repository = File.ReadAllText(Path.Combine(root, "Data/StoryIntelligencePipelineRepository.cs"));
|
||||
var pipeline = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligencePipelineStateService.cs"));
|
||||
var onboardingStory = File.ReadAllText(Path.Combine(root, "Services/OnboardingStoryIntelligenceService.cs"));
|
||||
var storyService = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceService.cs"));
|
||||
var coreServices = File.ReadAllText(Path.Combine(root, "Services/CoreServices.cs"));
|
||||
var characterService = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceCharacterImportService.cs"));
|
||||
var locationService = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceLocationImportService.cs"));
|
||||
var assetService = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceAssetImportService.cs"));
|
||||
var relationshipService = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceRelationshipImportService.cs"));
|
||||
var knowledgeService = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceKnowledgeImportService.cs"));
|
||||
var narrativeService = File.ReadAllText(Path.Combine(root, "Services/StoryIntelligenceNarrativeReviewService.cs"));
|
||||
var migration = File.ReadAllText(Path.Combine(root, "Sql/175_Phase27B_StoryIntelligenceIndependentReviewState.sql"));
|
||||
var synthesisSql = File.ReadAllText(Path.Combine(root, "Sql/174_Phase27_WholeBookPlotIntelligence.sql"));
|
||||
|
||||
foreach (var column in new[]
|
||||
{
|
||||
"CharacterReviewCompletedUtc",
|
||||
"LocationReviewCompletedUtc",
|
||||
"AssetReviewCompletedUtc",
|
||||
"RelationshipReviewCompletedUtc",
|
||||
"KnowledgeReviewCompletedUtc",
|
||||
"NarrativeReviewCompletedUtc"
|
||||
})
|
||||
{
|
||||
Assert(model.Contains($"public DateTime? {column}", StringComparison.Ordinal), $"{column} should be exposed on persisted pipeline state.");
|
||||
Assert(migration.Contains($"ADD {column} datetime2 NULL", StringComparison.Ordinal), $"{column} should be added by migration.");
|
||||
Assert(migration.Contains($"p.{column}", StringComparison.Ordinal), $"{column} should be returned by pipeline read procedures.");
|
||||
}
|
||||
|
||||
Assert(repository.Contains("request.MarkCharacterReviewComplete", StringComparison.Ordinal), "Pipeline upsert should pass durable character completion.");
|
||||
Assert(repository.Contains("request.MarkNarrativeReviewComplete", StringComparison.Ordinal), "Pipeline upsert should pass durable narrative completion.");
|
||||
Assert(pipeline.Contains("MarkCharacterReviewComplete = true", StringComparison.Ordinal), "Character import should mark only character review complete.");
|
||||
Assert(pipeline.Contains("MarkNarrativeReviewComplete = true", StringComparison.Ordinal), "Narrative import should mark only narrative review complete.");
|
||||
Assert(pipeline.Contains("MarkLocationReviewComplete = true", StringComparison.Ordinal), "Location import should mark durable location completion.");
|
||||
Assert(pipeline.Contains("MarkAssetReviewComplete = true", StringComparison.Ordinal), "Asset import should mark durable asset completion.");
|
||||
Assert(pipeline.Contains("MarkRelationshipReviewComplete = true", StringComparison.Ordinal), "Relationship import should mark durable relationship completion.");
|
||||
Assert(pipeline.Contains("MarkKnowledgeReviewComplete = true", StringComparison.Ordinal), "Knowledge import should mark durable knowledge completion.");
|
||||
Assert(migration.Contains("CharacterReviewCompletedUtc = CASE WHEN @MarkCharacterReviewComplete = 1 THEN COALESCE(CharacterReviewCompletedUtc", StringComparison.Ordinal), "Pipeline upsert must preserve character completion when other modules update.");
|
||||
Assert(migration.Contains("NarrativeReviewCompletedUtc = CASE WHEN @MarkNarrativeReviewComplete = 1 THEN COALESCE(NarrativeReviewCompletedUtc", StringComparison.Ordinal), "Pipeline upsert must preserve narrative completion when other modules update.");
|
||||
Assert(migration.Contains("WHERE NarrativeReviewCompletedUtc IS NULL", StringComparison.Ordinal), "Migration repair should be non-destructive.");
|
||||
|
||||
Assert(onboardingStory.Contains("pipeline?.CharacterReviewCompletedUtc is not null", StringComparison.Ordinal), "Review Centre should hydrate character completion from durable state.");
|
||||
Assert(onboardingStory.Contains("pipeline?.LocationReviewCompletedUtc is not null", StringComparison.Ordinal), "Review Centre should hydrate location completion independently.");
|
||||
Assert(onboardingStory.Contains("pipeline?.AssetReviewCompletedUtc is not null", StringComparison.Ordinal), "Review Centre should hydrate asset completion independently.");
|
||||
Assert(onboardingStory.Contains("pipeline?.RelationshipReviewCompletedUtc is not null", StringComparison.Ordinal), "Review Centre should hydrate relationship completion independently.");
|
||||
Assert(onboardingStory.Contains("pipeline?.KnowledgeReviewCompletedUtc is not null", StringComparison.Ordinal), "Review Centre should hydrate knowledge completion independently.");
|
||||
Assert(onboardingStory.Contains("pipeline?.NarrativeReviewCompletedUtc is not null", StringComparison.Ordinal), "Review Centre should hydrate narrative completion independently.");
|
||||
Assert(onboardingStory.Contains("batch.CharacterStageComplete = state.CharacterReviewCompletedUtc is not null", StringComparison.Ordinal), "Resume should hydrate character completion from durable state only.");
|
||||
Assert(!onboardingStory.Contains("state.CurrentStage is StoryIntelligencePipelineStages.LocationReview", StringComparison.Ordinal), "Resume should not infer character completion from post-character stage ordinals.");
|
||||
|
||||
Assert(characterService.Contains("pipeline?.CharacterReviewCompletedUtc is not null", StringComparison.Ordinal) && characterService.Contains("Candidates = []", StringComparison.Ordinal), "Completed character review should not resurrect stale scene candidates.");
|
||||
Assert(locationService.Contains("pipeline?.LocationReviewCompletedUtc is not null", StringComparison.Ordinal), "Completed location review should not resurrect stale scene candidates.");
|
||||
Assert(assetService.Contains("pipeline?.AssetReviewCompletedUtc is not null", StringComparison.Ordinal), "Completed asset review should not resurrect stale scene candidates.");
|
||||
Assert(relationshipService.Contains("pipeline?.RelationshipReviewCompletedUtc is not null", StringComparison.Ordinal), "Completed relationship review should not resurrect stale scene candidates.");
|
||||
Assert(knowledgeService.Contains("pipeline?.KnowledgeReviewCompletedUtc is not null", StringComparison.Ordinal), "Completed knowledge review should not resurrect stale scene candidates.");
|
||||
Assert(narrativeService.Contains("pipeline?.NarrativeReviewCompletedUtc is not null", StringComparison.Ordinal), "Completed plot review should not resurrect stale plot candidates.");
|
||||
|
||||
Assert(!storyService.Contains("StoryIntelligenceReadyForLocations", StringComparison.Ordinal), "Dashboard should not use old post-character location readiness ordinals.");
|
||||
Assert(!storyService.Contains("StoryIntelligenceReadyForAssets", StringComparison.Ordinal), "Dashboard should not use old post-character asset readiness ordinals.");
|
||||
Assert(!storyService.Contains("StoryIntelligenceReadyForRelationships", StringComparison.Ordinal), "Dashboard should not use old post-character relationship readiness ordinals.");
|
||||
Assert(!storyService.Contains("StoryIntelligenceReadyForKnowledge", StringComparison.Ordinal), "Dashboard should not use old post-character knowledge readiness ordinals.");
|
||||
Assert(coreServices.Contains("pipeline.CharacterReviewCompletedUtc is null", StringComparison.Ordinal), "Book action should not show character review required once durable character completion exists.");
|
||||
|
||||
Assert(synthesisSql.Contains("AND Status = N'Pending'", StringComparison.Ordinal), "Whole-book candidate regeneration should preserve reviewed plot decisions.");
|
||||
Assert(synthesisSql.Contains("Status IN (N'Queued', N'Running', N'Completed')", StringComparison.Ordinal), "Synthesis queue should preserve current completed runs rather than replacing them.");
|
||||
}
|
||||
|
||||
static void StoryIntelligenceReviewModulePagesOmitWizardChrome()
|
||||
{
|
||||
var root = PlotLineRoot();
|
||||
@ -3566,16 +3638,16 @@ static void StoryIntelligenceReviewCentreCompletionIsCategoryScoped()
|
||||
var controller = File.ReadAllText(Path.Combine(root, "Controllers/OnboardingController.cs"));
|
||||
var reviewView = File.ReadAllText(Path.Combine(root, "Views/Onboarding/StoryIntelligenceReviewCentre.cshtml"));
|
||||
|
||||
Assert(onboardingStory.Contains("LocationStageComplete = locationReview.IsComplete", StringComparison.Ordinal), "Location tile completion should come from the Location review service.");
|
||||
Assert(onboardingStory.Contains("AssetStageComplete = assetReview.IsComplete", StringComparison.Ordinal), "Asset tile completion should come from the Asset review service.");
|
||||
Assert(onboardingStory.Contains("RelationshipStageComplete = relationshipReview.IsComplete", StringComparison.Ordinal), "Relationship tile completion should come from the Relationship review service.");
|
||||
Assert(onboardingStory.Contains("KnowledgeStageComplete = knowledgeReview.IsComplete", StringComparison.Ordinal), "Knowledge tile completion should come from the Knowledge review service.");
|
||||
Assert(onboardingStory.Contains("NarrativeStageComplete = narrative.IsComplete", StringComparison.Ordinal), "Narrative tile completion should come from the Narrative review service.");
|
||||
Assert(onboardingStory.Contains("batch.LocationStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Location completion from the single pipeline stage marker.");
|
||||
Assert(onboardingStory.Contains("batch.AssetStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Asset completion from the single pipeline stage marker.");
|
||||
Assert(onboardingStory.Contains("batch.RelationshipStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Relationship completion from the single pipeline stage marker.");
|
||||
Assert(onboardingStory.Contains("batch.KnowledgeStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Knowledge completion from the single pipeline stage marker.");
|
||||
Assert(onboardingStory.Contains("batch.NarrativeStageComplete = false;", StringComparison.Ordinal), "Resume should not hydrate Narrative completion from the single pipeline stage marker.");
|
||||
Assert(onboardingStory.Contains("LocationStageComplete = locationComplete", StringComparison.Ordinal), "Location tile completion should come from scoped durable/review state.");
|
||||
Assert(onboardingStory.Contains("AssetStageComplete = assetComplete", StringComparison.Ordinal), "Asset tile completion should come from scoped durable/review state.");
|
||||
Assert(onboardingStory.Contains("RelationshipStageComplete = relationshipComplete", StringComparison.Ordinal), "Relationship tile completion should come from scoped durable/review state.");
|
||||
Assert(onboardingStory.Contains("KnowledgeStageComplete = knowledgeComplete", StringComparison.Ordinal), "Knowledge tile completion should come from scoped durable/review state.");
|
||||
Assert(onboardingStory.Contains("NarrativeStageComplete = narrativeComplete", StringComparison.Ordinal), "Narrative tile completion should come from scoped durable/review state.");
|
||||
Assert(onboardingStory.Contains("batch.LocationStageComplete = state.LocationReviewCompletedUtc is not null", StringComparison.Ordinal), "Resume should hydrate Location completion from durable state.");
|
||||
Assert(onboardingStory.Contains("batch.AssetStageComplete = state.AssetReviewCompletedUtc is not null", StringComparison.Ordinal), "Resume should hydrate Asset completion from durable state.");
|
||||
Assert(onboardingStory.Contains("batch.RelationshipStageComplete = state.RelationshipReviewCompletedUtc is not null", StringComparison.Ordinal), "Resume should hydrate Relationship completion from durable state.");
|
||||
Assert(onboardingStory.Contains("batch.KnowledgeStageComplete = state.KnowledgeReviewCompletedUtc is not null", StringComparison.Ordinal), "Resume should hydrate Knowledge completion from durable state.");
|
||||
Assert(onboardingStory.Contains("batch.NarrativeStageComplete = state.NarrativeReviewCompletedUtc is not null", StringComparison.Ordinal), "Resume should hydrate Narrative completion from durable state.");
|
||||
Assert(reviewView.Contains("Model.PipelineDashboard.RelationshipStageComplete", StringComparison.Ordinal), "Review Centre should render the scoped dashboard completion flags.");
|
||||
|
||||
var locationGet = ExtractMethod(controller, "StoryIntelligenceLocations");
|
||||
|
||||
@ -70,7 +70,13 @@ public sealed class StoryIntelligencePipelineRepository(ISqlConnectionFactory co
|
||||
request.CurrentReviewStage,
|
||||
request.Status,
|
||||
request.CompletedUtc,
|
||||
request.LastRunID
|
||||
request.LastRunID,
|
||||
request.MarkCharacterReviewComplete,
|
||||
request.MarkLocationReviewComplete,
|
||||
request.MarkAssetReviewComplete,
|
||||
request.MarkRelationshipReviewComplete,
|
||||
request.MarkKnowledgeReviewComplete,
|
||||
request.MarkNarrativeReviewComplete
|
||||
},
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
@ -62,6 +62,12 @@ public sealed class StoryIntelligenceBookPipelineState
|
||||
public string? CurrentReviewStage { get; init; }
|
||||
public string Status { get; init; } = StoryIntelligencePipelineStatuses.NeedsReview;
|
||||
public DateTime? CompletedUtc { get; init; }
|
||||
public DateTime? CharacterReviewCompletedUtc { get; init; }
|
||||
public DateTime? LocationReviewCompletedUtc { get; init; }
|
||||
public DateTime? AssetReviewCompletedUtc { get; init; }
|
||||
public DateTime? RelationshipReviewCompletedUtc { get; init; }
|
||||
public DateTime? KnowledgeReviewCompletedUtc { get; init; }
|
||||
public DateTime? NarrativeReviewCompletedUtc { get; init; }
|
||||
public int? LastRunID { get; init; }
|
||||
public DateTime CreatedUtc { get; init; }
|
||||
public DateTime UpdatedUtc { get; init; }
|
||||
@ -130,6 +136,12 @@ public sealed class StoryIntelligenceBookPipelineSaveRequest
|
||||
public string Status { get; init; } = StoryIntelligencePipelineStatuses.NeedsReview;
|
||||
public DateTime? CompletedUtc { get; init; }
|
||||
public int? LastRunID { get; init; }
|
||||
public bool MarkCharacterReviewComplete { get; init; }
|
||||
public bool MarkLocationReviewComplete { get; init; }
|
||||
public bool MarkAssetReviewComplete { get; init; }
|
||||
public bool MarkRelationshipReviewComplete { get; init; }
|
||||
public bool MarkKnowledgeReviewComplete { get; init; }
|
||||
public bool MarkNarrativeReviewComplete { get; init; }
|
||||
}
|
||||
|
||||
public sealed class StoryIntelligenceCommittedRunSummary
|
||||
|
||||
@ -1382,8 +1382,9 @@ public sealed class BookService(
|
||||
}
|
||||
|
||||
var isInProgress = string.Equals(pipeline.Status, StoryIntelligencePipelineStatuses.InProgress, StringComparison.OrdinalIgnoreCase);
|
||||
var needsCharacterReview = string.Equals(pipeline.CurrentReviewStage, StoryIntelligencePipelineStages.CharacterReview, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(pipeline.CurrentStage, StoryIntelligencePipelineStages.CharacterReview, StringComparison.OrdinalIgnoreCase);
|
||||
var needsCharacterReview = pipeline.CharacterReviewCompletedUtc is null
|
||||
&& (string.Equals(pipeline.CurrentReviewStage, StoryIntelligencePipelineStages.CharacterReview, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(pipeline.CurrentStage, StoryIntelligencePipelineStages.CharacterReview, StringComparison.OrdinalIgnoreCase));
|
||||
return new StoryIntelligenceBookActionViewModel
|
||||
{
|
||||
HasCanonicalSceneSources = hasCanonicalSceneSources,
|
||||
|
||||
@ -647,7 +647,13 @@ public sealed class OnboardingStoryIntelligenceService(
|
||||
var narrative = await narrativeReview.GetReviewAsync(batch);
|
||||
var plotSynthesisRun = await wholeBookPlotIntelligence.GetCurrentAsync(batch.BookID);
|
||||
var pipeline = await pipelineState.GetForBookAsync(batch.BookID, batch.UserID);
|
||||
var narrativePendingCount = narrative.Candidates.Count;
|
||||
var characterComplete = batch.CharacterStageComplete || pipeline?.CharacterReviewCompletedUtc is not null || characterReview.IsComplete;
|
||||
var locationComplete = batch.LocationStageComplete || pipeline?.LocationReviewCompletedUtc is not null || locationReview.IsComplete;
|
||||
var assetComplete = batch.AssetStageComplete || pipeline?.AssetReviewCompletedUtc is not null || assetReview.IsComplete;
|
||||
var relationshipComplete = batch.RelationshipStageComplete || pipeline?.RelationshipReviewCompletedUtc is not null || relationshipReview.IsComplete;
|
||||
var knowledgeComplete = batch.KnowledgeStageComplete || pipeline?.KnowledgeReviewCompletedUtc is not null || knowledgeReview.IsComplete;
|
||||
var narrativeComplete = batch.NarrativeStageComplete || pipeline?.NarrativeReviewCompletedUtc is not null || narrative.IsComplete;
|
||||
var narrativePendingCount = narrativeComplete ? 0 : narrative.Candidates.Count;
|
||||
var narrativeThreadCount = narrative.Candidates.Count(candidate =>
|
||||
string.Equals(candidate.CandidateType, StoryIntelligenceNarrativeCandidateTypes.Thread, StringComparison.OrdinalIgnoreCase));
|
||||
var narrativeEventCount = narrative.Candidates.Sum(candidate => candidate.Events.Count);
|
||||
@ -691,24 +697,24 @@ public sealed class OnboardingStoryIntelligenceService(
|
||||
SceneAnalysisComplete = pipeline is not null && SceneAnalysisComplete(pipeline),
|
||||
CharactersIdentified = characterReview.Candidates.Count + batch.CharacterDecisions.Count,
|
||||
CharactersCreatedOrLinked = batch.CharacterDecisions.Count(decision => decision.CreatedOrLinked),
|
||||
CharacterStageComplete = characterReview.IsComplete,
|
||||
CharacterStageComplete = characterComplete,
|
||||
LocationsIdentified = locationReview.Candidates.Count + batch.LocationDecisions.Count,
|
||||
LocationsCreatedOrLinked = batch.LocationDecisions.Count(decision => decision.CreatedOrLinked),
|
||||
LocationStageComplete = locationReview.IsComplete,
|
||||
LocationStageComplete = locationComplete,
|
||||
AssetsIdentified = assetReview.Candidates.Count + batch.AssetDecisions.Count,
|
||||
AssetsCreatedOrLinked = batch.AssetDecisions.Count(decision => decision.CreatedOrLinked),
|
||||
AssetStageComplete = assetReview.IsComplete,
|
||||
AssetStageComplete = assetComplete,
|
||||
RelationshipsIdentified = relationshipReview.Candidates.Count + batch.RelationshipDecisions.Count,
|
||||
RelationshipsCreatedOrLinked = batch.RelationshipDecisions.Count(decision => decision.CreatedOrLinked),
|
||||
RelationshipStageComplete = relationshipReview.IsComplete,
|
||||
RelationshipStageComplete = relationshipComplete,
|
||||
KnowledgeIdentified = knowledgeReview.Candidates.Count + batch.KnowledgeDecisions.Count,
|
||||
KnowledgeCreatedOrLinked = batch.KnowledgeDecisions.Count(decision => decision.CreatedOrLinked),
|
||||
KnowledgeStageComplete = knowledgeReview.IsComplete,
|
||||
KnowledgeStageComplete = knowledgeComplete,
|
||||
NarrativeCandidatesIdentified = narrative.Candidates.Count + (batch.LastNarrativeImportResult?.TotalReviewed ?? 0),
|
||||
NarrativeCandidatesApplied = (batch.LastNarrativeImportResult?.PlotLinesCreated ?? 0)
|
||||
+ (batch.LastNarrativeImportResult?.PlotLinesMerged ?? 0)
|
||||
+ (batch.LastNarrativeImportResult?.ThreadsCreated ?? 0),
|
||||
NarrativeStageComplete = narrative.IsComplete
|
||||
NarrativeStageComplete = narrativeComplete
|
||||
}
|
||||
};
|
||||
}
|
||||
@ -1045,24 +1051,12 @@ public sealed class OnboardingStoryIntelligenceService(
|
||||
Items = batchItems
|
||||
};
|
||||
|
||||
var characterStageComplete = HasCompletedStage(state, StoryIntelligencePipelineStages.CharacterImport)
|
||||
|| state.CurrentStage is StoryIntelligencePipelineStages.LocationReview
|
||||
or StoryIntelligencePipelineStages.LocationImport
|
||||
or StoryIntelligencePipelineStages.AssetReview
|
||||
or StoryIntelligencePipelineStages.AssetImport
|
||||
or StoryIntelligencePipelineStages.RelationshipReview
|
||||
or StoryIntelligencePipelineStages.RelationshipImport
|
||||
or StoryIntelligencePipelineStages.KnowledgeReview
|
||||
or StoryIntelligencePipelineStages.KnowledgeImport
|
||||
or StoryIntelligencePipelineStages.NarrativeReview
|
||||
or StoryIntelligencePipelineStages.NarrativeImport
|
||||
or StoryIntelligencePipelineStages.Complete;
|
||||
batch.CharacterStageComplete = characterStageComplete;
|
||||
batch.LocationStageComplete = false;
|
||||
batch.AssetStageComplete = false;
|
||||
batch.RelationshipStageComplete = false;
|
||||
batch.KnowledgeStageComplete = false;
|
||||
batch.NarrativeStageComplete = false;
|
||||
batch.CharacterStageComplete = state.CharacterReviewCompletedUtc is not null;
|
||||
batch.LocationStageComplete = state.LocationReviewCompletedUtc is not null;
|
||||
batch.AssetStageComplete = state.AssetReviewCompletedUtc is not null;
|
||||
batch.RelationshipStageComplete = state.RelationshipReviewCompletedUtc is not null;
|
||||
batch.KnowledgeStageComplete = state.KnowledgeReviewCompletedUtc is not null;
|
||||
batch.NarrativeStageComplete = state.NarrativeReviewCompletedUtc is not null;
|
||||
|
||||
await batchStore.SaveAsync(batch);
|
||||
|
||||
|
||||
@ -56,6 +56,19 @@ public sealed class StoryIntelligenceAssetImportService(
|
||||
public async Task<StoryIntelligenceAssetReviewViewModel> BuildReviewAsync(OnboardingStoryIntelligenceBatch batch)
|
||||
{
|
||||
var data = await BuildCandidateDataAsync(batch);
|
||||
var pipeline = await pipelineState.GetForBookAsync(batch.BookID, batch.UserID);
|
||||
if (batch.AssetStageComplete || pipeline?.AssetReviewCompletedUtc is not null)
|
||||
{
|
||||
return new StoryIntelligenceAssetReviewViewModel
|
||||
{
|
||||
HasCommittedScenes = data.HasCommittedScenes,
|
||||
CanImport = false,
|
||||
AlreadyLinkedCount = data.AlreadyLinkedCount,
|
||||
IsComplete = data.HasCommittedScenes,
|
||||
Candidates = []
|
||||
};
|
||||
}
|
||||
|
||||
var decidedKeys = batch.AssetDecisions.Select(decision => decision.Key).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var visibleCandidates = data.Candidates.Where(candidate => !decidedKeys.Contains(candidate.Key)).ToList();
|
||||
return new StoryIntelligenceAssetReviewViewModel
|
||||
|
||||
@ -72,6 +72,19 @@ public sealed class StoryIntelligenceCharacterImportService(
|
||||
public async Task<StoryIntelligenceCharacterReviewViewModel> BuildReviewAsync(OnboardingStoryIntelligenceBatch batch)
|
||||
{
|
||||
var data = await BuildCandidateDataAsync(batch);
|
||||
var pipeline = await pipelineState.GetForBookAsync(batch.BookID, batch.UserID);
|
||||
if (batch.CharacterStageComplete || pipeline?.CharacterReviewCompletedUtc is not null)
|
||||
{
|
||||
return new StoryIntelligenceCharacterReviewViewModel
|
||||
{
|
||||
HasCommittedScenes = data.HasCommittedScenes,
|
||||
CanImport = false,
|
||||
AlreadyLinkedCount = data.AlreadyLinkedCount,
|
||||
IsComplete = data.HasCommittedScenes,
|
||||
Candidates = []
|
||||
};
|
||||
}
|
||||
|
||||
var decidedKeys = batch.CharacterDecisions.Select(decision => decision.Key).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var visibleCandidates = data.Candidates.Where(candidate => !decidedKeys.Contains(candidate.Key)).ToList();
|
||||
var existingIndex = await BuildCharacterIndexAsync(batch.ProjectID);
|
||||
|
||||
@ -34,13 +34,18 @@ public sealed class StoryIntelligenceKnowledgeImportService(
|
||||
var decidedKeys = batch.KnowledgeDecisions.Select(decision => decision.Key).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var selectedStateIds = batch.KnowledgeSelections.ToDictionary(selection => selection.Key, selection => selection.KnowledgeStateID, StringComparer.OrdinalIgnoreCase);
|
||||
var visibleCandidates = data.Candidates.Where(candidate => !decidedKeys.Contains(candidate.Key)).ToList();
|
||||
var pipeline = await pipelineState.GetForBookAsync(batch.BookID, batch.UserID);
|
||||
if (batch.KnowledgeStageComplete || pipeline?.KnowledgeReviewCompletedUtc is not null)
|
||||
{
|
||||
visibleCandidates = [];
|
||||
}
|
||||
|
||||
return new StoryIntelligenceKnowledgeReviewViewModel
|
||||
{
|
||||
HasCommittedScenes = data.HasCommittedScenes,
|
||||
CanImport = visibleCandidates.Count > 0,
|
||||
AlreadyLinkedCount = data.AlreadyLinkedCount,
|
||||
IsComplete = data.HasCommittedScenes && visibleCandidates.Count == 0,
|
||||
IsComplete = data.HasCommittedScenes && (visibleCandidates.Count == 0 || batch.KnowledgeStageComplete || pipeline?.KnowledgeReviewCompletedUtc is not null),
|
||||
KnowledgeStateOptions = lookupData.KnowledgeStates
|
||||
.OrderBy(state => state.SortOrder)
|
||||
.ThenBy(state => state.StateName)
|
||||
|
||||
@ -70,6 +70,19 @@ public sealed class StoryIntelligenceLocationImportService(
|
||||
public async Task<StoryIntelligenceLocationReviewViewModel> BuildReviewAsync(OnboardingStoryIntelligenceBatch batch)
|
||||
{
|
||||
var data = await BuildCandidateDataAsync(batch);
|
||||
var pipeline = await pipelineState.GetForBookAsync(batch.BookID, batch.UserID);
|
||||
if (batch.LocationStageComplete || pipeline?.LocationReviewCompletedUtc is not null)
|
||||
{
|
||||
return new StoryIntelligenceLocationReviewViewModel
|
||||
{
|
||||
HasCommittedScenes = data.HasCommittedScenes,
|
||||
CanImport = false,
|
||||
AlreadyLinkedCount = data.AlreadyLinkedCount,
|
||||
IsComplete = data.HasCommittedScenes,
|
||||
Candidates = []
|
||||
};
|
||||
}
|
||||
|
||||
var decidedKeys = batch.LocationDecisions.Select(decision => decision.Key).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var visibleCandidates = data.Candidates.Where(candidate => !decidedKeys.Contains(candidate.Key)).ToList();
|
||||
return new StoryIntelligenceLocationReviewViewModel
|
||||
|
||||
@ -36,8 +36,11 @@ public sealed class StoryIntelligenceNarrativeReviewService(
|
||||
var rows = (await candidates.ListByBookAsync(batch.BookID))
|
||||
.Where(IsWholeBookCandidate)
|
||||
.ToList();
|
||||
var pipeline = await pipelineState.GetForBookAsync(batch.BookID, batch.UserID);
|
||||
var reviewComplete = batch.NarrativeStageComplete || pipeline?.NarrativeReviewCompletedUtc is not null;
|
||||
var pending = rows
|
||||
.Where(candidate => string.Equals(candidate.Status, StoryIntelligenceNarrativeCandidateStatuses.Pending, StringComparison.OrdinalIgnoreCase))
|
||||
.Where(candidate => !reviewComplete
|
||||
&& string.Equals(candidate.Status, StoryIntelligenceNarrativeCandidateStatuses.Pending, StringComparison.OrdinalIgnoreCase))
|
||||
.ToList();
|
||||
var plotLines = await plots.ListPlotLinesAsync(batch.ProjectID);
|
||||
|
||||
@ -45,7 +48,7 @@ public sealed class StoryIntelligenceNarrativeReviewService(
|
||||
{
|
||||
HasCommittedScenes = await HasCommittedScenesAsync(batch),
|
||||
CanImport = pending.Count > 0,
|
||||
IsComplete = rows.Count > 0 && pending.Count == 0,
|
||||
IsComplete = reviewComplete || (rows.Count > 0 && pending.Count == 0),
|
||||
PlotLineOptions = plotLines
|
||||
.OrderBy(plotLine => plotLine.SortOrder)
|
||||
.ThenBy(plotLine => plotLine.PlotLineName)
|
||||
|
||||
@ -108,7 +108,8 @@ public sealed class StoryIntelligencePipelineStateService(
|
||||
CurrentReviewStage = StoryIntelligencePipelineStages.LocationReview,
|
||||
Status = StoryIntelligencePipelineStatuses.NeedsReview,
|
||||
CompletedUtc = null,
|
||||
LastRunID = null
|
||||
LastRunID = null,
|
||||
MarkCharacterReviewComplete = true
|
||||
});
|
||||
}
|
||||
|
||||
@ -123,7 +124,8 @@ public sealed class StoryIntelligencePipelineStateService(
|
||||
CurrentReviewStage = StoryIntelligencePipelineStages.AssetReview,
|
||||
Status = StoryIntelligencePipelineStatuses.NeedsReview,
|
||||
CompletedUtc = null,
|
||||
LastRunID = null
|
||||
LastRunID = null,
|
||||
MarkLocationReviewComplete = true
|
||||
});
|
||||
}
|
||||
|
||||
@ -138,7 +140,8 @@ public sealed class StoryIntelligencePipelineStateService(
|
||||
CurrentReviewStage = StoryIntelligencePipelineStages.RelationshipReview,
|
||||
Status = StoryIntelligencePipelineStatuses.NeedsReview,
|
||||
CompletedUtc = null,
|
||||
LastRunID = null
|
||||
LastRunID = null,
|
||||
MarkAssetReviewComplete = true
|
||||
});
|
||||
}
|
||||
|
||||
@ -153,7 +156,8 @@ public sealed class StoryIntelligencePipelineStateService(
|
||||
CurrentReviewStage = StoryIntelligencePipelineStages.KnowledgeReview,
|
||||
Status = StoryIntelligencePipelineStatuses.NeedsReview,
|
||||
CompletedUtc = null,
|
||||
LastRunID = null
|
||||
LastRunID = null,
|
||||
MarkRelationshipReviewComplete = true
|
||||
});
|
||||
}
|
||||
|
||||
@ -168,7 +172,8 @@ public sealed class StoryIntelligencePipelineStateService(
|
||||
CurrentReviewStage = StoryIntelligencePipelineStages.NarrativeReview,
|
||||
Status = StoryIntelligencePipelineStatuses.NeedsReview,
|
||||
CompletedUtc = null,
|
||||
LastRunID = null
|
||||
LastRunID = null,
|
||||
MarkKnowledgeReviewComplete = true
|
||||
});
|
||||
}
|
||||
|
||||
@ -183,7 +188,8 @@ public sealed class StoryIntelligencePipelineStateService(
|
||||
CurrentReviewStage = null,
|
||||
Status = StoryIntelligencePipelineStatuses.Complete,
|
||||
CompletedUtc = DateTime.UtcNow,
|
||||
LastRunID = null
|
||||
LastRunID = null,
|
||||
MarkNarrativeReviewComplete = true
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@ -34,12 +34,18 @@ public sealed class StoryIntelligenceRelationshipImportService(
|
||||
var selectedTypeIds = batch.RelationshipSelections.ToDictionary(selection => selection.Key, selection => selection.RelationshipTypeID, StringComparer.OrdinalIgnoreCase);
|
||||
var decidedKeys = batch.RelationshipDecisions.Select(decision => decision.Key).ToHashSet(StringComparer.OrdinalIgnoreCase);
|
||||
var visibleCandidates = data.Candidates.Where(candidate => !decidedKeys.Contains(candidate.Key)).ToList();
|
||||
var pipeline = await pipelineState.GetForBookAsync(batch.BookID, batch.UserID);
|
||||
if (batch.RelationshipStageComplete || pipeline?.RelationshipReviewCompletedUtc is not null)
|
||||
{
|
||||
visibleCandidates = [];
|
||||
}
|
||||
|
||||
return new StoryIntelligenceRelationshipReviewViewModel
|
||||
{
|
||||
HasCommittedScenes = data.HasCommittedScenes,
|
||||
CanImport = visibleCandidates.Count > 0,
|
||||
AlreadyLinkedCount = data.AlreadyLinkedCount,
|
||||
IsComplete = data.HasCommittedScenes && visibleCandidates.Count == 0,
|
||||
IsComplete = data.HasCommittedScenes && (visibleCandidates.Count == 0 || batch.RelationshipStageComplete || pipeline?.RelationshipReviewCompletedUtc is not null),
|
||||
RelationshipTypeOptions = typeOptions,
|
||||
Candidates = visibleCandidates.Select(candidate => new StoryIntelligenceRelationshipReviewCandidateViewModel
|
||||
{
|
||||
|
||||
@ -281,10 +281,7 @@ public sealed class StoryIntelligenceService(
|
||||
StoryIntelligencePipelineStages.LocationReview => "Review Centre",
|
||||
StoryIntelligencePipelineStages.CharacterReview => "Character Resolution Needed",
|
||||
StoryIntelligencePipelineStages.SceneReview => "Needs Scene Review",
|
||||
_ when StoryIntelligenceReadyForKnowledge(state) => "Review Centre",
|
||||
_ when StoryIntelligenceReadyForRelationships(state) => "Review Centre",
|
||||
_ when StoryIntelligenceReadyForAssets(state) => "Review Centre",
|
||||
_ when StoryIntelligenceReadyForLocations(state) => "Review Centre",
|
||||
_ when StoryIntelligenceCharacterReviewComplete(state) => "Review Centre",
|
||||
_ => string.Equals(state.Status, StoryIntelligencePipelineStatuses.InProgress, StringComparison.OrdinalIgnoreCase)
|
||||
? "Story Intelligence In Progress"
|
||||
: "Story Intelligence Needs Review"
|
||||
@ -304,36 +301,12 @@ public sealed class StoryIntelligenceService(
|
||||
StoryIntelligencePipelineStages.LocationReview => "Open the Review Centre and choose which saved category to handle.",
|
||||
StoryIntelligencePipelineStages.CharacterReview => "Resolve character identities before reviewing dependent story intelligence.",
|
||||
StoryIntelligencePipelineStages.SceneReview => "Story Intelligence analysis has not finished for this book.",
|
||||
_ when StoryIntelligenceReadyForKnowledge(state) => "Open the Review Centre and choose which saved category to handle.",
|
||||
_ when StoryIntelligenceReadyForRelationships(state) => "Open the Review Centre and choose which saved category to handle.",
|
||||
_ when StoryIntelligenceReadyForAssets(state) => "Open the Review Centre and choose which saved category to handle.",
|
||||
_ when StoryIntelligenceReadyForLocations(state) => "Open the Review Centre and choose which saved category to handle.",
|
||||
_ when StoryIntelligenceCharacterReviewComplete(state) => "Open the Review Centre and choose which saved category to handle.",
|
||||
_ => "Open the Review Centre and choose which saved category to handle."
|
||||
};
|
||||
|
||||
private static bool StoryIntelligenceReadyForKnowledge(StoryIntelligenceBookPipelineState state)
|
||||
=> string.Equals(state.LastCompletedStage, StoryIntelligencePipelineStages.RelationshipImport, StringComparison.OrdinalIgnoreCase)
|
||||
|| state.CurrentStage is StoryIntelligencePipelineStages.KnowledgeReview or StoryIntelligencePipelineStages.KnowledgeImport;
|
||||
|
||||
private static bool StoryIntelligenceReadyForRelationships(StoryIntelligenceBookPipelineState state)
|
||||
=> string.Equals(state.LastCompletedStage, StoryIntelligencePipelineStages.AssetImport, StringComparison.OrdinalIgnoreCase)
|
||||
|| state.CurrentStage is StoryIntelligencePipelineStages.RelationshipReview or StoryIntelligencePipelineStages.RelationshipImport
|
||||
or StoryIntelligencePipelineStages.KnowledgeReview or StoryIntelligencePipelineStages.KnowledgeImport;
|
||||
|
||||
private static bool StoryIntelligenceReadyForAssets(StoryIntelligenceBookPipelineState state)
|
||||
=> string.Equals(state.LastCompletedStage, StoryIntelligencePipelineStages.LocationImport, StringComparison.OrdinalIgnoreCase)
|
||||
|| state.CurrentStage is StoryIntelligencePipelineStages.AssetReview or StoryIntelligencePipelineStages.AssetImport
|
||||
or StoryIntelligencePipelineStages.RelationshipReview or StoryIntelligencePipelineStages.RelationshipImport
|
||||
or StoryIntelligencePipelineStages.KnowledgeReview or StoryIntelligencePipelineStages.KnowledgeImport
|
||||
|| (string.Equals(state.CurrentStage, StoryIntelligencePipelineStages.Complete, StringComparison.OrdinalIgnoreCase)
|
||||
&& !string.Equals(state.LastCompletedStage, StoryIntelligencePipelineStages.KnowledgeImport, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
private static bool StoryIntelligenceReadyForLocations(StoryIntelligenceBookPipelineState state)
|
||||
=> string.Equals(state.LastCompletedStage, StoryIntelligencePipelineStages.CharacterImport, StringComparison.OrdinalIgnoreCase)
|
||||
|| state.CurrentStage is StoryIntelligencePipelineStages.LocationReview or StoryIntelligencePipelineStages.LocationImport
|
||||
or StoryIntelligencePipelineStages.AssetReview or StoryIntelligencePipelineStages.AssetImport
|
||||
or StoryIntelligencePipelineStages.RelationshipReview or StoryIntelligencePipelineStages.RelationshipImport
|
||||
or StoryIntelligencePipelineStages.KnowledgeReview or StoryIntelligencePipelineStages.KnowledgeImport;
|
||||
private static bool StoryIntelligenceCharacterReviewComplete(StoryIntelligenceBookPipelineState state)
|
||||
=> state.CharacterReviewCompletedUtc is not null;
|
||||
|
||||
private static string FormatElapsed(TimeSpan elapsed)
|
||||
=> elapsed.TotalMinutes < 1
|
||||
|
||||
@ -0,0 +1,255 @@
|
||||
SET ANSI_NULLS ON;
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
GO
|
||||
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceBookPipelines', N'CharacterReviewCompletedUtc') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.StoryIntelligenceBookPipelines ADD CharacterReviewCompletedUtc datetime2 NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceBookPipelines', N'LocationReviewCompletedUtc') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.StoryIntelligenceBookPipelines ADD LocationReviewCompletedUtc datetime2 NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceBookPipelines', N'AssetReviewCompletedUtc') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.StoryIntelligenceBookPipelines ADD AssetReviewCompletedUtc datetime2 NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceBookPipelines', N'RelationshipReviewCompletedUtc') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.StoryIntelligenceBookPipelines ADD RelationshipReviewCompletedUtc datetime2 NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceBookPipelines', N'KnowledgeReviewCompletedUtc') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.StoryIntelligenceBookPipelines ADD KnowledgeReviewCompletedUtc datetime2 NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
IF COL_LENGTH(N'dbo.StoryIntelligenceBookPipelines', N'NarrativeReviewCompletedUtc') IS NULL
|
||||
BEGIN
|
||||
ALTER TABLE dbo.StoryIntelligenceBookPipelines ADD NarrativeReviewCompletedUtc datetime2 NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
UPDATE dbo.StoryIntelligenceBookPipelines
|
||||
SET CharacterReviewCompletedUtc = COALESCE(CharacterReviewCompletedUtc, UpdatedUtc, SYSUTCDATETIME())
|
||||
WHERE CharacterReviewCompletedUtc IS NULL
|
||||
AND
|
||||
(
|
||||
LastCompletedStage IN
|
||||
(
|
||||
N'CharacterImport',
|
||||
N'LocationImport',
|
||||
N'AssetImport',
|
||||
N'RelationshipImport',
|
||||
N'KnowledgeImport',
|
||||
N'NarrativeImport'
|
||||
)
|
||||
OR CurrentStage IN
|
||||
(
|
||||
N'LocationReview',
|
||||
N'LocationImport',
|
||||
N'AssetReview',
|
||||
N'AssetImport',
|
||||
N'RelationshipReview',
|
||||
N'RelationshipImport',
|
||||
N'KnowledgeReview',
|
||||
N'KnowledgeImport',
|
||||
N'NarrativeReview',
|
||||
N'NarrativeImport',
|
||||
N'Complete'
|
||||
)
|
||||
);
|
||||
GO
|
||||
|
||||
UPDATE dbo.StoryIntelligenceBookPipelines
|
||||
SET NarrativeReviewCompletedUtc = COALESCE(NarrativeReviewCompletedUtc, CompletedUtc, UpdatedUtc, SYSUTCDATETIME())
|
||||
WHERE NarrativeReviewCompletedUtc IS NULL
|
||||
AND LastCompletedStage = N'NarrativeImport'
|
||||
AND Status = N'Complete';
|
||||
GO
|
||||
|
||||
UPDATE dbo.StoryIntelligenceBookPipelines
|
||||
SET LocationReviewCompletedUtc = COALESCE(LocationReviewCompletedUtc, UpdatedUtc, SYSUTCDATETIME())
|
||||
WHERE LocationReviewCompletedUtc IS NULL
|
||||
AND LastCompletedStage = N'LocationImport';
|
||||
GO
|
||||
|
||||
UPDATE dbo.StoryIntelligenceBookPipelines
|
||||
SET AssetReviewCompletedUtc = COALESCE(AssetReviewCompletedUtc, UpdatedUtc, SYSUTCDATETIME())
|
||||
WHERE AssetReviewCompletedUtc IS NULL
|
||||
AND LastCompletedStage = N'AssetImport';
|
||||
GO
|
||||
|
||||
UPDATE dbo.StoryIntelligenceBookPipelines
|
||||
SET RelationshipReviewCompletedUtc = COALESCE(RelationshipReviewCompletedUtc, UpdatedUtc, SYSUTCDATETIME())
|
||||
WHERE RelationshipReviewCompletedUtc IS NULL
|
||||
AND LastCompletedStage = N'RelationshipImport';
|
||||
GO
|
||||
|
||||
UPDATE dbo.StoryIntelligenceBookPipelines
|
||||
SET KnowledgeReviewCompletedUtc = COALESCE(KnowledgeReviewCompletedUtc, UpdatedUtc, SYSUTCDATETIME())
|
||||
WHERE KnowledgeReviewCompletedUtc IS NULL
|
||||
AND LastCompletedStage = N'KnowledgeImport';
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceBookPipeline_GetByBook
|
||||
@BookID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT p.StoryIntelligenceBookPipelineID, p.ProjectID, project.ProjectName,
|
||||
p.BookID, book.BookTitle, book.Subtitle AS BookSubtitle,
|
||||
p.CurrentStage, p.LastCompletedStage, p.CurrentReviewStage, p.Status,
|
||||
p.CompletedUtc, p.CharacterReviewCompletedUtc, p.LocationReviewCompletedUtc,
|
||||
p.AssetReviewCompletedUtc, p.RelationshipReviewCompletedUtc, p.KnowledgeReviewCompletedUtc,
|
||||
p.NarrativeReviewCompletedUtc, p.LastRunID, p.CreatedUtc, p.UpdatedUtc
|
||||
FROM dbo.StoryIntelligenceBookPipelines p
|
||||
INNER JOIN dbo.Projects project ON project.ProjectID = p.ProjectID
|
||||
INNER JOIN dbo.Books book ON book.BookID = p.BookID
|
||||
WHERE p.BookID = @BookID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceBookPipeline_Upsert
|
||||
@ProjectID int,
|
||||
@BookID int,
|
||||
@CurrentStage nvarchar(80),
|
||||
@LastCompletedStage nvarchar(80) = NULL,
|
||||
@CurrentReviewStage nvarchar(80) = NULL,
|
||||
@Status nvarchar(40),
|
||||
@CompletedUtc datetime2 = NULL,
|
||||
@LastRunID int = NULL,
|
||||
@MarkCharacterReviewComplete bit = 0,
|
||||
@MarkLocationReviewComplete bit = 0,
|
||||
@MarkAssetReviewComplete bit = 0,
|
||||
@MarkRelationshipReviewComplete bit = 0,
|
||||
@MarkKnowledgeReviewComplete bit = 0,
|
||||
@MarkNarrativeReviewComplete bit = 0
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
IF EXISTS (SELECT 1 FROM dbo.StoryIntelligenceBookPipelines WHERE BookID = @BookID)
|
||||
BEGIN
|
||||
UPDATE dbo.StoryIntelligenceBookPipelines
|
||||
SET ProjectID = @ProjectID,
|
||||
CurrentStage = @CurrentStage,
|
||||
LastCompletedStage = @LastCompletedStage,
|
||||
CurrentReviewStage = @CurrentReviewStage,
|
||||
Status = @Status,
|
||||
CompletedUtc = @CompletedUtc,
|
||||
LastRunID = COALESCE(@LastRunID, LastRunID),
|
||||
CharacterReviewCompletedUtc = CASE WHEN @MarkCharacterReviewComplete = 1 THEN COALESCE(CharacterReviewCompletedUtc, SYSUTCDATETIME()) ELSE CharacterReviewCompletedUtc END,
|
||||
LocationReviewCompletedUtc = CASE WHEN @MarkLocationReviewComplete = 1 THEN COALESCE(LocationReviewCompletedUtc, SYSUTCDATETIME()) ELSE LocationReviewCompletedUtc END,
|
||||
AssetReviewCompletedUtc = CASE WHEN @MarkAssetReviewComplete = 1 THEN COALESCE(AssetReviewCompletedUtc, SYSUTCDATETIME()) ELSE AssetReviewCompletedUtc END,
|
||||
RelationshipReviewCompletedUtc = CASE WHEN @MarkRelationshipReviewComplete = 1 THEN COALESCE(RelationshipReviewCompletedUtc, SYSUTCDATETIME()) ELSE RelationshipReviewCompletedUtc END,
|
||||
KnowledgeReviewCompletedUtc = CASE WHEN @MarkKnowledgeReviewComplete = 1 THEN COALESCE(KnowledgeReviewCompletedUtc, SYSUTCDATETIME()) ELSE KnowledgeReviewCompletedUtc END,
|
||||
NarrativeReviewCompletedUtc = CASE WHEN @MarkNarrativeReviewComplete = 1 THEN COALESCE(NarrativeReviewCompletedUtc, SYSUTCDATETIME()) ELSE NarrativeReviewCompletedUtc END,
|
||||
UpdatedUtc = SYSUTCDATETIME()
|
||||
WHERE BookID = @BookID;
|
||||
END
|
||||
ELSE
|
||||
BEGIN
|
||||
INSERT dbo.StoryIntelligenceBookPipelines
|
||||
(ProjectID, BookID, CurrentStage, LastCompletedStage, CurrentReviewStage, Status, CompletedUtc, LastRunID,
|
||||
CharacterReviewCompletedUtc, LocationReviewCompletedUtc, AssetReviewCompletedUtc,
|
||||
RelationshipReviewCompletedUtc, KnowledgeReviewCompletedUtc, NarrativeReviewCompletedUtc)
|
||||
VALUES
|
||||
(@ProjectID, @BookID, @CurrentStage, @LastCompletedStage, @CurrentReviewStage, @Status, @CompletedUtc, @LastRunID,
|
||||
CASE WHEN @MarkCharacterReviewComplete = 1 THEN SYSUTCDATETIME() ELSE NULL END,
|
||||
CASE WHEN @MarkLocationReviewComplete = 1 THEN SYSUTCDATETIME() ELSE NULL END,
|
||||
CASE WHEN @MarkAssetReviewComplete = 1 THEN SYSUTCDATETIME() ELSE NULL END,
|
||||
CASE WHEN @MarkRelationshipReviewComplete = 1 THEN SYSUTCDATETIME() ELSE NULL END,
|
||||
CASE WHEN @MarkKnowledgeReviewComplete = 1 THEN SYSUTCDATETIME() ELSE NULL END,
|
||||
CASE WHEN @MarkNarrativeReviewComplete = 1 THEN SYSUTCDATETIME() ELSE NULL END);
|
||||
END;
|
||||
|
||||
EXEC dbo.StoryIntelligenceBookPipeline_GetByBook @BookID = @BookID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceBookPipeline_GetByBookForUser
|
||||
@BookID int,
|
||||
@UserID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT p.StoryIntelligenceBookPipelineID, p.ProjectID, project.ProjectName,
|
||||
p.BookID, book.BookTitle, book.Subtitle AS BookSubtitle,
|
||||
p.CurrentStage, p.LastCompletedStage, p.CurrentReviewStage, p.Status,
|
||||
p.CompletedUtc, p.CharacterReviewCompletedUtc, p.LocationReviewCompletedUtc,
|
||||
p.AssetReviewCompletedUtc, p.RelationshipReviewCompletedUtc, p.KnowledgeReviewCompletedUtc,
|
||||
p.NarrativeReviewCompletedUtc, p.LastRunID, p.CreatedUtc, p.UpdatedUtc
|
||||
FROM dbo.StoryIntelligenceBookPipelines p
|
||||
INNER JOIN dbo.Projects project ON project.ProjectID = p.ProjectID
|
||||
INNER JOIN dbo.Books book ON book.BookID = p.BookID
|
||||
INNER JOIN dbo.ProjectUserAccess access ON access.ProjectID = p.ProjectID
|
||||
AND access.UserID = @UserID
|
||||
AND access.IsActive = 1
|
||||
WHERE p.BookID = @BookID
|
||||
AND book.IsArchived = 0
|
||||
AND project.IsArchived = 0;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceBookPipeline_GetByIDForUser
|
||||
@StoryIntelligenceBookPipelineID int,
|
||||
@UserID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT p.StoryIntelligenceBookPipelineID, p.ProjectID, project.ProjectName,
|
||||
p.BookID, book.BookTitle, book.Subtitle AS BookSubtitle,
|
||||
p.CurrentStage, p.LastCompletedStage, p.CurrentReviewStage, p.Status,
|
||||
p.CompletedUtc, p.CharacterReviewCompletedUtc, p.LocationReviewCompletedUtc,
|
||||
p.AssetReviewCompletedUtc, p.RelationshipReviewCompletedUtc, p.KnowledgeReviewCompletedUtc,
|
||||
p.NarrativeReviewCompletedUtc, p.LastRunID, p.CreatedUtc, p.UpdatedUtc
|
||||
FROM dbo.StoryIntelligenceBookPipelines p
|
||||
INNER JOIN dbo.Projects project ON project.ProjectID = p.ProjectID
|
||||
INNER JOIN dbo.Books book ON book.BookID = p.BookID
|
||||
INNER JOIN dbo.ProjectUserAccess access ON access.ProjectID = p.ProjectID
|
||||
AND access.UserID = @UserID
|
||||
AND access.IsActive = 1
|
||||
WHERE p.StoryIntelligenceBookPipelineID = @StoryIntelligenceBookPipelineID
|
||||
AND book.IsArchived = 0
|
||||
AND project.IsArchived = 0;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceBookPipeline_ListForUser
|
||||
@UserID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT p.StoryIntelligenceBookPipelineID, p.ProjectID, project.ProjectName,
|
||||
p.BookID, book.BookTitle, book.Subtitle AS BookSubtitle,
|
||||
p.CurrentStage, p.LastCompletedStage, p.CurrentReviewStage, p.Status,
|
||||
p.CompletedUtc, p.CharacterReviewCompletedUtc, p.LocationReviewCompletedUtc,
|
||||
p.AssetReviewCompletedUtc, p.RelationshipReviewCompletedUtc, p.KnowledgeReviewCompletedUtc,
|
||||
p.NarrativeReviewCompletedUtc, p.LastRunID, p.CreatedUtc, p.UpdatedUtc
|
||||
FROM dbo.StoryIntelligenceBookPipelines p
|
||||
INNER JOIN dbo.Projects project ON project.ProjectID = p.ProjectID
|
||||
INNER JOIN dbo.Books book ON book.BookID = p.BookID
|
||||
INNER JOIN dbo.ProjectUserAccess access ON access.ProjectID = p.ProjectID
|
||||
AND access.UserID = @UserID
|
||||
AND access.IsActive = 1
|
||||
WHERE book.IsArchived = 0
|
||||
AND project.IsArchived = 0
|
||||
ORDER BY
|
||||
CASE p.Status WHEN N'NeedsReview' THEN 0 WHEN N'InProgress' THEN 1 ELSE 2 END,
|
||||
p.UpdatedUtc DESC;
|
||||
END;
|
||||
GO
|
||||
Loading…
x
Reference in New Issue
Block a user