-- Reuse canonical relationships and events. Existing author data is not rewritten. SET XACT_ABORT ON; GO IF COL_LENGTH('dbo.RelationshipEvents','SourceReviewKey') IS NULL ALTER TABLE dbo.RelationshipEvents ADD SourceReviewKey nvarchar(450) NULL; GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE object_id=OBJECT_ID('dbo.RelationshipEvents') AND name='UX_RelationshipEvents_ReviewSource') CREATE UNIQUE INDEX UX_RelationshipEvents_ReviewSource ON dbo.RelationshipEvents(SceneID,SourceReviewKey) WHERE SourceReviewKey IS NOT NULL; GO CREATE OR ALTER PROCEDURE dbo.StoryIntelligenceRelationship_AcceptDevelopment @ProjectID int, @BookID int, @SceneID int, @SourceReviewKey nvarchar(450), @CharacterAID int, @CharacterBID int, @RelationshipTypeID int, @RelationshipStateID int, @IsPermanent bit, @IsReciprocal bit, @IsKnownToOtherCharacter bit, @Intensity int=NULL, @Description nvarchar(max) AS BEGIN SET NOCOUNT ON; SET XACT_ABORT ON; IF NOT EXISTS(SELECT 1 FROM dbo.Scenes s JOIN dbo.Chapters c ON c.ChapterID=s.ChapterID JOIN dbo.Books b ON b.BookID=c.BookID WHERE s.SceneID=@SceneID AND b.BookID=@BookID AND b.ProjectID=@ProjectID AND s.IsArchived=0 AND c.IsArchived=0 AND b.IsArchived=0) THROW 53100,'Invalid scene scope.',1; IF @CharacterAID=@CharacterBID OR (SELECT COUNT(*) FROM dbo.Characters WHERE CharacterID IN (@CharacterAID,@CharacterBID) AND ProjectID=@ProjectID AND IsArchived=0)<>2 THROW 53101,'Invalid relationship participants.',1; IF @Intensity NOT BETWEEN 1 AND 10 OR NULLIF(LTRIM(RTRIM(@Description)),N'') IS NULL OR NULLIF(@SourceReviewKey,N'') IS NULL THROW 53102,'Invalid relationship development.',1; BEGIN TRANSACTION; DECLARE @LockResult int; EXEC @LockResult=sys.sp_getapplock @Resource=N'StoryIntelligenceRelationshipAcceptance',@LockMode=N'Exclusive',@LockOwner=N'Transaction',@LockTimeout=15000; IF @LockResult<0 THROW 53103,'Relationship acceptance is busy. Retry.',1; DECLARE @RelationshipID int; SELECT @RelationshipID=CharacterRelationshipID FROM dbo.RelationshipEvents WHERE SceneID=@SceneID AND SourceReviewKey=@SourceReviewKey; IF @RelationshipID IS NULL BEGIN -- Do not flatten factual ties into changing feelings, or reverse a directional track. SELECT TOP(1) @RelationshipID=CharacterRelationshipID FROM dbo.CharacterRelationships WHERE ProjectID=@ProjectID AND IsArchived=0 AND EndSceneID IS NULL AND RelationshipTypeID=@RelationshipTypeID AND IsPermanent=@IsPermanent AND IsReciprocal=@IsReciprocal AND ((CharacterAID=@CharacterAID AND CharacterBID=@CharacterBID) OR (@IsReciprocal=1 AND CharacterAID=@CharacterBID AND CharacterBID=@CharacterAID)) ORDER BY CharacterRelationshipID; IF @RelationshipID IS NULL BEGIN INSERT dbo.CharacterRelationships(ProjectID,CharacterAID,CharacterBID,RelationshipTypeID,IsPermanent,StartSceneID, IsKnownToReader,Notes,IsInitialRelationship,InitialBookID,ReaderInitiallyKnows,IsReciprocal) VALUES(@ProjectID,@CharacterAID,@CharacterBID,@RelationshipTypeID,@IsPermanent,@SceneID,1, N'Imported from Story Intelligence scene relationship review.',0,@BookID,0,@IsReciprocal); SET @RelationshipID=CONVERT(int,SCOPE_IDENTITY()); END; INSERT dbo.RelationshipEvents(CharacterRelationshipID,SceneID,RelationshipStateID,Intensity,IsKnownToOtherCharacter,Description,SourceReviewKey) VALUES(@RelationshipID,@SceneID,@RelationshipStateID,@Intensity,@IsKnownToOtherCharacter,@Description,@SourceReviewKey); -- A later-reviewed earlier scene must move the start of the track back in story order. UPDATE r SET StartSceneID=@SceneID FROM dbo.CharacterRelationships r JOIN dbo.Scenes oldScene ON oldScene.SceneID=r.StartSceneID JOIN dbo.Chapters oldChapter ON oldChapter.ChapterID=oldScene.ChapterID JOIN dbo.Scenes newScene ON newScene.SceneID=@SceneID JOIN dbo.Chapters newChapter ON newChapter.ChapterID=newScene.ChapterID WHERE r.CharacterRelationshipID=@RelationshipID AND r.IsInitialRelationship=0 AND oldChapter.BookID=@BookID AND (newChapter.ChapterNumberN'Pending' AND (c.StableKey=JSON_VALUE(p.value,'$.key') OR (ISNULL(TRY_CONVERT(int,JSON_VALUE(c.CandidatePayloadJson,'$.reviewRulesVersion')),0)<3 AND ((JSON_VALUE(c.CandidatePayloadJson,'$.characterAName')=JSON_VALUE(p.value,'$.characterAName') AND JSON_VALUE(c.CandidatePayloadJson,'$.characterBName')=JSON_VALUE(p.value,'$.characterBName')) OR (JSON_VALUE(c.CandidatePayloadJson,'$.characterAName')=JSON_VALUE(p.value,'$.characterBName') AND JSON_VALUE(c.CandidatePayloadJson,'$.characterBName')=JSON_VALUE(p.value,'$.characterAName')))))); DELETE FROM dbo.StoryIntelligenceReviewCandidates WHERE ReviewCandidateGenerationID=@GenerationID AND DecisionStatus=N'Pending'; INSERT dbo.StoryIntelligenceReviewCandidates(ReviewCandidateGenerationID,ProjectID,BookID,Module,StableKey,CandidateType,DisplayName,SortOrder,ExistingCanonicalID,CandidatePayloadJson) SELECT @GenerationID,g.ProjectID,@BookID,N'Relationships',JSON_VALUE(p.value,'$.key'),JSON_VALUE(p.value,'$.relationshipType'), CONCAT(JSON_VALUE(p.value,'$.characterAName'),N' / ',JSON_VALUE(p.value,'$.characterBName')),CONVERT(int,p.ordinal)+1, TRY_CONVERT(int,JSON_VALUE(p.value,'$.existingRelationshipID')),p.value FROM #Incoming p JOIN dbo.StoryIntelligenceReviewCandidateGenerations g ON g.ReviewCandidateGenerationID=@GenerationID; UPDATE g SET CandidateCount=(SELECT COUNT(*) FROM dbo.StoryIntelligenceReviewCandidates WHERE ReviewCandidateGenerationID=@GenerationID), PendingCount=(SELECT COUNT(*) FROM dbo.StoryIntelligenceReviewCandidates WHERE ReviewCandidateGenerationID=@GenerationID AND DecisionStatus=N'Pending'),UpdatedUtc=SYSUTCDATETIME() FROM dbo.StoryIntelligenceReviewCandidateGenerations g WHERE ReviewCandidateGenerationID=@GenerationID; COMMIT; END; GO