PlotDirector/PlotLine/Sql/174_Phase27_WholeBookPlotIntelligence.sql

821 lines
35 KiB
Transact-SQL

SET ANSI_NULLS ON;
GO
SET QUOTED_IDENTIFIER ON;
GO
IF OBJECT_ID(N'dbo.AuthorPlotSeeds', N'U') IS NULL
BEGIN
CREATE TABLE dbo.AuthorPlotSeeds
(
AuthorPlotSeedID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_AuthorPlotSeeds PRIMARY KEY,
ProjectID int NOT NULL,
BookID int NOT NULL,
Description nvarchar(max) NOT NULL,
Status nvarchar(30) NOT NULL CONSTRAINT DF_AuthorPlotSeeds_Status DEFAULT N'Active',
MatchedPlotLineID int NULL,
Source nvarchar(40) NOT NULL CONSTRAINT DF_AuthorPlotSeeds_Source DEFAULT N'Author',
CreatedUtc datetime2 NOT NULL CONSTRAINT DF_AuthorPlotSeeds_CreatedUtc DEFAULT SYSUTCDATETIME(),
UpdatedUtc datetime2 NOT NULL CONSTRAINT DF_AuthorPlotSeeds_UpdatedUtc DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_AuthorPlotSeeds_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID),
CONSTRAINT FK_AuthorPlotSeeds_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID) ON DELETE CASCADE,
CONSTRAINT FK_AuthorPlotSeeds_MatchedPlotLines FOREIGN KEY (MatchedPlotLineID) REFERENCES dbo.PlotLines(PlotLineID) ON DELETE SET NULL
);
END;
GO
IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_AuthorPlotSeeds_Books' AND parent_object_id = OBJECT_ID(N'dbo.AuthorPlotSeeds'))
BEGIN
ALTER TABLE dbo.AuthorPlotSeeds DROP CONSTRAINT FK_AuthorPlotSeeds_Books;
ALTER TABLE dbo.AuthorPlotSeeds WITH CHECK
ADD CONSTRAINT FK_AuthorPlotSeeds_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID) ON DELETE CASCADE;
END;
GO
IF EXISTS (SELECT 1 FROM sys.foreign_keys WHERE name = N'FK_AuthorPlotSeeds_MatchedPlotLines' AND parent_object_id = OBJECT_ID(N'dbo.AuthorPlotSeeds'))
BEGIN
ALTER TABLE dbo.AuthorPlotSeeds DROP CONSTRAINT FK_AuthorPlotSeeds_MatchedPlotLines;
ALTER TABLE dbo.AuthorPlotSeeds WITH CHECK
ADD CONSTRAINT FK_AuthorPlotSeeds_MatchedPlotLines FOREIGN KEY (MatchedPlotLineID) REFERENCES dbo.PlotLines(PlotLineID) ON DELETE SET NULL;
END;
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_AuthorPlotSeeds_Book_Status' AND object_id = OBJECT_ID(N'dbo.AuthorPlotSeeds'))
CREATE INDEX IX_AuthorPlotSeeds_Book_Status ON dbo.AuthorPlotSeeds(BookID, Status);
GO
IF OBJECT_ID(N'dbo.WholeBookPlotSynthesisRuns', N'U') IS NULL
BEGIN
CREATE TABLE dbo.WholeBookPlotSynthesisRuns
(
WholeBookPlotSynthesisRunID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_WholeBookPlotSynthesisRuns PRIMARY KEY,
ProjectID int NOT NULL,
BookID int NOT NULL,
UserID int NOT NULL,
SourceStoryIntelligenceRunID int NULL,
SourceStoryIntelligenceRunKey AS ISNULL(SourceStoryIntelligenceRunID, 0) PERSISTED,
CharacterResolutionVersion nvarchar(120) NOT NULL,
Status nvarchar(30) NOT NULL CONSTRAINT DF_WholeBookPlotSynthesisRuns_Status DEFAULT N'Queued',
QueuedUtc datetime2 NOT NULL CONSTRAINT DF_WholeBookPlotSynthesisRuns_QueuedUtc DEFAULT SYSUTCDATETIME(),
StartedUtc datetime2 NULL,
CompletedUtc datetime2 NULL,
FailedUtc datetime2 NULL,
AttemptCount int NOT NULL CONSTRAINT DF_WholeBookPlotSynthesisRuns_AttemptCount DEFAULT 0,
Model nvarchar(100) NULL,
OpenAIResponseID nvarchar(120) NULL,
InputTokens int NULL,
OutputTokens int NULL,
CachedInputTokens int NULL,
TotalTokens int NULL,
DurationMs bigint NULL,
ErrorMessage nvarchar(max) NULL,
ErrorDetail nvarchar(max) NULL,
ResultJson nvarchar(max) NULL,
IsCurrent bit NOT NULL CONSTRAINT DF_WholeBookPlotSynthesisRuns_IsCurrent DEFAULT 1,
UpdatedUtc datetime2 NOT NULL CONSTRAINT DF_WholeBookPlotSynthesisRuns_UpdatedUtc DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_WholeBookPlotSynthesisRuns_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID),
CONSTRAINT FK_WholeBookPlotSynthesisRuns_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID) ON DELETE CASCADE,
CONSTRAINT FK_WholeBookPlotSynthesisRuns_AppUser FOREIGN KEY (UserID) REFERENCES dbo.AppUser(UserID),
CONSTRAINT FK_WholeBookPlotSynthesisRuns_SourceRun FOREIGN KEY (SourceStoryIntelligenceRunID) REFERENCES dbo.StoryIntelligenceRuns(StoryIntelligenceRunID) ON DELETE SET NULL
);
END;
GO
IF COL_LENGTH(N'dbo.WholeBookPlotSynthesisRuns', N'SourceStoryIntelligenceRunKey') IS NULL
ALTER TABLE dbo.WholeBookPlotSynthesisRuns ADD SourceStoryIntelligenceRunKey AS ISNULL(SourceStoryIntelligenceRunID, 0) PERSISTED;
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_WholeBookPlotSynthesisRuns_Queue' AND object_id = OBJECT_ID(N'dbo.WholeBookPlotSynthesisRuns'))
CREATE INDEX IX_WholeBookPlotSynthesisRuns_Queue ON dbo.WholeBookPlotSynthesisRuns(Status, QueuedUtc) INCLUDE (BookID, UserID);
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'UX_WholeBookPlotSynthesisRuns_CurrentVersion' AND object_id = OBJECT_ID(N'dbo.WholeBookPlotSynthesisRuns'))
CREATE UNIQUE INDEX UX_WholeBookPlotSynthesisRuns_CurrentVersion
ON dbo.WholeBookPlotSynthesisRuns(BookID, SourceStoryIntelligenceRunKey, CharacterResolutionVersion)
WHERE IsCurrent = 1 AND Status IN (N'Queued', N'Running', N'Completed');
GO
IF COL_LENGTH(N'dbo.StoryIntelligenceNarrativeCandidates', N'WholeBookPlotSynthesisRunID') IS NULL
ALTER TABLE dbo.StoryIntelligenceNarrativeCandidates ADD WholeBookPlotSynthesisRunID int NULL;
GO
IF COL_LENGTH(N'dbo.StoryIntelligenceNarrativeCandidates', N'AuthorPlotSeedID') IS NULL
ALTER TABLE dbo.StoryIntelligenceNarrativeCandidates ADD AuthorPlotSeedID int NULL;
GO
IF COL_LENGTH(N'dbo.StoryIntelligenceNarrativeCandidates', N'ExistingPlotLineMatchConfidence') IS NULL
ALTER TABLE dbo.StoryIntelligenceNarrativeCandidates ADD ExistingPlotLineMatchConfidence decimal(5,4) NULL;
GO
IF COL_LENGTH(N'dbo.StoryIntelligenceNarrativeCandidates', N'ExistingPlotLineMatchReason') IS NULL
ALTER TABLE dbo.StoryIntelligenceNarrativeCandidates ADD ExistingPlotLineMatchReason nvarchar(max) NULL;
GO
IF COL_LENGTH(N'dbo.StoryIntelligenceNarrativeCandidates', N'SeedMatchConfidence') IS NULL
ALTER TABLE dbo.StoryIntelligenceNarrativeCandidates ADD SeedMatchConfidence decimal(5,4) NULL;
GO
IF COL_LENGTH(N'dbo.StoryIntelligenceNarrativeCandidates', N'SeedMatchReason') IS NULL
ALTER TABLE dbo.StoryIntelligenceNarrativeCandidates ADD SeedMatchReason nvarchar(max) NULL;
GO
IF COL_LENGTH(N'dbo.StoryIntelligenceNarrativeCandidates', N'Source') IS NULL
ALTER TABLE dbo.StoryIntelligenceNarrativeCandidates ADD Source nvarchar(60) NULL;
GO
IF OBJECT_ID(N'dbo.StoryIntelligenceNarrativeCandidateEvents', N'U') IS NULL
BEGIN
CREATE TABLE dbo.StoryIntelligenceNarrativeCandidateEvents
(
NarrativeCandidateEventID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_StoryIntelligenceNarrativeCandidateEvents PRIMARY KEY,
NarrativeCandidateID int NOT NULL,
WholeBookPlotSynthesisRunID int NULL,
EventKey nvarchar(260) NOT NULL,
SceneID int NOT NULL,
EventType nvarchar(100) NOT NULL,
EventTitle nvarchar(200) NOT NULL,
EventDescription nvarchar(max) NULL,
Evidence nvarchar(max) NULL,
Confidence decimal(5,4) NULL,
SortOrder int NOT NULL CONSTRAINT DF_StoryIntelligenceNarrativeCandidateEvents_SortOrder DEFAULT 0,
IsIgnored bit NOT NULL CONSTRAINT DF_StoryIntelligenceNarrativeCandidateEvents_IsIgnored DEFAULT 0,
AcceptedThreadEventID int NULL,
CreatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceNarrativeCandidateEvents_CreatedUtc DEFAULT SYSUTCDATETIME(),
UpdatedUtc datetime2 NOT NULL CONSTRAINT DF_StoryIntelligenceNarrativeCandidateEvents_UpdatedUtc DEFAULT SYSUTCDATETIME(),
CONSTRAINT FK_StoryIntelligenceNarrativeCandidateEvents_Candidates FOREIGN KEY (NarrativeCandidateID) REFERENCES dbo.StoryIntelligenceNarrativeCandidates(NarrativeCandidateID) ON DELETE CASCADE,
CONSTRAINT FK_StoryIntelligenceNarrativeCandidateEvents_SynthesisRun FOREIGN KEY (WholeBookPlotSynthesisRunID) REFERENCES dbo.WholeBookPlotSynthesisRuns(WholeBookPlotSynthesisRunID),
CONSTRAINT FK_StoryIntelligenceNarrativeCandidateEvents_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID),
CONSTRAINT FK_StoryIntelligenceNarrativeCandidateEvents_AcceptedThreadEvents FOREIGN KEY (AcceptedThreadEventID) REFERENCES dbo.ThreadEvents(ThreadEventID)
);
END;
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'UX_StoryIntelligenceNarrativeCandidateEvents_Key' AND object_id = OBJECT_ID(N'dbo.StoryIntelligenceNarrativeCandidateEvents'))
CREATE UNIQUE INDEX UX_StoryIntelligenceNarrativeCandidateEvents_Key ON dbo.StoryIntelligenceNarrativeCandidateEvents(NarrativeCandidateID, EventKey);
GO
IF COL_LENGTH(N'dbo.ThreadEvents', N'Source') IS NULL
ALTER TABLE dbo.ThreadEvents ADD Source nvarchar(60) NOT NULL CONSTRAINT DF_ThreadEvents_Source DEFAULT N'Author';
GO
IF COL_LENGTH(N'dbo.ThreadEvents', N'SourceReferenceID') IS NULL
ALTER TABLE dbo.ThreadEvents ADD SourceReferenceID int NULL;
GO
IF COL_LENGTH(N'dbo.ThreadEvents', N'SourceKey') IS NULL
ALTER TABLE dbo.ThreadEvents ADD SourceKey nvarchar(260) NULL;
GO
IF COL_LENGTH(N'dbo.ThreadEvents', N'Confidence') IS NULL
ALTER TABLE dbo.ThreadEvents ADD Confidence decimal(5,4) NULL;
GO
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'UX_ThreadEvents_SourceKey' AND object_id = OBJECT_ID(N'dbo.ThreadEvents'))
CREATE UNIQUE INDEX UX_ThreadEvents_SourceKey ON dbo.ThreadEvents(Source, SourceKey) WHERE SourceKey IS NOT NULL;
GO
CREATE OR ALTER PROCEDURE dbo.AuthorPlotSeed_SaveForBook
@ProjectID int,
@BookID int,
@SeedsJson nvarchar(max)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @Seeds TABLE (Description nvarchar(max) NOT NULL, SortOrder int IDENTITY(1,1));
INSERT @Seeds (Description)
SELECT DISTINCT LEFT(LTRIM(RTRIM(JSON_VALUE(value, '$.description'))), 4000)
FROM OPENJSON(@SeedsJson)
WHERE NULLIF(LTRIM(RTRIM(JSON_VALUE(value, '$.description'))), N'') IS NOT NULL;
UPDATE dbo.AuthorPlotSeeds
SET Status = N'Archived',
UpdatedUtc = SYSUTCDATETIME()
WHERE ProjectID = @ProjectID
AND BookID = @BookID
AND Source = N'Author'
AND Status = N'Active'
AND NOT EXISTS
(
SELECT 1 FROM @Seeds seed WHERE seed.Description = AuthorPlotSeeds.Description
);
INSERT dbo.AuthorPlotSeeds (ProjectID, BookID, Description, Status, Source)
SELECT @ProjectID, @BookID, seed.Description, N'Active', N'Author'
FROM @Seeds seed
WHERE NOT EXISTS
(
SELECT 1
FROM dbo.AuthorPlotSeeds existing
WHERE existing.ProjectID = @ProjectID
AND existing.BookID = @BookID
AND existing.Source = N'Author'
AND existing.Description = seed.Description
AND existing.Status = N'Active'
);
END;
GO
CREATE OR ALTER PROCEDURE dbo.AuthorPlotSeed_ListByBook
@BookID int
AS
BEGIN
SET NOCOUNT ON;
SELECT AuthorPlotSeedID, ProjectID, BookID, Description, Status, MatchedPlotLineID, Source, CreatedUtc, UpdatedUtc
FROM dbo.AuthorPlotSeeds
WHERE BookID = @BookID AND Status = N'Active'
ORDER BY AuthorPlotSeedID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesisRun_Queue
@ProjectID int,
@BookID int,
@UserID int,
@SourceStoryIntelligenceRunID int = NULL,
@CharacterResolutionVersion nvarchar(120)
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @ExistingID int;
SELECT TOP (1) @ExistingID = WholeBookPlotSynthesisRunID
FROM dbo.WholeBookPlotSynthesisRuns
WHERE BookID = @BookID
AND ISNULL(SourceStoryIntelligenceRunID, 0) = ISNULL(@SourceStoryIntelligenceRunID, 0)
AND CharacterResolutionVersion = @CharacterResolutionVersion
AND IsCurrent = 1
AND Status IN (N'Queued', N'Running', N'Completed')
ORDER BY WholeBookPlotSynthesisRunID DESC;
IF @ExistingID IS NULL
BEGIN
INSERT dbo.WholeBookPlotSynthesisRuns (ProjectID, BookID, UserID, SourceStoryIntelligenceRunID, CharacterResolutionVersion, Status)
VALUES (@ProjectID, @BookID, @UserID, @SourceStoryIntelligenceRunID, @CharacterResolutionVersion, N'Queued');
SET @ExistingID = CAST(SCOPE_IDENTITY() AS int);
END;
SELECT * FROM dbo.WholeBookPlotSynthesisRuns WHERE WholeBookPlotSynthesisRunID = @ExistingID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesisRun_Retry
@BookID int,
@UserID int
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @PreviousID int, @ProjectID int, @SourceRunID int, @Version nvarchar(120);
SELECT TOP (1)
@PreviousID = WholeBookPlotSynthesisRunID,
@ProjectID = ProjectID,
@SourceRunID = SourceStoryIntelligenceRunID,
@Version = CharacterResolutionVersion
FROM dbo.WholeBookPlotSynthesisRuns
WHERE BookID = @BookID AND UserID = @UserID AND IsCurrent = 1 AND Status = N'Failed'
ORDER BY WholeBookPlotSynthesisRunID DESC;
IF @PreviousID IS NULL
BEGIN
SELECT TOP (0) * FROM dbo.WholeBookPlotSynthesisRuns;
RETURN;
END;
UPDATE dbo.WholeBookPlotSynthesisRuns
SET IsCurrent = 0,
Status = N'Superseded',
UpdatedUtc = SYSUTCDATETIME()
WHERE WholeBookPlotSynthesisRunID = @PreviousID;
INSERT dbo.WholeBookPlotSynthesisRuns (ProjectID, BookID, UserID, SourceStoryIntelligenceRunID, CharacterResolutionVersion, Status)
VALUES (@ProjectID, @BookID, @UserID, @SourceRunID, @Version, N'Queued');
SELECT * FROM dbo.WholeBookPlotSynthesisRuns WHERE WholeBookPlotSynthesisRunID = CAST(SCOPE_IDENTITY() AS int);
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesisRun_ClaimNext
@LeaseMinutes int = 90
AS
BEGIN
SET NOCOUNT ON;
SET XACT_ABORT ON;
DECLARE @RunID int;
SELECT TOP (1) @RunID = WholeBookPlotSynthesisRunID
FROM dbo.WholeBookPlotSynthesisRuns WITH (UPDLOCK, READPAST)
WHERE IsCurrent = 1
AND (
Status = N'Queued'
OR (Status = N'Running' AND StartedUtc < DATEADD(minute, -ABS(@LeaseMinutes), SYSUTCDATETIME()))
)
ORDER BY QueuedUtc, WholeBookPlotSynthesisRunID;
IF @RunID IS NULL
BEGIN
SELECT TOP (0) * FROM dbo.WholeBookPlotSynthesisRuns;
RETURN;
END;
UPDATE dbo.WholeBookPlotSynthesisRuns
SET Status = N'Running',
StartedUtc = SYSUTCDATETIME(),
AttemptCount = AttemptCount + 1,
UpdatedUtc = SYSUTCDATETIME()
WHERE WholeBookPlotSynthesisRunID = @RunID;
SELECT * FROM dbo.WholeBookPlotSynthesisRuns WHERE WholeBookPlotSynthesisRunID = @RunID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesisRun_GetCurrentByBook
@BookID int
AS
BEGIN
SET NOCOUNT ON;
SELECT TOP (1) *
FROM dbo.WholeBookPlotSynthesisRuns
WHERE BookID = @BookID AND IsCurrent = 1
ORDER BY WholeBookPlotSynthesisRunID DESC;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesisRun_Complete
@WholeBookPlotSynthesisRunID int,
@Model nvarchar(100),
@OpenAIResponseID nvarchar(120) = NULL,
@InputTokens int = NULL,
@OutputTokens int = NULL,
@CachedInputTokens int = NULL,
@TotalTokens int = NULL,
@DurationMs bigint = NULL,
@ResultJson nvarchar(max)
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.WholeBookPlotSynthesisRuns
SET Status = N'Completed',
CompletedUtc = SYSUTCDATETIME(),
Model = @Model,
OpenAIResponseID = @OpenAIResponseID,
InputTokens = @InputTokens,
OutputTokens = @OutputTokens,
CachedInputTokens = @CachedInputTokens,
TotalTokens = @TotalTokens,
DurationMs = @DurationMs,
ResultJson = @ResultJson,
ErrorMessage = NULL,
ErrorDetail = NULL,
UpdatedUtc = SYSUTCDATETIME()
WHERE WholeBookPlotSynthesisRunID = @WholeBookPlotSynthesisRunID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesisRun_Fail
@WholeBookPlotSynthesisRunID int,
@ErrorMessage nvarchar(max),
@ErrorDetail nvarchar(max) = NULL,
@DurationMs bigint = NULL
AS
BEGIN
SET NOCOUNT ON;
UPDATE dbo.WholeBookPlotSynthesisRuns
SET Status = N'Failed',
FailedUtc = SYSUTCDATETIME(),
ErrorMessage = @ErrorMessage,
ErrorDetail = @ErrorDetail,
DurationMs = @DurationMs,
UpdatedUtc = SYSUTCDATETIME()
WHERE WholeBookPlotSynthesisRunID = @WholeBookPlotSynthesisRunID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesis_ContextGet
@ProjectID int,
@BookID int
AS
BEGIN
SET NOCOUNT ON;
SELECT BookTitle = b.BookTitle
FROM dbo.Books b
WHERE b.BookID = @BookID AND b.ProjectID = @ProjectID AND b.IsArchived = 0;
SELECT
c.ChapterID,
c.ChapterNumber,
c.ChapterTitle,
s.SceneID,
s.SceneNumber,
s.SceneTitle,
s.StructuralSummary,
s.StructuralSetting,
pov.CharacterName AS PovCharacterName,
sms.SourceText
FROM dbo.Chapters c
INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
INNER JOIN dbo.SceneManuscriptSources sms ON sms.SceneID = s.SceneID
LEFT JOIN dbo.Characters pov ON pov.CharacterID = COALESCE(s.POVCharacterID, c.POVCharacterID)
WHERE c.BookID = @BookID AND c.IsArchived = 0
ORDER BY c.SortOrder, c.ChapterNumber, s.SortOrder, s.SceneNumber, s.SceneID;
SELECT CharacterID, CanonicalName = CharacterName
FROM dbo.Characters
WHERE ProjectID = @ProjectID AND IsArchived = 0
ORDER BY CharacterName;
SELECT alias.CharacterID, alias.Alias
FROM dbo.CharacterAliases alias
INNER JOIN dbo.Characters character ON character.CharacterID = alias.CharacterID
WHERE character.ProjectID = @ProjectID
AND character.IsArchived = 0
ORDER BY alias.CharacterID, alias.Alias;
SELECT
pl.PlotLineID,
Name = pl.PlotLineName,
pl.Description,
Importance = pi.ImportanceName,
pl.BookID
FROM dbo.PlotLines pl
LEFT JOIN dbo.PlotImportance pi ON pi.PlotImportanceID = pl.PlotImportanceID
WHERE pl.ProjectID = @ProjectID AND pl.IsArchived = 0
ORDER BY pl.SortOrder, pl.PlotLineName;
SELECT
pt.PlotThreadID,
pt.PlotLineID,
Title = pt.ThreadTitle,
pt.Summary,
Status = ts.StatusName
FROM dbo.PlotThreads pt
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
LEFT JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
WHERE pl.ProjectID = @ProjectID
AND pl.IsArchived = 0
AND pt.IsArchived = 0
ORDER BY pt.PlotLineID, pt.Importance DESC, pt.ThreadTitle;
EXEC dbo.AuthorPlotSeed_ListByBook @BookID;
SELECT
SceneCount = (
SELECT COUNT(1)
FROM dbo.Scenes s
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
WHERE c.BookID = @BookID AND c.IsArchived = 0 AND s.IsArchived = 0
),
SourceRowCount = (
SELECT COUNT(1)
FROM dbo.SceneManuscriptSources sms
INNER JOIN dbo.Scenes s ON s.SceneID = sms.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
WHERE c.BookID = @BookID AND c.IsArchived = 0 AND s.IsArchived = 0
),
MissingSourceCount = (
SELECT COUNT(1)
FROM dbo.Scenes s
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
LEFT JOIN dbo.SceneManuscriptSources sms ON sms.SceneID = s.SceneID
WHERE c.BookID = @BookID AND c.IsArchived = 0 AND s.IsArchived = 0 AND sms.SceneID IS NULL
),
DuplicateSourceSceneCount = 0;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesisCandidate_Save
@WholeBookPlotSynthesisRunID int,
@CandidateType nvarchar(30),
@CandidateKey nvarchar(220),
@ProposedTitle nvarchar(200),
@ProposedDescription nvarchar(max) = NULL,
@Category nvarchar(100) = NULL,
@InferredState nvarchar(100) = NULL,
@ChangeType nvarchar(40) = NULL,
@Significance nvarchar(20) = NULL,
@ExistingPlotLineID int = NULL,
@ExistingPlotThreadID int = NULL,
@RelatedPlotLineID int = NULL,
@ParentCandidateKey nvarchar(220) = NULL,
@ParentNarrativeCandidateID int = NULL,
@AuthorPlotSeedID int = NULL,
@ExistingPlotLineMatchConfidence decimal(5,4) = NULL,
@ExistingPlotLineMatchReason nvarchar(max) = NULL,
@SeedMatchConfidence decimal(5,4) = NULL,
@SeedMatchReason nvarchar(max) = NULL,
@Confidence decimal(5,4) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE @ProjectID int, @BookID int, @NarrativeCandidateID int;
SELECT @ProjectID = ProjectID, @BookID = BookID
FROM dbo.WholeBookPlotSynthesisRuns
WHERE WholeBookPlotSynthesisRunID = @WholeBookPlotSynthesisRunID;
SELECT @NarrativeCandidateID = NarrativeCandidateID
FROM dbo.StoryIntelligenceNarrativeCandidates
WHERE BookID = @BookID AND CandidateType = @CandidateType AND CandidateKey = @CandidateKey;
IF @NarrativeCandidateID IS NULL
BEGIN
INSERT dbo.StoryIntelligenceNarrativeCandidates
(
ProjectID, BookID, CandidateType, CandidateKey, ProposedTitle, ProposedDescription, Category,
InferredState, ChangeType, Significance, ExistingPlotLineID, ExistingPlotThreadID, RelatedPlotLineID,
ParentCandidateKey, ParentNarrativeCandidateID, WholeBookPlotSynthesisRunID, AuthorPlotSeedID,
ExistingPlotLineMatchConfidence, ExistingPlotLineMatchReason, SeedMatchConfidence, SeedMatchReason,
Confidence, Source
)
VALUES
(
@ProjectID, @BookID, @CandidateType, @CandidateKey, @ProposedTitle, @ProposedDescription, @Category,
@InferredState, @ChangeType, @Significance, @ExistingPlotLineID, @ExistingPlotThreadID, @RelatedPlotLineID,
@ParentCandidateKey, @ParentNarrativeCandidateID, @WholeBookPlotSynthesisRunID, @AuthorPlotSeedID,
@ExistingPlotLineMatchConfidence, @ExistingPlotLineMatchReason, @SeedMatchConfidence, @SeedMatchReason,
@Confidence, N'WholeBookPlotSynthesis'
);
SET @NarrativeCandidateID = CAST(SCOPE_IDENTITY() AS int);
END
ELSE
BEGIN
UPDATE dbo.StoryIntelligenceNarrativeCandidates
SET ProposedTitle = @ProposedTitle,
ProposedDescription = @ProposedDescription,
Category = @Category,
InferredState = @InferredState,
ChangeType = @ChangeType,
Significance = @Significance,
ExistingPlotLineID = @ExistingPlotLineID,
ExistingPlotThreadID = @ExistingPlotThreadID,
RelatedPlotLineID = @RelatedPlotLineID,
ParentCandidateKey = @ParentCandidateKey,
ParentNarrativeCandidateID = @ParentNarrativeCandidateID,
WholeBookPlotSynthesisRunID = @WholeBookPlotSynthesisRunID,
AuthorPlotSeedID = @AuthorPlotSeedID,
ExistingPlotLineMatchConfidence = @ExistingPlotLineMatchConfidence,
ExistingPlotLineMatchReason = @ExistingPlotLineMatchReason,
SeedMatchConfidence = @SeedMatchConfidence,
SeedMatchReason = @SeedMatchReason,
Confidence = @Confidence,
Source = N'WholeBookPlotSynthesis',
UpdatedUtc = SYSUTCDATETIME()
WHERE NarrativeCandidateID = @NarrativeCandidateID
AND Status = N'Pending';
END;
SELECT @NarrativeCandidateID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.WholeBookPlotSynthesisCandidateEvent_Save
@NarrativeCandidateID int,
@SynthesisRunID int,
@EventKey nvarchar(260),
@SceneID int,
@EventType nvarchar(100),
@EventTitle nvarchar(200),
@EventDescription nvarchar(max) = NULL,
@Evidence nvarchar(max) = NULL,
@Confidence decimal(5,4) = NULL,
@SortOrder int = 0
AS
BEGIN
SET NOCOUNT ON;
MERGE dbo.StoryIntelligenceNarrativeCandidateEvents AS target
USING (SELECT @NarrativeCandidateID AS NarrativeCandidateID, @EventKey AS EventKey) AS source
ON target.NarrativeCandidateID = source.NarrativeCandidateID AND target.EventKey = source.EventKey
WHEN MATCHED THEN
UPDATE SET WholeBookPlotSynthesisRunID = @SynthesisRunID,
SceneID = @SceneID,
EventType = @EventType,
EventTitle = @EventTitle,
EventDescription = @EventDescription,
Evidence = @Evidence,
Confidence = @Confidence,
SortOrder = @SortOrder,
UpdatedUtc = SYSUTCDATETIME()
WHEN NOT MATCHED THEN
INSERT (NarrativeCandidateID, WholeBookPlotSynthesisRunID, EventKey, SceneID, EventType, EventTitle, EventDescription, Evidence, Confidence, SortOrder)
VALUES (@NarrativeCandidateID, @SynthesisRunID, @EventKey, @SceneID, @EventType, @EventTitle, @EventDescription, @Evidence, @Confidence, @SortOrder);
END;
GO
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceNarrativeCandidate_Save
@ProjectID int,
@BookID int,
@CandidateType nvarchar(30),
@CandidateKey nvarchar(220),
@ProposedTitle nvarchar(200),
@ProposedDescription nvarchar(max) = NULL,
@Category nvarchar(100) = NULL,
@InferredState nvarchar(100) = NULL,
@ChangeType nvarchar(40) = NULL,
@Significance nvarchar(20) = NULL,
@ExistingPlotLineID int = NULL,
@ExistingPlotThreadID int = NULL,
@WholeBookPlotSynthesisRunID int = NULL,
@AuthorPlotSeedID int = NULL,
@ExistingPlotLineMatchConfidence decimal(5,4) = NULL,
@ExistingPlotLineMatchReason nvarchar(max) = NULL,
@SeedMatchConfidence decimal(5,4) = NULL,
@SeedMatchReason nvarchar(max) = NULL,
@Source nvarchar(60) = NULL,
@RelatedPlotLineID int = NULL,
@ParentCandidateKey nvarchar(220) = NULL,
@ParentNarrativeCandidateID int = NULL,
@Confidence decimal(5,4) = NULL
AS
BEGIN
SET NOCOUNT ON;
DECLARE @NarrativeCandidateID int;
SELECT @NarrativeCandidateID = NarrativeCandidateID
FROM dbo.StoryIntelligenceNarrativeCandidates
WHERE BookID = @BookID AND CandidateType = @CandidateType AND CandidateKey = @CandidateKey;
IF @ParentNarrativeCandidateID IS NULL AND @ParentCandidateKey IS NOT NULL
BEGIN
SELECT @ParentNarrativeCandidateID = NarrativeCandidateID
FROM dbo.StoryIntelligenceNarrativeCandidates
WHERE BookID = @BookID
AND CandidateType = N'PlotLine'
AND CandidateKey = @ParentCandidateKey;
END;
IF @NarrativeCandidateID IS NULL
BEGIN
INSERT dbo.StoryIntelligenceNarrativeCandidates
(ProjectID, BookID, CandidateType, CandidateKey, ProposedTitle, ProposedDescription, Category, InferredState,
ChangeType, Significance, ExistingPlotLineID, ExistingPlotThreadID, RelatedPlotLineID, ParentCandidateKey,
ParentNarrativeCandidateID, WholeBookPlotSynthesisRunID, AuthorPlotSeedID, ExistingPlotLineMatchConfidence,
ExistingPlotLineMatchReason, SeedMatchConfidence, SeedMatchReason, Confidence, Source)
VALUES
(@ProjectID, @BookID, @CandidateType, @CandidateKey, @ProposedTitle, @ProposedDescription, @Category, @InferredState,
@ChangeType, @Significance, @ExistingPlotLineID, @ExistingPlotThreadID, @RelatedPlotLineID, @ParentCandidateKey,
@ParentNarrativeCandidateID, @WholeBookPlotSynthesisRunID, @AuthorPlotSeedID, @ExistingPlotLineMatchConfidence,
@ExistingPlotLineMatchReason, @SeedMatchConfidence, @SeedMatchReason, @Confidence, COALESCE(@Source, N'SceneSignals'));
SET @NarrativeCandidateID = CAST(SCOPE_IDENTITY() AS int);
END
ELSE
BEGIN
UPDATE dbo.StoryIntelligenceNarrativeCandidates
SET ProposedTitle = @ProposedTitle,
ProposedDescription = @ProposedDescription,
Category = @Category,
InferredState = @InferredState,
ChangeType = @ChangeType,
Significance = @Significance,
ExistingPlotLineID = @ExistingPlotLineID,
ExistingPlotThreadID = @ExistingPlotThreadID,
RelatedPlotLineID = @RelatedPlotLineID,
ParentCandidateKey = @ParentCandidateKey,
ParentNarrativeCandidateID = @ParentNarrativeCandidateID,
WholeBookPlotSynthesisRunID = @WholeBookPlotSynthesisRunID,
AuthorPlotSeedID = @AuthorPlotSeedID,
ExistingPlotLineMatchConfidence = @ExistingPlotLineMatchConfidence,
ExistingPlotLineMatchReason = @ExistingPlotLineMatchReason,
SeedMatchConfidence = @SeedMatchConfidence,
SeedMatchReason = @SeedMatchReason,
Confidence = @Confidence,
Source = COALESCE(@Source, Source, N'SceneSignals'),
UpdatedUtc = SYSUTCDATETIME()
WHERE NarrativeCandidateID = @NarrativeCandidateID
AND Status = N'Pending';
END;
SELECT @NarrativeCandidateID;
END;
GO
CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceNarrativeCandidate_ListByBook
@BookID int
AS
BEGIN
SET NOCOUNT ON;
SELECT nc.NarrativeCandidateID, nc.ProjectID, nc.BookID, nc.CandidateType, nc.CandidateKey,
nc.ProposedTitle, nc.ProposedDescription, nc.Category, nc.InferredState, nc.ChangeType, nc.Significance,
nc.ExistingPlotLineID, epl.PlotLineName AS ExistingPlotLineName,
nc.ExistingPlotThreadID, ept.ThreadTitle AS ExistingPlotThreadTitle,
nc.WholeBookPlotSynthesisRunID, nc.AuthorPlotSeedID,
aps.Description AS AuthorPlotSeedDescription, aps.Source AS AuthorPlotSeedSource,
nc.ExistingPlotLineMatchConfidence,
nc.ExistingPlotLineMatchReason, nc.SeedMatchConfidence, nc.SeedMatchReason, nc.Source,
nc.RelatedPlotLineID, rpl.PlotLineName AS RelatedPlotLineName,
nc.ParentCandidateKey, nc.ParentNarrativeCandidateID, parent.ProposedTitle AS ParentCandidateTitle,
nc.MergeTargetNarrativeCandidateID,
nc.Confidence, nc.Status, nc.ReviewAction, nc.AcceptedPlotLineID, apl.PlotLineName AS AcceptedPlotLineName,
nc.AcceptedPlotThreadID, apt.ThreadTitle AS AcceptedPlotThreadTitle, nc.ReviewedByUserID, nc.ReviewedUtc,
nc.CreatedUtc, nc.UpdatedUtc
FROM dbo.StoryIntelligenceNarrativeCandidates nc
LEFT JOIN dbo.PlotLines epl ON epl.PlotLineID = nc.ExistingPlotLineID
LEFT JOIN dbo.AuthorPlotSeeds aps ON aps.AuthorPlotSeedID = nc.AuthorPlotSeedID
LEFT JOIN dbo.PlotThreads ept ON ept.PlotThreadID = nc.ExistingPlotThreadID
LEFT JOIN dbo.PlotLines rpl ON rpl.PlotLineID = nc.RelatedPlotLineID
LEFT JOIN dbo.StoryIntelligenceNarrativeCandidates parent ON parent.NarrativeCandidateID = nc.ParentNarrativeCandidateID
LEFT JOIN dbo.PlotLines apl ON apl.PlotLineID = nc.AcceptedPlotLineID
LEFT JOIN dbo.PlotThreads apt ON apt.PlotThreadID = nc.AcceptedPlotThreadID
WHERE nc.BookID = @BookID
ORDER BY CASE nc.CandidateType WHEN N'PlotLine' THEN 0 ELSE 1 END, nc.ProposedTitle;
SELECT e.NarrativeCandidateEvidenceID, e.NarrativeCandidateID, e.SceneID,
s.SceneNumber, s.SceneTitle, c.ChapterNumber, c.ChapterTitle,
e.ContributionType, e.Evidence, e.SortOrder, e.CreatedUtc
FROM dbo.StoryIntelligenceNarrativeCandidateEvidence e
INNER JOIN dbo.StoryIntelligenceNarrativeCandidates nc ON nc.NarrativeCandidateID = e.NarrativeCandidateID
INNER JOIN dbo.Scenes s ON s.SceneID = e.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
WHERE nc.BookID = @BookID
ORDER BY e.NarrativeCandidateID, c.SortOrder, s.SortOrder, e.SortOrder;
SELECT ev.NarrativeCandidateEventID, ev.NarrativeCandidateID, ev.WholeBookPlotSynthesisRunID, ev.EventKey,
ev.SceneID, s.SceneNumber, s.SceneTitle, c.ChapterNumber, c.ChapterTitle,
ev.EventType, ev.EventTitle, ev.EventDescription, ev.Evidence, ev.Confidence, ev.SortOrder,
ev.IsIgnored, ev.AcceptedThreadEventID, ev.CreatedUtc, ev.UpdatedUtc
FROM dbo.StoryIntelligenceNarrativeCandidateEvents ev
INNER JOIN dbo.StoryIntelligenceNarrativeCandidates nc ON nc.NarrativeCandidateID = ev.NarrativeCandidateID
INNER JOIN dbo.Scenes s ON s.SceneID = ev.SceneID
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
WHERE nc.BookID = @BookID
ORDER BY ev.NarrativeCandidateID, c.SortOrder, s.SortOrder, ev.SortOrder;
END;
GO
CREATE OR ALTER PROCEDURE dbo.ThreadEvent_Save
@ThreadEventID int = NULL,
@PlotThreadID int,
@SceneID int,
@EventTypeID int,
@PlotEventTypeID int = 2,
@TargetPlotLineID int = NULL,
@EventTitle nvarchar(200),
@EventDescription nvarchar(max) = NULL,
@Source nvarchar(60) = N'Author',
@SourceReferenceID int = NULL,
@SourceKey nvarchar(260) = NULL,
@Confidence decimal(5,4) = NULL
AS
BEGIN
SET NOCOUNT ON;
IF (@ThreadEventID IS NULL OR @ThreadEventID = 0) AND @SourceKey IS NOT NULL
BEGIN
SELECT @ThreadEventID = ThreadEventID
FROM dbo.ThreadEvents
WHERE Source = @Source AND SourceKey = @SourceKey;
END;
IF @ThreadEventID IS NULL OR @ThreadEventID = 0
BEGIN
INSERT dbo.ThreadEvents (PlotThreadID, SceneID, EventTypeID, PlotEventTypeID, TargetPlotLineID, EventTitle, EventDescription, Source, SourceReferenceID, SourceKey, Confidence)
VALUES (@PlotThreadID, @SceneID, @EventTypeID, @PlotEventTypeID, @TargetPlotLineID, @EventTitle, @EventDescription, @Source, @SourceReferenceID, @SourceKey, @Confidence);
SET @ThreadEventID = CAST(SCOPE_IDENTITY() AS int);
END
ELSE
BEGIN
UPDATE dbo.ThreadEvents
SET PlotThreadID = @PlotThreadID,
SceneID = @SceneID,
EventTypeID = @EventTypeID,
PlotEventTypeID = @PlotEventTypeID,
TargetPlotLineID = @TargetPlotLineID,
EventTitle = @EventTitle,
EventDescription = @EventDescription,
Source = @Source,
SourceReferenceID = @SourceReferenceID,
SourceKey = @SourceKey,
Confidence = @Confidence,
UpdatedDate = SYSUTCDATETIME()
WHERE ThreadEventID = @ThreadEventID
AND (Source <> N'Author' OR @Source = N'Author');
END;
DECLARE @EventTypeName nvarchar(100);
SELECT @EventTypeName = TypeName FROM dbo.ThreadEventTypes WHERE ThreadEventTypeID = @EventTypeID;
IF @EventTypeName = N'Introduced'
BEGIN
UPDATE pt
SET IntroducedSceneID = COALESCE(IntroducedSceneID, @SceneID),
ThreadStatusID = CASE WHEN ts.StatusName = N'Planned' THEN introduced.ThreadStatusID ELSE pt.ThreadStatusID END,
UpdatedDate = SYSUTCDATETIME()
FROM dbo.PlotThreads pt
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
CROSS APPLY (SELECT ThreadStatusID FROM dbo.ThreadStatuses WHERE StatusName = N'Introduced') introduced
WHERE pt.PlotThreadID = @PlotThreadID;
END;
IF @EventTypeName IN (N'Resolved', N'Payoff')
BEGIN
UPDATE pt
SET ActualResolutionSceneID = COALESCE(ActualResolutionSceneID, @SceneID),
ThreadStatusID = CASE WHEN @EventTypeName = N'Resolved' THEN resolved.ThreadStatusID ELSE pt.ThreadStatusID END,
UpdatedDate = SYSUTCDATETIME()
FROM dbo.PlotThreads pt
CROSS APPLY (SELECT ThreadStatusID FROM dbo.ThreadStatuses WHERE StatusName = N'Resolved') resolved
WHERE pt.PlotThreadID = @PlotThreadID;
END;
SELECT @ThreadEventID;
END;
GO