SET ANSI_NULLS ON; GO SET QUOTED_IDENTIFIER ON; GO IF OBJECT_ID(N'dbo.StoryIntelligenceBookPipelines', N'U') IS NULL BEGIN CREATE TABLE dbo.StoryIntelligenceBookPipelines ( StoryIntelligenceBookPipelineID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_StoryIntelligenceBookPipelines PRIMARY KEY, ProjectID int NOT NULL, BookID int NOT NULL, CurrentStage nvarchar(80) NOT NULL, LastCompletedStage nvarchar(80) NULL, CurrentReviewStage nvarchar(80) NULL, Status nvarchar(40) NOT NULL, CompletedUtc datetime2 NULL, LastRunID int NULL, CreatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceBookPipelines_CreatedUtc DEFAULT SYSUTCDATETIME(), UpdatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceBookPipelines_UpdatedUtc DEFAULT SYSUTCDATETIME(), CONSTRAINT FK_StoryIntelligenceBookPipelines_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID), CONSTRAINT FK_StoryIntelligenceBookPipelines_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID), CONSTRAINT FK_StoryIntelligenceBookPipelines_LastRun FOREIGN KEY (LastRunID) REFERENCES dbo.StoryIntelligenceRuns(StoryIntelligenceRunID), CONSTRAINT CK_StoryIntelligenceBookPipelines_Status CHECK (Status IN (N'InProgress', N'NeedsReview', N'Complete')), CONSTRAINT CK_StoryIntelligenceBookPipelines_CurrentStage CHECK (CurrentStage IN (N'Chapters', N'SceneReview', N'SceneImport', N'CharacterReview', N'CharacterImport', N'Complete')) ); END; GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'UX_StoryIntelligenceBookPipelines_Book' AND object_id = OBJECT_ID(N'dbo.StoryIntelligenceBookPipelines')) CREATE UNIQUE INDEX UX_StoryIntelligenceBookPipelines_Book ON dbo.StoryIntelligenceBookPipelines(BookID); GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_StoryIntelligenceBookPipelines_Project_Status' AND object_id = OBJECT_ID(N'dbo.StoryIntelligenceBookPipelines')) CREATE INDEX IX_StoryIntelligenceBookPipelines_Project_Status ON dbo.StoryIntelligenceBookPipelines(ProjectID, Status, UpdatedUtc DESC); GO ;WITH LatestCommittedBookRun AS ( SELECT CAST(r.ProjectID AS int) AS ProjectID, CAST(r.BookID AS int) AS BookID, r.StoryIntelligenceRunID, ROW_NUMBER() OVER (PARTITION BY r.BookID ORDER BY r.StoryIntelligenceRunID DESC) AS RowNumber FROM dbo.StoryIntelligenceRuns r INNER JOIN dbo.StoryIntelligenceImportCommits commitRows ON commitRows.StoryIntelligenceRunID = r.StoryIntelligenceRunID AND commitRows.Status = N'Completed' INNER JOIN dbo.Books b ON b.BookID = r.BookID INNER JOIN dbo.Projects p ON p.ProjectID = r.ProjectID WHERE r.ProjectID IS NOT NULL AND r.BookID IS NOT NULL AND b.IsArchived = 0 AND p.IsArchived = 0 ) INSERT dbo.StoryIntelligenceBookPipelines (ProjectID, BookID, CurrentStage, LastCompletedStage, CurrentReviewStage, Status, CompletedUtc, LastRunID) SELECT source.ProjectID, source.BookID, N'CharacterReview', N'SceneImport', N'CharacterReview', N'NeedsReview', NULL, source.StoryIntelligenceRunID FROM LatestCommittedBookRun source WHERE source.RowNumber = 1 AND NOT EXISTS ( SELECT 1 FROM dbo.StoryIntelligenceBookPipelines existing WHERE existing.BookID = source.BookID ); 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.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 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), UpdatedUtc = SYSUTCDATETIME() WHERE BookID = @BookID; END ELSE BEGIN INSERT dbo.StoryIntelligenceBookPipelines (ProjectID, BookID, CurrentStage, LastCompletedStage, CurrentReviewStage, Status, CompletedUtc, LastRunID) VALUES (@ProjectID, @BookID, @CurrentStage, @LastCompletedStage, @CurrentReviewStage, @Status, @CompletedUtc, @LastRunID); 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.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_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.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 CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceBookPipeline_ListCommittedRunsByBook @BookID int, @UserID int AS BEGIN SET NOCOUNT ON; SELECT r.StoryIntelligenceRunID, r.UserID, CAST(r.ProjectID AS int) AS ProjectID, project.ProjectName, CAST(r.BookID AS int) AS BookID, book.BookTitle, book.Subtitle AS BookSubtitle, CAST(r.ChapterID AS int) AS ChapterID, CAST(COALESCE(r.ChapterNumber, chapter.ChapterNumber) AS decimal(10,2)) AS ChapterNumber, chapter.ChapterTitle, r.SourceLabel FROM dbo.StoryIntelligenceRuns r INNER JOIN dbo.StoryIntelligenceImportCommits commitRows ON commitRows.StoryIntelligenceRunID = r.StoryIntelligenceRunID AND commitRows.Status = N'Completed' INNER JOIN dbo.Books book ON book.BookID = r.BookID INNER JOIN dbo.Projects project ON project.ProjectID = r.ProjectID INNER JOIN dbo.Chapters chapter ON chapter.ChapterID = r.ChapterID INNER JOIN dbo.ProjectUserAccess access ON access.ProjectID = project.ProjectID AND access.UserID = @UserID AND access.IsActive = 1 WHERE r.BookID = @BookID AND r.ProjectID IS NOT NULL AND r.ChapterID IS NOT NULL AND r.BookID IS NOT NULL AND book.IsArchived = 0 AND project.IsArchived = 0 AND chapter.IsArchived = 0 ORDER BY chapter.SortOrder, chapter.ChapterNumber, r.StoryIntelligenceRunID; END; GO