846 lines
41 KiB
Transact-SQL
846 lines
41 KiB
Transact-SQL
SET ANSI_NULLS ON;
|
|
GO
|
|
SET QUOTED_IDENTIFIER ON;
|
|
GO
|
|
|
|
IF OBJECT_ID(N'dbo.WarningTypes', N'U') IS NULL
|
|
BEGIN
|
|
CREATE TABLE dbo.WarningTypes
|
|
(
|
|
WarningTypeID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_WarningTypes PRIMARY KEY,
|
|
TypeName nvarchar(150) NOT NULL,
|
|
Description nvarchar(max) NULL,
|
|
SortOrder int NOT NULL,
|
|
IsActive bit NOT NULL CONSTRAINT DF_WarningTypes_IsActive DEFAULT 1,
|
|
CONSTRAINT UQ_WarningTypes_TypeName UNIQUE (TypeName)
|
|
);
|
|
END;
|
|
GO
|
|
|
|
IF OBJECT_ID(N'dbo.WarningSeverities', N'U') IS NULL
|
|
BEGIN
|
|
CREATE TABLE dbo.WarningSeverities
|
|
(
|
|
WarningSeverityID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_WarningSeverities PRIMARY KEY,
|
|
SeverityName nvarchar(50) NOT NULL,
|
|
SortOrder int NOT NULL,
|
|
IsActive bit NOT NULL CONSTRAINT DF_WarningSeverities_IsActive DEFAULT 1,
|
|
CONSTRAINT UQ_WarningSeverities_SeverityName UNIQUE (SeverityName)
|
|
);
|
|
END;
|
|
GO
|
|
|
|
IF OBJECT_ID(N'dbo.ValidationRuns', N'U') IS NULL
|
|
BEGIN
|
|
CREATE TABLE dbo.ValidationRuns
|
|
(
|
|
ValidationRunID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ValidationRuns PRIMARY KEY,
|
|
ProjectID int NOT NULL,
|
|
BookID int NULL,
|
|
StartedDate datetime2 NOT NULL CONSTRAINT DF_ValidationRuns_StartedDate DEFAULT SYSUTCDATETIME(),
|
|
CompletedDate datetime2 NULL,
|
|
TriggeredBy nvarchar(100) NULL,
|
|
ScopeDescription nvarchar(200) NULL,
|
|
CONSTRAINT FK_ValidationRuns_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID),
|
|
CONSTRAINT FK_ValidationRuns_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID)
|
|
);
|
|
END;
|
|
GO
|
|
|
|
IF OBJECT_ID(N'dbo.ContinuityWarnings', N'U') IS NULL
|
|
BEGIN
|
|
CREATE TABLE dbo.ContinuityWarnings
|
|
(
|
|
ContinuityWarningID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_ContinuityWarnings PRIMARY KEY,
|
|
ValidationRunID int NULL,
|
|
ProjectID int NOT NULL,
|
|
BookID int NULL,
|
|
ChapterID int NULL,
|
|
SceneID int NULL,
|
|
EntityType nvarchar(80) NOT NULL,
|
|
EntityID int NULL,
|
|
WarningTypeID int NOT NULL,
|
|
WarningSeverityID int NOT NULL,
|
|
Message nvarchar(500) NOT NULL,
|
|
Details nvarchar(max) NULL,
|
|
IsDismissed bit NOT NULL CONSTRAINT DF_ContinuityWarnings_IsDismissed DEFAULT 0,
|
|
IsIntentional bit NOT NULL CONSTRAINT DF_ContinuityWarnings_IsIntentional DEFAULT 0,
|
|
CreatedDate datetime2 NOT NULL CONSTRAINT DF_ContinuityWarnings_CreatedDate DEFAULT SYSUTCDATETIME(),
|
|
LastDetectedDate datetime2 NOT NULL CONSTRAINT DF_ContinuityWarnings_LastDetectedDate DEFAULT SYSUTCDATETIME(),
|
|
CONSTRAINT FK_ContinuityWarnings_ValidationRuns FOREIGN KEY (ValidationRunID) REFERENCES dbo.ValidationRuns(ValidationRunID),
|
|
CONSTRAINT FK_ContinuityWarnings_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID),
|
|
CONSTRAINT FK_ContinuityWarnings_Books FOREIGN KEY (BookID) REFERENCES dbo.Books(BookID),
|
|
CONSTRAINT FK_ContinuityWarnings_Chapters FOREIGN KEY (ChapterID) REFERENCES dbo.Chapters(ChapterID),
|
|
CONSTRAINT FK_ContinuityWarnings_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID),
|
|
CONSTRAINT FK_ContinuityWarnings_Types FOREIGN KEY (WarningTypeID) REFERENCES dbo.WarningTypes(WarningTypeID),
|
|
CONSTRAINT FK_ContinuityWarnings_Severities FOREIGN KEY (WarningSeverityID) REFERENCES dbo.WarningSeverities(WarningSeverityID)
|
|
);
|
|
END;
|
|
GO
|
|
|
|
IF OBJECT_ID(N'dbo.SceneDependencyTypes', N'U') IS NULL
|
|
BEGIN
|
|
CREATE TABLE dbo.SceneDependencyTypes
|
|
(
|
|
SceneDependencyTypeID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneDependencyTypes PRIMARY KEY,
|
|
TypeName nvarchar(100) NOT NULL,
|
|
SortOrder int NOT NULL,
|
|
IsActive bit NOT NULL CONSTRAINT DF_SceneDependencyTypes_IsActive DEFAULT 1,
|
|
CONSTRAINT UQ_SceneDependencyTypes_TypeName UNIQUE (TypeName)
|
|
);
|
|
END;
|
|
GO
|
|
|
|
IF OBJECT_ID(N'dbo.SceneDependencies', N'U') IS NULL
|
|
BEGIN
|
|
CREATE TABLE dbo.SceneDependencies
|
|
(
|
|
SceneDependencyID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneDependencies PRIMARY KEY,
|
|
SourceSceneID int NOT NULL,
|
|
TargetSceneID int NOT NULL,
|
|
SceneDependencyTypeID int NOT NULL,
|
|
Description nvarchar(max) NULL,
|
|
CreatedDate datetime2 NOT NULL CONSTRAINT DF_SceneDependencies_CreatedDate DEFAULT SYSUTCDATETIME(),
|
|
UpdatedDate datetime2 NOT NULL CONSTRAINT DF_SceneDependencies_UpdatedDate DEFAULT SYSUTCDATETIME(),
|
|
IsArchived bit NOT NULL CONSTRAINT DF_SceneDependencies_IsArchived DEFAULT 0,
|
|
CONSTRAINT FK_SceneDependencies_SourceScene FOREIGN KEY (SourceSceneID) REFERENCES dbo.Scenes(SceneID),
|
|
CONSTRAINT FK_SceneDependencies_TargetScene FOREIGN KEY (TargetSceneID) REFERENCES dbo.Scenes(SceneID),
|
|
CONSTRAINT FK_SceneDependencies_Types FOREIGN KEY (SceneDependencyTypeID) REFERENCES dbo.SceneDependencyTypes(SceneDependencyTypeID)
|
|
);
|
|
END;
|
|
GO
|
|
|
|
IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_ContinuityWarnings_Project' AND object_id = OBJECT_ID(N'dbo.ContinuityWarnings'))
|
|
CREATE INDEX IX_ContinuityWarnings_Project ON dbo.ContinuityWarnings(ProjectID, BookID, SceneID, IsDismissed, IsIntentional, WarningSeverityID);
|
|
GO
|
|
|
|
MERGE dbo.WarningSeverities AS target
|
|
USING (VALUES (N'Info', 10), (N'Warning', 20), (N'Error', 30)) AS source (SeverityName, SortOrder)
|
|
ON target.SeverityName = source.SeverityName
|
|
WHEN NOT MATCHED THEN INSERT (SeverityName, SortOrder) VALUES (source.SeverityName, source.SortOrder);
|
|
GO
|
|
|
|
MERGE dbo.WarningTypes AS target
|
|
USING (VALUES
|
|
(N'Thread Resolved Before Introduced', N'Plot thread appears resolved before introduction.', 10),
|
|
(N'Question Answered Before Raised', N'Question event appears answered before raised.', 20),
|
|
(N'Clue Without Payoff', N'Clue has no payoff or resolution event.', 30),
|
|
(N'Thread Dormant', N'Open plot thread has gone quiet for many scenes.', 40),
|
|
(N'Asset Used Before Introduced', N'Asset appears used before it is introduced.', 50),
|
|
(N'Asset State Dependency Missing', N'Asset dependency may not have been satisfied.', 60),
|
|
(N'Asset Custody Conflict', N'Asset custody may not match use.', 70),
|
|
(N'Asset Location Conflict', N'Asset location changes without a movement event.', 80),
|
|
(N'Character Knowledge Conflict', N'Character knowledge may need a record.', 90),
|
|
(N'Scene Dependency Broken', N'Scene dependency order may be broken.', 100),
|
|
(N'Character Location Conflict', N'Character appears in conflicting scene locations.', 110),
|
|
(N'Scene Date Order Conflict', N'Narrative order and exact dates may conflict.', 120),
|
|
(N'Long Unexplained Time Gap', N'Long gap without time passage explanation.', 130),
|
|
(N'Sustained High Intensity', N'Metric remains high for many scenes.', 140),
|
|
(N'Sustained Low Intensity', N'Metric remains low for many scenes.', 150),
|
|
(N'Missing Scene Location', N'Scene has no primary location.', 160),
|
|
(N'Missing Scene Purpose', N'Scene has no purpose.', 170),
|
|
(N'Missing Scene Outcome', N'Scene has no outcome.', 180),
|
|
(N'Too Many Open Major Threads', N'Many major plot threads appear open at the same time.', 190)
|
|
) AS source (TypeName, Description, SortOrder)
|
|
ON target.TypeName = source.TypeName
|
|
WHEN NOT MATCHED THEN INSERT (TypeName, Description, SortOrder) VALUES (source.TypeName, source.Description, source.SortOrder);
|
|
GO
|
|
|
|
MERGE dbo.SceneDependencyTypes AS target
|
|
USING (VALUES
|
|
(N'Must Occur Before', 10), (N'Emotional Setup', 20), (N'Reveal Setup', 30), (N'Action Consequence', 40),
|
|
(N'Travel Dependency', 50), (N'Relationship Dependency', 60), (N'Knowledge Dependency', 70), (N'Object Dependency', 80)
|
|
) AS source (TypeName, SortOrder)
|
|
ON target.TypeName = source.TypeName
|
|
WHEN NOT MATCHED THEN INSERT (TypeName, SortOrder) VALUES (source.TypeName, source.SortOrder);
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.WarningLookup_All
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
SELECT WarningTypeID, TypeName, Description, SortOrder, IsActive FROM dbo.WarningTypes WHERE IsActive = 1 ORDER BY SortOrder;
|
|
SELECT WarningSeverityID, SeverityName, SortOrder, IsActive FROM dbo.WarningSeverities WHERE IsActive = 1 ORDER BY SortOrder;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ValidationRun_Create
|
|
@ProjectID int,
|
|
@BookID int = NULL,
|
|
@TriggeredBy nvarchar(100) = NULL,
|
|
@ScopeDescription nvarchar(200) = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
INSERT dbo.ValidationRuns (ProjectID, BookID, TriggeredBy, ScopeDescription)
|
|
VALUES (@ProjectID, @BookID, @TriggeredBy, @ScopeDescription);
|
|
DECLARE @ValidationRunID int = CAST(SCOPE_IDENTITY() AS int);
|
|
RETURN @ValidationRunID;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ValidationRun_Complete
|
|
@ValidationRunID int
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
UPDATE dbo.ValidationRuns SET CompletedDate = SYSUTCDATETIME() WHERE ValidationRunID = @ValidationRunID;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_ClearActive
|
|
@ProjectID int,
|
|
@BookID int = NULL,
|
|
@SceneID int = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
DELETE cw
|
|
FROM dbo.ContinuityWarnings cw
|
|
WHERE cw.ProjectID = @ProjectID
|
|
AND (@BookID IS NULL OR cw.BookID = @BookID)
|
|
AND (@SceneID IS NULL OR cw.SceneID = @SceneID)
|
|
AND cw.IsDismissed = 0
|
|
AND cw.IsIntentional = 0;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_Upsert
|
|
@ValidationRunID int = NULL,
|
|
@ProjectID int,
|
|
@BookID int = NULL,
|
|
@ChapterID int = NULL,
|
|
@SceneID int = NULL,
|
|
@EntityType nvarchar(80),
|
|
@EntityID int = NULL,
|
|
@WarningTypeName nvarchar(150),
|
|
@SeverityName nvarchar(50),
|
|
@Message nvarchar(500),
|
|
@Details nvarchar(max) = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
DECLARE @WarningTypeID int = (SELECT WarningTypeID FROM dbo.WarningTypes WHERE TypeName = @WarningTypeName);
|
|
DECLARE @WarningSeverityID int = (SELECT WarningSeverityID FROM dbo.WarningSeverities WHERE SeverityName = @SeverityName);
|
|
|
|
IF @WarningTypeID IS NULL OR @WarningSeverityID IS NULL
|
|
RETURN;
|
|
|
|
DECLARE @ExistingID int;
|
|
SELECT TOP (1) @ExistingID = ContinuityWarningID
|
|
FROM dbo.ContinuityWarnings
|
|
WHERE ProjectID = @ProjectID
|
|
AND ISNULL(BookID, 0) = ISNULL(@BookID, 0)
|
|
AND ISNULL(ChapterID, 0) = ISNULL(@ChapterID, 0)
|
|
AND ISNULL(SceneID, 0) = ISNULL(@SceneID, 0)
|
|
AND EntityType = @EntityType
|
|
AND ISNULL(EntityID, 0) = ISNULL(@EntityID, 0)
|
|
AND WarningTypeID = @WarningTypeID
|
|
AND IsDismissed = 0
|
|
AND IsIntentional = 0;
|
|
|
|
IF @ExistingID IS NULL
|
|
BEGIN
|
|
INSERT dbo.ContinuityWarnings (ValidationRunID, ProjectID, BookID, ChapterID, SceneID, EntityType, EntityID, WarningTypeID, WarningSeverityID, Message, Details)
|
|
VALUES (@ValidationRunID, @ProjectID, @BookID, @ChapterID, @SceneID, @EntityType, @EntityID, @WarningTypeID, @WarningSeverityID, @Message, @Details);
|
|
END
|
|
ELSE
|
|
BEGIN
|
|
UPDATE dbo.ContinuityWarnings
|
|
SET ValidationRunID = @ValidationRunID,
|
|
WarningSeverityID = @WarningSeverityID,
|
|
Message = @Message,
|
|
Details = @Details,
|
|
LastDetectedDate = SYSUTCDATETIME()
|
|
WHERE ContinuityWarningID = @ExistingID;
|
|
END
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER VIEW dbo.NarrativeSceneOrder
|
|
AS
|
|
SELECT p.ProjectID, b.BookID, c.ChapterID, s.SceneID,
|
|
b.SortOrder AS BookSortOrder, c.SortOrder AS ChapterSortOrder, s.SortOrder AS SceneSortOrder,
|
|
ROW_NUMBER() OVER (PARTITION BY p.ProjectID ORDER BY b.SortOrder, b.BookNumber, c.SortOrder, c.ChapterNumber, s.SortOrder, s.SceneNumber, s.SceneID) AS GlobalSceneIndex,
|
|
ROW_NUMBER() OVER (PARTITION BY b.BookID ORDER BY c.SortOrder, c.ChapterNumber, s.SortOrder, s.SceneNumber, s.SceneID) AS BookSceneIndex,
|
|
b.BookTitle, b.BookNumber, c.ChapterNumber, c.ChapterTitle, s.SceneNumber, s.SceneTitle,
|
|
s.PrimaryLocationID, s.StartDateTime, s.RelativeTimeText, s.ScenePurposeNotes, s.SceneOutcomeNotes
|
|
FROM dbo.Projects p
|
|
INNER JOIN dbo.Books b ON b.ProjectID = p.ProjectID AND b.IsArchived = 0
|
|
INNER JOIN dbo.Chapters c ON c.BookID = b.BookID AND c.IsArchived = 0
|
|
INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
|
|
WHERE p.IsArchived = 0;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.NarrativeSceneOrder_GetByProject
|
|
@ProjectID int,
|
|
@BookID int = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
SELECT * FROM dbo.NarrativeSceneOrder
|
|
WHERE ProjectID = @ProjectID AND (@BookID IS NULL OR BookID = @BookID)
|
|
ORDER BY GlobalSceneIndex;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityValidation_ValidateProject
|
|
@ProjectID int,
|
|
@BookID int = NULL,
|
|
@SceneID int = NULL,
|
|
@TriggeredBy nvarchar(100) = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
DECLARE @ValidationRunID int;
|
|
DECLARE @Scope nvarchar(200) = CASE WHEN @SceneID IS NOT NULL THEN N'Scene validation' WHEN @BookID IS NOT NULL THEN N'Book validation' ELSE N'Project validation' END;
|
|
EXEC @ValidationRunID = dbo.ValidationRun_Create @ProjectID = @ProjectID, @BookID = @BookID, @TriggeredBy = @TriggeredBy, @ScopeDescription = @Scope;
|
|
EXEC dbo.ContinuityWarning_ClearActive @ProjectID = @ProjectID, @BookID = @BookID, @SceneID = @SceneID;
|
|
|
|
DECLARE @Warnings TABLE
|
|
(
|
|
BookID int NULL, ChapterID int NULL, SceneID int NULL, EntityType nvarchar(80), EntityID int NULL,
|
|
WarningTypeName nvarchar(150), SeverityName nvarchar(50), Message nvarchar(500), Details nvarchar(max) NULL
|
|
);
|
|
|
|
;WITH ScopeScenes AS
|
|
(
|
|
SELECT * FROM dbo.NarrativeSceneOrder
|
|
WHERE ProjectID = @ProjectID AND (@BookID IS NULL OR BookID = @BookID) AND (@SceneID IS NULL OR SceneID = @SceneID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'Scene', SceneID, N'Missing Scene Location', N'Info',
|
|
N'Possible issue: this scene has no primary location yet.',
|
|
N'Add a primary location if the scene depends on where characters or assets are.'
|
|
FROM ScopeScenes WHERE PrimaryLocationID IS NULL;
|
|
|
|
;WITH ScopeScenes AS
|
|
(
|
|
SELECT nso.*
|
|
FROM dbo.NarrativeSceneOrder nso
|
|
WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@SceneID IS NULL OR nso.SceneID = @SceneID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT ss.BookID, ss.ChapterID, ss.SceneID, N'Scene', ss.SceneID, N'Missing Scene Purpose', N'Info',
|
|
N'Possible issue: this scene has no purpose label or purpose notes.',
|
|
N'Add a scene purpose if this is not intentional.'
|
|
FROM ScopeScenes ss
|
|
WHERE ISNULL(NULLIF(LTRIM(RTRIM(ss.ScenePurposeNotes)), N''), N'') = N''
|
|
AND NOT EXISTS (SELECT 1 FROM dbo.ScenePurposes sp WHERE sp.SceneID = ss.SceneID);
|
|
|
|
;WITH ScopeScenes AS
|
|
(
|
|
SELECT * FROM dbo.NarrativeSceneOrder
|
|
WHERE ProjectID = @ProjectID AND (@BookID IS NULL OR BookID = @BookID) AND (@SceneID IS NULL OR SceneID = @SceneID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'Scene', SceneID, N'Missing Scene Outcome', N'Info',
|
|
N'Possible issue: this scene has no outcome notes.',
|
|
N'Add an outcome if the result of the scene needs tracking.'
|
|
FROM ScopeScenes WHERE ISNULL(NULLIF(LTRIM(RTRIM(SceneOutcomeNotes)), N''), N'') = N'';
|
|
|
|
;WITH Ordered AS
|
|
(
|
|
SELECT *, LAG(SceneID) OVER (ORDER BY GlobalSceneIndex) AS PreviousSceneID,
|
|
LAG(StartDateTime) OVER (ORDER BY GlobalSceneIndex) AS PreviousStartDateTime
|
|
FROM dbo.NarrativeSceneOrder
|
|
WHERE ProjectID = @ProjectID AND (@BookID IS NULL OR BookID = @BookID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'Scene', SceneID, N'Scene Date Order Conflict', N'Warning',
|
|
N'Possible issue: this narrative scene has an earlier story date than the scene before it.',
|
|
CONCAT(N'Previous date: ', CONVERT(nvarchar(30), PreviousStartDateTime, 120), N'. Current date: ', CONVERT(nvarchar(30), StartDateTime, 120), N'.')
|
|
FROM Ordered
|
|
WHERE (@SceneID IS NULL OR SceneID = @SceneID)
|
|
AND PreviousStartDateTime IS NOT NULL AND StartDateTime IS NOT NULL AND StartDateTime < PreviousStartDateTime;
|
|
|
|
;WITH Ordered AS
|
|
(
|
|
SELECT *, LAG(StartDateTime) OVER (ORDER BY GlobalSceneIndex) AS PreviousStartDateTime
|
|
FROM dbo.NarrativeSceneOrder
|
|
WHERE ProjectID = @ProjectID AND (@BookID IS NULL OR BookID = @BookID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'Scene', SceneID, N'Long Unexplained Time Gap', N'Info',
|
|
N'Possible issue: there is a long story-time gap before this scene without a clear time-passage note.',
|
|
CONCAT(N'Gap: ', DATEDIFF(day, PreviousStartDateTime, StartDateTime), N' days.')
|
|
FROM Ordered o
|
|
WHERE (@SceneID IS NULL OR SceneID = @SceneID)
|
|
AND PreviousStartDateTime IS NOT NULL AND StartDateTime IS NOT NULL
|
|
AND DATEDIFF(day, PreviousStartDateTime, StartDateTime) > 14
|
|
AND ISNULL(NULLIF(LTRIM(RTRIM(RelativeTimeText)), N''), N'') = N''
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM dbo.ScenePurposes sp
|
|
INNER JOIN dbo.ScenePurposeTypes spt ON spt.ScenePurposeTypeID = sp.ScenePurposeTypeID
|
|
WHERE sp.SceneID = o.SceneID AND spt.PurposeName = N'Time Passage'
|
|
);
|
|
|
|
;WITH Events AS
|
|
(
|
|
SELECT pt.PlotThreadID, pt.ThreadTitle, pt.Importance, ts.IsOpenStatus, te.SceneID, tet.TypeName, nso.BookID, nso.ChapterID, nso.GlobalSceneIndex
|
|
FROM dbo.ThreadEvents te
|
|
INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
|
|
INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID AND pt.IsArchived = 0
|
|
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
|
|
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.ProjectID = @ProjectID AND pl.IsArchived = 0
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = te.SceneID
|
|
WHERE (@BookID IS NULL OR nso.BookID = @BookID)
|
|
),
|
|
ThreadRanges AS
|
|
(
|
|
SELECT PlotThreadID, ThreadTitle, Importance, IsOpenStatus,
|
|
MIN(CASE WHEN TypeName = N'Introduced' THEN GlobalSceneIndex END) AS IntroducedIndex,
|
|
MIN(CASE WHEN TypeName IN (N'Resolved', N'Payoff') THEN GlobalSceneIndex END) AS ResolvedIndex,
|
|
MIN(CASE WHEN TypeName = N'Question Raised' THEN GlobalSceneIndex END) AS QuestionRaisedIndex,
|
|
MIN(CASE WHEN TypeName = N'Question Answered' THEN GlobalSceneIndex END) AS QuestionAnsweredIndex,
|
|
MAX(GlobalSceneIndex) AS LastEventIndex,
|
|
MAX(BookID) AS BookID, MAX(ChapterID) AS ChapterID, MIN(SceneID) AS AnySceneID
|
|
FROM Events GROUP BY PlotThreadID, ThreadTitle, Importance, IsOpenStatus
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, AnySceneID, N'PlotThread', PlotThreadID, N'Thread Resolved Before Introduced', N'Warning',
|
|
CONCAT(N'Possible issue: "', ThreadTitle, N'" appears resolved before it is introduced.'),
|
|
NULL
|
|
FROM ThreadRanges
|
|
WHERE ResolvedIndex IS NOT NULL AND IntroducedIndex IS NOT NULL AND ResolvedIndex < IntroducedIndex;
|
|
|
|
;WITH Events AS
|
|
(
|
|
SELECT pt.PlotThreadID, pt.ThreadTitle, te.SceneID, tet.TypeName, nso.BookID, nso.ChapterID, nso.GlobalSceneIndex
|
|
FROM dbo.ThreadEvents te
|
|
INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
|
|
INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID AND pt.IsArchived = 0
|
|
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.ProjectID = @ProjectID AND pl.IsArchived = 0
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = te.SceneID
|
|
WHERE (@BookID IS NULL OR nso.BookID = @BookID)
|
|
),
|
|
ThreadRanges AS
|
|
(
|
|
SELECT PlotThreadID, ThreadTitle,
|
|
MIN(CASE WHEN TypeName = N'Question Raised' THEN GlobalSceneIndex END) AS QuestionRaisedIndex,
|
|
MIN(CASE WHEN TypeName = N'Question Answered' THEN GlobalSceneIndex END) AS QuestionAnsweredIndex,
|
|
MAX(BookID) AS BookID, MAX(ChapterID) AS ChapterID, MIN(SceneID) AS AnySceneID
|
|
FROM Events GROUP BY PlotThreadID, ThreadTitle
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, AnySceneID, N'PlotThread', PlotThreadID, N'Question Answered Before Raised', N'Warning',
|
|
CONCAT(N'Possible issue: "', ThreadTitle, N'" is answered before it is raised.'),
|
|
NULL
|
|
FROM ThreadRanges
|
|
WHERE QuestionAnsweredIndex IS NOT NULL AND QuestionRaisedIndex IS NOT NULL AND QuestionAnsweredIndex < QuestionRaisedIndex;
|
|
|
|
;WITH Clues AS
|
|
(
|
|
SELECT pt.PlotThreadID, pt.ThreadTitle, MIN(nso.SceneID) AS SceneID, MIN(nso.BookID) AS BookID, MIN(nso.ChapterID) AS ChapterID
|
|
FROM dbo.ThreadEvents te
|
|
INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
|
|
INNER JOIN dbo.PlotThreads pt ON pt.PlotThreadID = te.PlotThreadID AND pt.IsArchived = 0
|
|
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.ProjectID = @ProjectID AND pl.IsArchived = 0
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = te.SceneID
|
|
WHERE tet.TypeName = N'Clue Planted' AND (@BookID IS NULL OR nso.BookID = @BookID)
|
|
GROUP BY pt.PlotThreadID, pt.ThreadTitle
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'PlotThread', PlotThreadID, N'Clue Without Payoff', N'Info',
|
|
CONCAT(N'Possible issue: clue thread "', ThreadTitle, N'" has no payoff, answer, reveal, or resolution yet.'),
|
|
NULL
|
|
FROM Clues c
|
|
WHERE NOT EXISTS (
|
|
SELECT 1 FROM dbo.ThreadEvents te
|
|
INNER JOIN dbo.ThreadEventTypes tet ON tet.ThreadEventTypeID = te.EventTypeID
|
|
WHERE te.PlotThreadID = c.PlotThreadID AND tet.TypeName IN (N'Payoff', N'Question Answered', N'Revealed', N'Resolved')
|
|
);
|
|
|
|
;WITH LastThreadEvent AS
|
|
(
|
|
SELECT pt.PlotThreadID, pt.ThreadTitle, MAX(nso.GlobalSceneIndex) AS LastEventIndex, MAX(nso.BookID) AS BookID, MAX(nso.ChapterID) AS ChapterID, MAX(nso.SceneID) AS SceneID
|
|
FROM dbo.PlotThreads pt
|
|
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID AND ts.IsOpenStatus = 1
|
|
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.ProjectID = @ProjectID AND pl.IsArchived = 0
|
|
INNER JOIN dbo.ThreadEvents te ON te.PlotThreadID = pt.PlotThreadID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = te.SceneID
|
|
WHERE pt.IsArchived = 0 AND (@BookID IS NULL OR nso.BookID = @BookID)
|
|
GROUP BY pt.PlotThreadID, pt.ThreadTitle
|
|
),
|
|
MaxScope AS
|
|
(
|
|
SELECT MAX(GlobalSceneIndex) AS MaxSceneIndex FROM dbo.NarrativeSceneOrder WHERE ProjectID = @ProjectID AND (@BookID IS NULL OR BookID = @BookID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'PlotThread', PlotThreadID, N'Thread Dormant', N'Info',
|
|
CONCAT(N'Possible issue: "', ThreadTitle, N'" has no event for more than 8 scenes.'),
|
|
CONCAT(N'Scenes since last event: ', MaxSceneIndex - LastEventIndex, N'.')
|
|
FROM LastThreadEvent CROSS JOIN MaxScope
|
|
WHERE MaxSceneIndex - LastEventIndex > 8;
|
|
|
|
;WITH OpenMajor AS
|
|
(
|
|
SELECT COUNT(*) AS OpenCount
|
|
FROM dbo.PlotThreads pt
|
|
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID AND ts.IsOpenStatus = 1
|
|
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID AND pl.ProjectID = @ProjectID AND pl.IsArchived = 0
|
|
WHERE pt.IsArchived = 0 AND pt.Importance >= 8 AND (@BookID IS NULL OR pl.BookID IS NULL OR pl.BookID = @BookID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT @BookID, NULL, NULL, N'Project', @ProjectID, N'Too Many Open Major Threads', N'Info',
|
|
CONCAT(N'Possible issue: there are ', OpenCount, N' high-importance open threads.'),
|
|
N'This may be intentional, but it can make the active story load feel heavy.'
|
|
FROM OpenMajor WHERE OpenCount > 10;
|
|
|
|
;WITH AssetEventsOrdered AS
|
|
(
|
|
SELECT ae.StoryAssetID, sa.AssetName, ae.SceneID, aet.TypeName, nso.BookID, nso.ChapterID, nso.GlobalSceneIndex
|
|
FROM dbo.AssetEvents ae
|
|
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ae.StoryAssetID AND sa.ProjectID = @ProjectID AND sa.IsArchived = 0
|
|
INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = ae.SceneID
|
|
WHERE (@BookID IS NULL OR nso.BookID = @BookID)
|
|
),
|
|
AssetFirsts AS
|
|
(
|
|
SELECT StoryAssetID, AssetName,
|
|
MIN(CASE WHEN TypeName IN (N'First Mentioned', N'Mentioned', N'Found') THEN GlobalSceneIndex END) AS IntroducedIndex,
|
|
MIN(CASE WHEN TypeName IN (N'Used', N'Opened', N'Revealed', N'Destroyed', N'Given', N'Taken', N'Hidden', N'Lost', N'Stolen') THEN GlobalSceneIndex END) AS UsedIndex,
|
|
MIN(CASE WHEN TypeName IN (N'Used', N'Opened', N'Revealed', N'Destroyed', N'Given', N'Taken', N'Hidden', N'Lost', N'Stolen') THEN SceneID END) AS UsedSceneID,
|
|
MIN(BookID) AS BookID, MIN(ChapterID) AS ChapterID
|
|
FROM AssetEventsOrdered GROUP BY StoryAssetID, AssetName
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, UsedSceneID, N'StoryAsset', StoryAssetID, N'Asset Used Before Introduced', N'Warning',
|
|
CONCAT(N'Possible issue: "', AssetName, N'" appears used before it is introduced.'),
|
|
NULL
|
|
FROM AssetFirsts
|
|
WHERE UsedIndex IS NOT NULL AND (IntroducedIndex IS NULL OR UsedIndex < IntroducedIndex);
|
|
|
|
;WITH TargetEvents AS
|
|
(
|
|
SELECT ad.AssetDependencyID, ad.SourceAssetID, source.AssetName AS SourceAssetName, ad.TargetAssetID, target.AssetName AS TargetAssetName,
|
|
ad.SourceRequiredStateID, ad.TargetBlockedStateID, ae.SceneID, nso.BookID, nso.ChapterID, nso.GlobalSceneIndex
|
|
FROM dbo.AssetDependencies ad
|
|
INNER JOIN dbo.AssetDependencyTypes adt ON adt.AssetDependencyTypeID = ad.AssetDependencyTypeID AND adt.IsValidatingDependency = 1
|
|
INNER JOIN dbo.StoryAssets source ON source.StoryAssetID = ad.SourceAssetID
|
|
INNER JOIN dbo.StoryAssets target ON target.StoryAssetID = ad.TargetAssetID AND target.ProjectID = @ProjectID
|
|
INNER JOIN dbo.AssetEvents ae ON ae.StoryAssetID = ad.TargetAssetID AND ae.ToStateID = ad.TargetBlockedStateID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = ae.SceneID
|
|
WHERE ad.IsArchived = 0 AND ad.SourceRequiredStateID IS NOT NULL AND ad.TargetBlockedStateID IS NOT NULL AND (@BookID IS NULL OR nso.BookID = @BookID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'AssetDependency', AssetDependencyID, N'Asset State Dependency Missing', N'Warning',
|
|
CONCAT(N'Possible issue: "', TargetAssetName, N'" reaches a blocked state before "', SourceAssetName, N'" reaches its required state.'),
|
|
NULL
|
|
FROM TargetEvents te
|
|
WHERE NOT EXISTS (
|
|
SELECT 1 FROM dbo.AssetEvents sourceEvent
|
|
INNER JOIN dbo.NarrativeSceneOrder sourceScene ON sourceScene.SceneID = sourceEvent.SceneID
|
|
WHERE sourceEvent.StoryAssetID = te.SourceAssetID
|
|
AND sourceEvent.ToStateID = te.SourceRequiredStateID
|
|
AND sourceScene.GlobalSceneIndex <= te.GlobalSceneIndex
|
|
);
|
|
|
|
;WITH AssetPlaces AS
|
|
(
|
|
SELECT sal.StoryAssetID, sa.AssetName, sal.LocationID, sal.SceneID, nso.BookID, nso.ChapterID, nso.GlobalSceneIndex,
|
|
LAG(sal.LocationID) OVER (PARTITION BY sal.StoryAssetID ORDER BY nso.GlobalSceneIndex) AS PreviousLocationID
|
|
FROM dbo.SceneAssetLocations sal
|
|
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = sal.StoryAssetID AND sa.ProjectID = @ProjectID AND sa.IsArchived = 0
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = sal.SceneID
|
|
WHERE (@BookID IS NULL OR nso.BookID = @BookID)
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'StoryAsset', StoryAssetID, N'Asset Location Conflict', N'Warning',
|
|
CONCAT(N'Possible issue: "', AssetName, N'" appears in a new location without a movement-style event.'),
|
|
N'Add a Move, Given, Taken, Found, or Location Changed event if the location change needs explaining.'
|
|
FROM AssetPlaces ap
|
|
WHERE PreviousLocationID IS NOT NULL AND PreviousLocationID <> LocationID
|
|
AND NOT EXISTS (
|
|
SELECT 1 FROM dbo.AssetEvents ae
|
|
INNER JOIN dbo.AssetEventTypes aet ON aet.AssetEventTypeID = ae.AssetEventTypeID
|
|
WHERE ae.StoryAssetID = ap.StoryAssetID AND ae.SceneID = ap.SceneID AND aet.TypeName IN (N'Moved', N'Given', N'Taken', N'Found', N'Location Changed')
|
|
);
|
|
|
|
INSERT @Warnings
|
|
SELECT DISTINCT nso.BookID, nso.ChapterID, sc.SceneID, N'Character', sc.CharacterID, N'Character Knowledge Conflict', N'Info',
|
|
CONCAT(N'Possible issue: ', ch.CharacterName, N' has knowledge notes in this scene but no structured knowledge record.'),
|
|
N'This is only a low-severity prompt to add Character Knowledge if useful.'
|
|
FROM dbo.SceneCharacters sc
|
|
INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = sc.SceneID
|
|
WHERE ch.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@SceneID IS NULL OR sc.SceneID = @SceneID)
|
|
AND ISNULL(NULLIF(LTRIM(RTRIM(sc.KnowledgeNotes)), N''), N'') <> N''
|
|
AND NOT EXISTS (SELECT 1 FROM dbo.CharacterKnowledge ck WHERE ck.SceneID = sc.SceneID AND ck.CharacterID = sc.CharacterID);
|
|
|
|
INSERT @Warnings
|
|
SELECT targetOrder.BookID, targetOrder.ChapterID, sd.TargetSceneID, N'SceneDependency', sd.SceneDependencyID, N'Scene Dependency Broken', N'Warning',
|
|
N'Possible issue: a scene dependency says an earlier setup should occur before this scene, but the order is reversed.',
|
|
sd.Description
|
|
FROM dbo.SceneDependencies sd
|
|
INNER JOIN dbo.NarrativeSceneOrder sourceOrder ON sourceOrder.SceneID = sd.SourceSceneID
|
|
INNER JOIN dbo.NarrativeSceneOrder targetOrder ON targetOrder.SceneID = sd.TargetSceneID
|
|
WHERE sd.IsArchived = 0 AND sourceOrder.ProjectID = @ProjectID
|
|
AND (@BookID IS NULL OR targetOrder.BookID = @BookID) AND (@SceneID IS NULL OR targetOrder.SceneID = @SceneID)
|
|
AND sourceOrder.GlobalSceneIndex > targetOrder.GlobalSceneIndex;
|
|
|
|
;WITH CharacterSceneLocations AS
|
|
(
|
|
SELECT sc.SceneID, sc.CharacterID, COUNT(DISTINCT sc.LocationID) AS LocationCount, MAX(ch.CharacterName) AS CharacterName,
|
|
MAX(nso.BookID) AS BookID, MAX(nso.ChapterID) AS ChapterID
|
|
FROM dbo.SceneCharacters sc
|
|
INNER JOIN dbo.Characters ch ON ch.CharacterID = sc.CharacterID AND ch.ProjectID = @ProjectID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = sc.SceneID
|
|
LEFT JOIN dbo.PresenceTypes pt ON pt.PresenceTypeID = sc.PresenceTypeID
|
|
WHERE sc.LocationID IS NOT NULL AND (@BookID IS NULL OR nso.BookID = @BookID) AND (@SceneID IS NULL OR sc.SceneID = @SceneID)
|
|
AND ISNULL(pt.TypeName, N'') NOT IN (N'Nearby', N'Hidden', N'Overhearing', N'Off-screen')
|
|
GROUP BY sc.SceneID, sc.CharacterID
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, SceneID, N'Character', CharacterID, N'Character Location Conflict', N'Warning',
|
|
CONCAT(N'Possible issue: ', CharacterName, N' appears in more than one location in the same scene.'),
|
|
NULL
|
|
FROM CharacterSceneLocations WHERE LocationCount > 1;
|
|
|
|
;WITH MetricRows AS
|
|
(
|
|
SELECT smv.MetricTypeID, smt.MetricName, smv.SceneID, smv.Value, nso.BookID, nso.ChapterID, nso.GlobalSceneIndex,
|
|
ROW_NUMBER() OVER (PARTITION BY smv.MetricTypeID ORDER BY nso.GlobalSceneIndex) AS rn,
|
|
ROW_NUMBER() OVER (PARTITION BY smv.MetricTypeID, CASE WHEN smv.Value >= 8 THEN 1 ELSE 0 END ORDER BY nso.GlobalSceneIndex) AS grpRn
|
|
FROM dbo.SceneMetricValues smv
|
|
INNER JOIN dbo.SceneMetricTypes smt ON smt.MetricTypeID = smv.MetricTypeID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = smv.SceneID
|
|
WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID)
|
|
),
|
|
HighGroups AS
|
|
(
|
|
SELECT MetricTypeID, MetricName, rn - grpRn AS grp, COUNT(*) AS SceneCount, MIN(SceneID) AS FirstSceneID, MAX(SceneID) AS LastSceneID,
|
|
MAX(BookID) AS BookID, MAX(ChapterID) AS ChapterID
|
|
FROM MetricRows WHERE Value >= 8 GROUP BY MetricTypeID, MetricName, rn - grpRn HAVING COUNT(*) > 6
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, LastSceneID, N'Metric', MetricTypeID, N'Sustained High Intensity', N'Info',
|
|
CONCAT(N'Possible reader fatigue: ', MetricName, N' remains high for ', SceneCount, N' consecutive scenes.'),
|
|
CONCAT(N'Scene range: ', FirstSceneID, N' to ', LastSceneID, N'.')
|
|
FROM HighGroups;
|
|
|
|
;WITH MetricRows AS
|
|
(
|
|
SELECT smv.MetricTypeID, smt.MetricName, smv.SceneID, smv.Value, nso.BookID, nso.ChapterID, nso.GlobalSceneIndex,
|
|
ROW_NUMBER() OVER (PARTITION BY smv.MetricTypeID ORDER BY nso.GlobalSceneIndex) AS rn,
|
|
ROW_NUMBER() OVER (PARTITION BY smv.MetricTypeID, CASE WHEN smv.Value <= 2 THEN 1 ELSE 0 END ORDER BY nso.GlobalSceneIndex) AS grpRn
|
|
FROM dbo.SceneMetricValues smv
|
|
INNER JOIN dbo.SceneMetricTypes smt ON smt.MetricTypeID = smv.MetricTypeID
|
|
INNER JOIN dbo.NarrativeSceneOrder nso ON nso.SceneID = smv.SceneID
|
|
WHERE nso.ProjectID = @ProjectID AND (@BookID IS NULL OR nso.BookID = @BookID)
|
|
AND smt.MetricName IN (N'Overall Intensity', N'Tension', N'Emotional Weight', N'Action')
|
|
),
|
|
LowGroups AS
|
|
(
|
|
SELECT MetricTypeID, MetricName, rn - grpRn AS grp, COUNT(*) AS SceneCount, MIN(SceneID) AS FirstSceneID, MAX(SceneID) AS LastSceneID,
|
|
MAX(BookID) AS BookID, MAX(ChapterID) AS ChapterID
|
|
FROM MetricRows WHERE Value <= 2 GROUP BY MetricTypeID, MetricName, rn - grpRn HAVING COUNT(*) > 8
|
|
)
|
|
INSERT @Warnings
|
|
SELECT BookID, ChapterID, LastSceneID, N'Metric', MetricTypeID, N'Sustained Low Intensity', N'Info',
|
|
CONCAT(N'Possible issue: ', MetricName, N' remains low for ', SceneCount, N' consecutive scenes.'),
|
|
CONCAT(N'Scene range: ', FirstSceneID, N' to ', LastSceneID, N'.')
|
|
FROM LowGroups;
|
|
|
|
INSERT dbo.ContinuityWarnings (ValidationRunID, ProjectID, BookID, ChapterID, SceneID, EntityType, EntityID, WarningTypeID, WarningSeverityID, Message, Details)
|
|
SELECT @ValidationRunID, @ProjectID, w.BookID, w.ChapterID, w.SceneID, w.EntityType, w.EntityID, wt.WarningTypeID, ws.WarningSeverityID, w.Message, w.Details
|
|
FROM @Warnings w
|
|
INNER JOIN dbo.WarningTypes wt ON wt.TypeName = w.WarningTypeName
|
|
INNER JOIN dbo.WarningSeverities ws ON ws.SeverityName = w.SeverityName
|
|
WHERE NOT EXISTS
|
|
(
|
|
SELECT 1 FROM dbo.ContinuityWarnings existing
|
|
WHERE existing.ProjectID = @ProjectID
|
|
AND ISNULL(existing.BookID, 0) = ISNULL(w.BookID, 0)
|
|
AND ISNULL(existing.ChapterID, 0) = ISNULL(w.ChapterID, 0)
|
|
AND ISNULL(existing.SceneID, 0) = ISNULL(w.SceneID, 0)
|
|
AND existing.EntityType = w.EntityType
|
|
AND ISNULL(existing.EntityID, 0) = ISNULL(w.EntityID, 0)
|
|
AND existing.WarningTypeID = wt.WarningTypeID
|
|
AND (existing.IsDismissed = 1 OR existing.IsIntentional = 1)
|
|
);
|
|
|
|
EXEC dbo.ValidationRun_Complete @ValidationRunID;
|
|
|
|
SELECT @ValidationRunID AS ValidationRunID,
|
|
SUM(CASE WHEN ws.SeverityName = N'Error' THEN 1 ELSE 0 END) AS ErrorCount,
|
|
SUM(CASE WHEN ws.SeverityName = N'Warning' THEN 1 ELSE 0 END) AS WarningCount,
|
|
SUM(CASE WHEN ws.SeverityName = N'Info' THEN 1 ELSE 0 END) AS InfoCount,
|
|
COUNT(*) AS TotalCount
|
|
FROM dbo.ContinuityWarnings cw
|
|
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
|
WHERE cw.ValidationRunID = @ValidationRunID;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityValidation_ValidateBook
|
|
@BookID int,
|
|
@TriggeredBy nvarchar(100) = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
DECLARE @ProjectID int = (SELECT ProjectID FROM dbo.Books WHERE BookID = @BookID);
|
|
EXEC dbo.ContinuityValidation_ValidateProject @ProjectID = @ProjectID, @BookID = @BookID, @TriggeredBy = @TriggeredBy;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityValidation_ValidateScene
|
|
@SceneID int,
|
|
@TriggeredBy nvarchar(100) = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
DECLARE @ProjectID int, @BookID int;
|
|
SELECT @ProjectID = b.ProjectID, @BookID = b.BookID
|
|
FROM dbo.Scenes s
|
|
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
|
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
|
WHERE s.SceneID = @SceneID;
|
|
EXEC dbo.ContinuityValidation_ValidateProject @ProjectID = @ProjectID, @BookID = @BookID, @SceneID = @SceneID, @TriggeredBy = @TriggeredBy;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_List
|
|
@ProjectID int,
|
|
@BookID int = NULL,
|
|
@SceneID int = NULL,
|
|
@EntityType nvarchar(80) = NULL,
|
|
@EntityID int = NULL,
|
|
@WarningTypeID int = NULL,
|
|
@WarningSeverityID int = NULL,
|
|
@IncludeDismissed bit = 0,
|
|
@IncludeIntentional bit = 0
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
SELECT cw.ContinuityWarningID, cw.ValidationRunID, cw.ProjectID, cw.BookID, b.BookTitle, cw.ChapterID, c.ChapterNumber, c.ChapterTitle,
|
|
cw.SceneID, s.SceneNumber, s.SceneTitle, cw.EntityType, cw.EntityID, cw.WarningTypeID, wt.TypeName AS WarningTypeName,
|
|
cw.WarningSeverityID, ws.SeverityName, cw.Message, cw.Details, cw.IsDismissed, cw.IsIntentional, cw.CreatedDate, cw.LastDetectedDate
|
|
FROM dbo.ContinuityWarnings cw
|
|
INNER JOIN dbo.WarningTypes wt ON wt.WarningTypeID = cw.WarningTypeID
|
|
INNER JOIN dbo.WarningSeverities ws ON ws.WarningSeverityID = cw.WarningSeverityID
|
|
LEFT JOIN dbo.Books b ON b.BookID = cw.BookID
|
|
LEFT JOIN dbo.Chapters c ON c.ChapterID = cw.ChapterID
|
|
LEFT JOIN dbo.Scenes s ON s.SceneID = cw.SceneID
|
|
WHERE cw.ProjectID = @ProjectID
|
|
AND (@BookID IS NULL OR cw.BookID = @BookID)
|
|
AND (@SceneID IS NULL OR cw.SceneID = @SceneID)
|
|
AND (@EntityType IS NULL OR cw.EntityType = @EntityType)
|
|
AND (@EntityID IS NULL OR cw.EntityID = @EntityID)
|
|
AND (@WarningTypeID IS NULL OR cw.WarningTypeID = @WarningTypeID)
|
|
AND (@WarningSeverityID IS NULL OR cw.WarningSeverityID = @WarningSeverityID)
|
|
AND (@IncludeDismissed = 1 OR cw.IsDismissed = 0)
|
|
AND (@IncludeIntentional = 1 OR cw.IsIntentional = 0)
|
|
ORDER BY ws.SortOrder DESC, cw.LastDetectedDate DESC, cw.ContinuityWarningID DESC;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_ListByProject
|
|
@ProjectID int,
|
|
@IncludeDismissed bit = 0,
|
|
@IncludeIntentional bit = 0
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
EXEC dbo.ContinuityWarning_List @ProjectID = @ProjectID, @IncludeDismissed = @IncludeDismissed, @IncludeIntentional = @IncludeIntentional;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_ListByBook
|
|
@ProjectID int,
|
|
@BookID int,
|
|
@IncludeDismissed bit = 0,
|
|
@IncludeIntentional bit = 0
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
EXEC dbo.ContinuityWarning_List @ProjectID = @ProjectID, @BookID = @BookID, @IncludeDismissed = @IncludeDismissed, @IncludeIntentional = @IncludeIntentional;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_ListByScene
|
|
@SceneID int,
|
|
@IncludeDismissed bit = 0,
|
|
@IncludeIntentional bit = 0
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
DECLARE @ProjectID int = (SELECT b.ProjectID FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE s.SceneID = @SceneID);
|
|
EXEC dbo.ContinuityWarning_List @ProjectID = @ProjectID, @SceneID = @SceneID, @IncludeDismissed = @IncludeDismissed, @IncludeIntentional = @IncludeIntentional;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_ListByEntity
|
|
@ProjectID int,
|
|
@EntityType nvarchar(80),
|
|
@EntityID int,
|
|
@IncludeDismissed bit = 0,
|
|
@IncludeIntentional bit = 0
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
EXEC dbo.ContinuityWarning_List @ProjectID = @ProjectID, @EntityType = @EntityType, @EntityID = @EntityID, @IncludeDismissed = @IncludeDismissed, @IncludeIntentional = @IncludeIntentional;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_SceneCounts
|
|
@ProjectID int,
|
|
@BookID int = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
SELECT cw.SceneID, COUNT(*) AS WarningCount
|
|
FROM dbo.ContinuityWarnings cw
|
|
WHERE cw.ProjectID = @ProjectID
|
|
AND cw.SceneID IS NOT NULL
|
|
AND cw.IsDismissed = 0
|
|
AND cw.IsIntentional = 0
|
|
AND (@BookID IS NULL OR cw.BookID = @BookID)
|
|
GROUP BY cw.SceneID;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_SetState
|
|
@ContinuityWarningID int,
|
|
@IsDismissed bit = NULL,
|
|
@IsIntentional bit = NULL
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
UPDATE dbo.ContinuityWarnings
|
|
SET IsDismissed = COALESCE(@IsDismissed, IsDismissed),
|
|
IsIntentional = COALESCE(@IsIntentional, IsIntentional)
|
|
WHERE ContinuityWarningID = @ContinuityWarningID;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_Dismiss
|
|
@ContinuityWarningID int
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
EXEC dbo.ContinuityWarning_SetState @ContinuityWarningID = @ContinuityWarningID, @IsDismissed = 1;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_MarkIntentional
|
|
@ContinuityWarningID int
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
EXEC dbo.ContinuityWarning_SetState @ContinuityWarningID = @ContinuityWarningID, @IsIntentional = 1;
|
|
END;
|
|
GO
|
|
|
|
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_Restore
|
|
@ContinuityWarningID int
|
|
AS
|
|
BEGIN
|
|
SET NOCOUNT ON;
|
|
UPDATE dbo.ContinuityWarnings
|
|
SET IsDismissed = 0, IsIntentional = 0
|
|
WHERE ContinuityWarningID = @ContinuityWarningID;
|
|
END;
|
|
GO
|