SET ANSI_NULLS ON; GO SET QUOTED_IDENTIFIER ON; GO IF OBJECT_ID(N'dbo.SceneMetricTypes', N'U') IS NULL BEGIN CREATE TABLE dbo.SceneMetricTypes ( MetricTypeID int IDENTITY(1,1) NOT NULL CONSTRAINT PK_SceneMetricTypes PRIMARY KEY, ProjectID int NULL, MetricName nvarchar(100) NOT NULL, Description nvarchar(500) NULL, MinValue int NOT NULL CONSTRAINT DF_SceneMetricTypes_MinValue DEFAULT 1, MaxValue int NOT NULL CONSTRAINT DF_SceneMetricTypes_MaxValue DEFAULT 10, DefaultValue int NOT NULL CONSTRAINT DF_SceneMetricTypes_DefaultValue DEFAULT 5, SortOrder int NOT NULL, IsActive bit NOT NULL CONSTRAINT DF_SceneMetricTypes_IsActive DEFAULT 1, CONSTRAINT FK_SceneMetricTypes_Projects FOREIGN KEY (ProjectID) REFERENCES dbo.Projects(ProjectID), CONSTRAINT CK_SceneMetricTypes_Range CHECK (MinValue < MaxValue AND DefaultValue BETWEEN MinValue AND MaxValue) ); END; GO IF OBJECT_ID(N'dbo.SceneMetricValues', N'U') IS NULL BEGIN CREATE TABLE dbo.SceneMetricValues ( SceneID int NOT NULL, MetricTypeID int NOT NULL, Value int NOT NULL, Notes nvarchar(500) NULL, CONSTRAINT PK_SceneMetricValues PRIMARY KEY (SceneID, MetricTypeID), CONSTRAINT FK_SceneMetricValues_Scenes FOREIGN KEY (SceneID) REFERENCES dbo.Scenes(SceneID), CONSTRAINT FK_SceneMetricValues_SceneMetricTypes FOREIGN KEY (MetricTypeID) REFERENCES dbo.SceneMetricTypes(MetricTypeID), CONSTRAINT CK_SceneMetricValues_Value CHECK (Value BETWEEN 1 AND 10) ); END; GO IF NOT EXISTS (SELECT 1 FROM sys.indexes WHERE name = N'IX_SceneMetricTypes_ProjectID_SortOrder' AND object_id = OBJECT_ID(N'dbo.SceneMetricTypes')) CREATE INDEX IX_SceneMetricTypes_ProjectID_SortOrder ON dbo.SceneMetricTypes(ProjectID, IsActive, SortOrder); GO MERGE dbo.SceneMetricTypes AS target USING (VALUES (N'Overall Intensity', N'General dramatic intensity for the scene.', 1, 10, 5, 10), (N'Tension', N'Pressure, suspense, uncertainty, or conflict.', 1, 10, 5, 20), (N'Emotional Weight', N'Emotional load carried by the scene.', 1, 10, 5, 30), (N'Action', N'Physical action, movement, or event energy.', 1, 10, 3, 40), (N'Darkness', N'Darkness, bleakness, fear, or threat.', 1, 10, 3, 50), (N'Romance', N'Romantic focus or intimacy.', 1, 10, 1, 60), (N'Sexual Charge', N'Sexual tension or sensual charge.', 1, 10, 1, 70), (N'Violence', N'Violent action, injury, or threat.', 1, 10, 1, 80), (N'Hope / Lightness', N'Hope, relief, warmth, or tonal lightness.', 1, 10, 4, 90), (N'Comedy', N'Humour or comic relief.', 1, 10, 1, 100), (N'Mystery', N'Questions, clue energy, and mystery pressure.', 1, 10, 5, 110) ) AS source (MetricName, Description, MinValue, MaxValue, DefaultValue, SortOrder) ON target.ProjectID IS NULL AND target.MetricName = source.MetricName WHEN NOT MATCHED THEN INSERT (ProjectID, MetricName, Description, MinValue, MaxValue, DefaultValue, SortOrder, IsActive) VALUES (NULL, source.MetricName, source.Description, source.MinValue, source.MaxValue, source.DefaultValue, source.SortOrder, 1); GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_List @ProjectID int = NULL AS BEGIN SET NOCOUNT ON; SELECT MetricTypeID, ProjectID, MetricName, Description, MinValue, MaxValue, DefaultValue, SortOrder, IsActive FROM dbo.SceneMetricTypes WHERE IsActive = 1 AND (ProjectID IS NULL OR ProjectID = @ProjectID) ORDER BY SortOrder, MetricName; END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetric_EnsureDefaults @SceneID int AS BEGIN SET NOCOUNT ON; DECLARE @ProjectID int; SELECT @ProjectID = 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; INSERT dbo.SceneMetricValues (SceneID, MetricTypeID, Value, Notes) SELECT @SceneID, mt.MetricTypeID, mt.DefaultValue, NULL FROM dbo.SceneMetricTypes mt WHERE mt.IsActive = 1 AND (mt.ProjectID IS NULL OR mt.ProjectID = @ProjectID) AND NOT EXISTS ( SELECT 1 FROM dbo.SceneMetricValues smv WHERE smv.SceneID = @SceneID AND smv.MetricTypeID = mt.MetricTypeID ); END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetric_ListByScene @SceneID int AS BEGIN SET NOCOUNT ON; EXEC dbo.SceneMetric_EnsureDefaults @SceneID = @SceneID; SELECT mt.MetricTypeID, mt.ProjectID, mt.MetricName, mt.Description, mt.MinValue, mt.MaxValue, mt.DefaultValue, mt.SortOrder, mt.IsActive, smv.SceneID, smv.Value, smv.Notes FROM dbo.SceneMetricTypes mt INNER JOIN dbo.SceneMetricValues smv ON smv.MetricTypeID = mt.MetricTypeID AND smv.SceneID = @SceneID WHERE mt.IsActive = 1 ORDER BY mt.SortOrder, mt.MetricName; END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetric_SaveValue @SceneID int, @MetricTypeID int, @Value int, @Notes nvarchar(500) = NULL AS BEGIN SET NOCOUNT ON; DECLARE @MinValue int; DECLARE @MaxValue int; SELECT @MinValue = MinValue, @MaxValue = MaxValue FROM dbo.SceneMetricTypes WHERE MetricTypeID = @MetricTypeID AND IsActive = 1; IF @MinValue IS NULL THROW 50001, 'Metric type does not exist.', 1; IF @Value < @MinValue OR @Value > @MaxValue THROW 50002, 'Metric value is outside the configured range.', 1; MERGE dbo.SceneMetricValues AS target USING (SELECT @SceneID AS SceneID, @MetricTypeID AS MetricTypeID) AS source ON target.SceneID = source.SceneID AND target.MetricTypeID = source.MetricTypeID WHEN MATCHED THEN UPDATE SET Value = @Value, Notes = @Notes WHEN NOT MATCHED THEN INSERT (SceneID, MetricTypeID, Value, Notes) VALUES (@SceneID, @MetricTypeID, @Value, @Notes); END; GO CREATE OR ALTER PROCEDURE dbo.Scene_Get @SceneID int AS BEGIN SET NOCOUNT ON; EXEC dbo.SceneMetric_EnsureDefaults @SceneID = @SceneID; SELECT s.SceneID, s.ChapterID, s.SceneNumber, s.SceneTitle, s.Summary, s.POVCharacterID, s.PrimaryLocationID, s.SortOrder, s.TimeModeID, tm.TimeModeName, s.StartDateTime, s.EndDateTime, s.DurationAmount, s.DurationUnitID, du.DurationUnitName, s.RelativeTimeText, s.TimeConfidenceID, tc.TimeConfidenceName, s.ScenePurposeNotes, s.SceneOutcomeNotes, s.RevisionStatusID, rs.StatusName AS RevisionStatusName, s.CreatedDate, s.UpdatedDate, s.IsArchived FROM dbo.Scenes s INNER JOIN dbo.TimeModes tm ON tm.TimeModeID = s.TimeModeID LEFT JOIN dbo.DurationUnits du ON du.DurationUnitID = s.DurationUnitID INNER JOIN dbo.TimeConfidences tc ON tc.TimeConfidenceID = s.TimeConfidenceID INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID WHERE s.SceneID = @SceneID AND s.IsArchived = 0; SELECT sp.ScenePurposeTypeID FROM dbo.ScenePurposes sp WHERE sp.SceneID = @SceneID; SELECT mt.MetricTypeID, mt.ProjectID, mt.MetricName, mt.Description, mt.MinValue, mt.MaxValue, mt.DefaultValue, mt.SortOrder, mt.IsActive, smv.SceneID, smv.Value, smv.Notes FROM dbo.SceneMetricTypes mt INNER JOIN dbo.SceneMetricValues smv ON smv.MetricTypeID = mt.MetricTypeID AND smv.SceneID = @SceneID WHERE mt.IsActive = 1 ORDER BY mt.SortOrder, mt.MetricName; END; GO CREATE OR ALTER PROCEDURE dbo.Timeline_GetByProject @ProjectID int, @BookID int = NULL AS BEGIN SET NOCOUNT ON; SELECT ProjectID, ProjectName, Description, CreatedDate, UpdatedDate, IsArchived FROM dbo.Projects WHERE ProjectID = @ProjectID AND IsArchived = 0; SELECT BookID, ProjectID, BookTitle, BookNumber, Description, SortOrder, CreatedDate, UpdatedDate, IsArchived FROM dbo.Books WHERE ProjectID = @ProjectID AND IsArchived = 0 AND (@BookID IS NULL OR BookID = @BookID) ORDER BY SortOrder, BookNumber; SELECT c.ChapterID, c.BookID, c.ChapterNumber, c.ChapterTitle, c.POVCharacterID, c.Summary, c.SortOrder, c.RevisionStatusID, rs.StatusName AS RevisionStatusName, c.CreatedDate, c.UpdatedDate, c.IsArchived FROM dbo.Chapters c INNER JOIN dbo.Books b ON b.BookID = c.BookID INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = c.RevisionStatusID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 AND c.IsArchived = 0 AND (@BookID IS NULL OR b.BookID = @BookID) ORDER BY b.SortOrder, c.SortOrder, c.ChapterNumber; SELECT s.SceneID, s.ChapterID, s.SceneNumber, s.SceneTitle, s.Summary, s.POVCharacterID, s.PrimaryLocationID, s.SortOrder, s.TimeModeID, tm.TimeModeName, s.StartDateTime, s.EndDateTime, s.DurationAmount, s.DurationUnitID, du.DurationUnitName, s.RelativeTimeText, s.TimeConfidenceID, tc.TimeConfidenceName, s.ScenePurposeNotes, s.SceneOutcomeNotes, s.RevisionStatusID, rs.StatusName AS RevisionStatusName, s.CreatedDate, s.UpdatedDate, s.IsArchived FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID INNER JOIN dbo.TimeModes tm ON tm.TimeModeID = s.TimeModeID LEFT JOIN dbo.DurationUnits du ON du.DurationUnitID = s.DurationUnitID INNER JOIN dbo.TimeConfidences tc ON tc.TimeConfidenceID = s.TimeConfidenceID INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 AND c.IsArchived = 0 AND s.IsArchived = 0 AND (@BookID IS NULL OR b.BookID = @BookID) ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, s.SceneNumber; SELECT sp.SceneID, spt.ScenePurposeTypeID, spt.PurposeName, spt.SortOrder FROM dbo.ScenePurposes sp INNER JOIN dbo.ScenePurposeTypes spt ON spt.ScenePurposeTypeID = sp.ScenePurposeTypeID INNER JOIN dbo.Scenes s ON s.SceneID = sp.SceneID INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 AND c.IsArchived = 0 AND s.IsArchived = 0 AND (@BookID IS NULL OR b.BookID = @BookID) ORDER BY sp.SceneID, spt.SortOrder; SELECT MetricTypeID, ProjectID, MetricName, Description, MinValue, MaxValue, DefaultValue, SortOrder, IsActive FROM dbo.SceneMetricTypes WHERE IsActive = 1 AND (ProjectID IS NULL OR ProjectID = @ProjectID) ORDER BY SortOrder, MetricName; SELECT s.SceneID, mt.MetricTypeID, mt.MetricName, COALESCE(smv.Value, mt.DefaultValue) AS Value, smv.Notes FROM dbo.Scenes s INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID INNER JOIN dbo.Books b ON b.BookID = c.BookID CROSS JOIN dbo.SceneMetricTypes mt LEFT JOIN dbo.SceneMetricValues smv ON smv.SceneID = s.SceneID AND smv.MetricTypeID = mt.MetricTypeID WHERE b.ProjectID = @ProjectID AND b.IsArchived = 0 AND c.IsArchived = 0 AND s.IsArchived = 0 AND mt.IsActive = 1 AND (mt.ProjectID IS NULL OR mt.ProjectID = @ProjectID) AND (@BookID IS NULL OR b.BookID = @BookID) ORDER BY b.SortOrder, c.SortOrder, s.SortOrder, mt.SortOrder; END; GO