SET ANSI_NULLS ON; GO SET QUOTED_IDENTIFIER ON; GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_ProjectHasMetrics @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT CAST(CASE WHEN EXISTS ( SELECT 1 FROM dbo.ProjectTimelineMetricSettings ptms INNER JOIN dbo.SceneMetricTypes mt ON mt.MetricTypeID = ptms.MetricTypeID WHERE ptms.ProjectID = @ProjectID AND mt.IsActive = 1 ) THEN 1 ELSE 0 END AS bit); END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_AddMissingDefaultsForProject @ProjectID int AS BEGIN SET NOCOUNT ON; IF NOT EXISTS (SELECT 1 FROM dbo.ProjectTimelineSettings WHERE ProjectID = @ProjectID) BEGIN INSERT dbo.ProjectTimelineSettings (ProjectID, ShowSceneCards, ShowMetricShape, ShowPlotLines, ShowStoryAssets, ShowCharacterAppearances, ShowWarnings) VALUES (@ProjectID, 1, 1, 1, 1, 1, 1); END; INSERT dbo.ProjectTimelineMetricSettings (ProjectID, MetricTypeID, SortOrder) SELECT @ProjectID, mt.MetricTypeID, mt.SortOrder FROM dbo.SceneMetricTypes mt WHERE mt.ProjectID IS NULL AND mt.IsActive = 1 AND mt.MetricName IN ( N'Overall Intensity', N'Tension', N'Emotional Weight', N'Action', N'Darkness', N'Romance', N'Sexual Charge', N'Violence', N'Hope / Lightness', N'Comedy', N'Mystery' ) AND NOT EXISTS ( SELECT 1 FROM dbo.ProjectTimelineMetricSettings ptms INNER JOIN dbo.SceneMetricTypes existing ON existing.MetricTypeID = ptms.MetricTypeID WHERE ptms.ProjectID = @ProjectID AND LOWER(existing.MetricName) = LOWER(mt.MetricName) ); END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_ListForProjectManagement @ProjectID int AS BEGIN SET NOCOUNT ON; ;WITH CandidateMetrics AS ( SELECT mt.* FROM dbo.SceneMetricTypes mt WHERE mt.ProjectID = @ProjectID UNION SELECT mt.* FROM dbo.SceneMetricTypes mt WHERE mt.ProjectID IS NULL AND mt.MetricName IN ( N'Overall Intensity', N'Tension', N'Emotional Weight', N'Action', N'Darkness', N'Romance', N'Sexual Charge', N'Violence', N'Hope / Lightness', N'Comedy', N'Mystery' ) UNION SELECT mt.* FROM dbo.SceneMetricTypes mt INNER JOIN dbo.ProjectTimelineMetricSettings ptms ON ptms.MetricTypeID = mt.MetricTypeID WHERE ptms.ProjectID = @ProjectID ) SELECT cm.MetricTypeID, cm.ProjectID, cm.MetricName, cm.Description, cm.MinValue, cm.MaxValue, cm.DefaultValue, COALESCE(ptms.SortOrder, cm.SortOrder) AS SortOrder, CAST(CASE WHEN cm.IsActive = 1 AND ptms.MetricTypeID IS NOT NULL THEN 1 ELSE 0 END AS bit) AS IsActive, CAST(CASE WHEN cm.IsActive = 1 AND ptms.MetricTypeID IS NOT NULL THEN 1 ELSE 0 END AS bit) AS IsEnabledForProject, COUNT(smv.SceneID) AS ValueCount FROM CandidateMetrics cm LEFT JOIN dbo.ProjectTimelineMetricSettings ptms ON ptms.ProjectID = @ProjectID AND ptms.MetricTypeID = cm.MetricTypeID LEFT JOIN dbo.SceneMetricValues smv ON smv.MetricTypeID = cm.MetricTypeID GROUP BY cm.MetricTypeID, cm.ProjectID, cm.MetricName, cm.Description, cm.MinValue, cm.MaxValue, cm.DefaultValue, cm.SortOrder, cm.IsActive, ptms.MetricTypeID, ptms.SortOrder ORDER BY CASE WHEN cm.IsActive = 1 AND ptms.MetricTypeID IS NOT NULL THEN 0 ELSE 1 END, COALESCE(ptms.SortOrder, cm.SortOrder), cm.MetricName; END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_GetForProject @MetricTypeID int, @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT mt.MetricTypeID, mt.ProjectID, mt.MetricName, mt.Description, mt.MinValue, mt.MaxValue, mt.DefaultValue, COALESCE(ptms.SortOrder, mt.SortOrder) AS SortOrder, CAST(CASE WHEN mt.IsActive = 1 AND ptms.MetricTypeID IS NOT NULL THEN 1 ELSE 0 END AS bit) AS IsActive, CAST(CASE WHEN mt.IsActive = 1 AND ptms.MetricTypeID IS NOT NULL THEN 1 ELSE 0 END AS bit) AS IsEnabledForProject, (SELECT COUNT(*) FROM dbo.SceneMetricValues smv WHERE smv.MetricTypeID = mt.MetricTypeID) AS ValueCount FROM dbo.SceneMetricTypes mt LEFT JOIN dbo.ProjectTimelineMetricSettings ptms ON ptms.ProjectID = @ProjectID AND ptms.MetricTypeID = mt.MetricTypeID WHERE mt.MetricTypeID = @MetricTypeID AND (mt.ProjectID IS NULL OR mt.ProjectID = @ProjectID); END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_SaveForProject @MetricTypeID int = NULL, @ProjectID int, @MetricName nvarchar(100), @Description nvarchar(500) = NULL, @MinValue int, @MaxValue int, @DefaultValue int, @SortOrder int, @IsActive bit = 1 AS BEGIN SET NOCOUNT ON; IF @MinValue >= @MaxValue THROW 50010, 'Metric minimum must be less than maximum.', 1; IF @DefaultValue < @MinValue OR @DefaultValue > @MaxValue THROW 50011, 'Metric default value must be inside the metric range.', 1; IF @MetricTypeID IS NULL OR @MetricTypeID = 0 BEGIN SELECT @MetricTypeID = MetricTypeID FROM dbo.SceneMetricTypes WHERE ProjectID = @ProjectID AND LOWER(MetricName) = LOWER(@MetricName); IF @MetricTypeID IS NULL OR @MetricTypeID = 0 BEGIN INSERT dbo.SceneMetricTypes (ProjectID, MetricName, Description, MinValue, MaxValue, DefaultValue, SortOrder, IsActive) VALUES (@ProjectID, @MetricName, @Description, @MinValue, @MaxValue, @DefaultValue, @SortOrder, 1); SET @MetricTypeID = CAST(SCOPE_IDENTITY() AS int); END END UPDATE dbo.SceneMetricTypes SET MetricName = @MetricName, Description = @Description, MinValue = @MinValue, MaxValue = @MaxValue, DefaultValue = @DefaultValue, SortOrder = @SortOrder, IsActive = 1 WHERE MetricTypeID = @MetricTypeID AND (ProjectID = @ProjectID OR ProjectID IS NULL); IF @IsActive = 1 BEGIN MERGE dbo.ProjectTimelineMetricSettings AS target USING (SELECT @ProjectID AS ProjectID, @MetricTypeID AS MetricTypeID, @SortOrder AS SortOrder) AS source ON target.ProjectID = source.ProjectID AND target.MetricTypeID = source.MetricTypeID WHEN MATCHED THEN UPDATE SET SortOrder = source.SortOrder WHEN NOT MATCHED THEN INSERT (ProjectID, MetricTypeID, SortOrder) VALUES (source.ProjectID, source.MetricTypeID, source.SortOrder); END ELSE BEGIN DELETE FROM dbo.ProjectTimelineMetricSettings WHERE ProjectID = @ProjectID AND MetricTypeID = @MetricTypeID; END; SELECT @MetricTypeID; END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_SetActiveForProject @MetricTypeID int, @ProjectID int, @IsActive bit AS BEGIN SET NOCOUNT ON; IF @IsActive = 1 BEGIN UPDATE dbo.SceneMetricTypes SET IsActive = 1 WHERE MetricTypeID = @MetricTypeID AND ProjectID = @ProjectID; INSERT dbo.ProjectTimelineMetricSettings (ProjectID, MetricTypeID, SortOrder) SELECT @ProjectID, mt.MetricTypeID, mt.SortOrder FROM dbo.SceneMetricTypes mt WHERE mt.MetricTypeID = @MetricTypeID AND mt.IsActive = 1 AND (mt.ProjectID IS NULL OR mt.ProjectID = @ProjectID) AND NOT EXISTS ( SELECT 1 FROM dbo.ProjectTimelineMetricSettings ptms WHERE ptms.ProjectID = @ProjectID AND ptms.MetricTypeID = @MetricTypeID ); SELECT CAST(CASE WHEN EXISTS ( SELECT 1 FROM dbo.ProjectTimelineMetricSettings ptms INNER JOIN dbo.SceneMetricTypes mt ON mt.MetricTypeID = ptms.MetricTypeID WHERE ptms.ProjectID = @ProjectID AND ptms.MetricTypeID = @MetricTypeID AND mt.IsActive = 1 AND (mt.ProjectID IS NULL OR mt.ProjectID = @ProjectID) ) THEN 1 ELSE 0 END AS bit); RETURN; END ELSE BEGIN DELETE FROM dbo.ProjectTimelineMetricSettings WHERE ProjectID = @ProjectID AND MetricTypeID = @MetricTypeID; UPDATE dbo.SceneMetricTypes SET IsActive = 0 WHERE MetricTypeID = @MetricTypeID AND ProjectID = @ProjectID; SELECT CAST(CASE WHEN NOT EXISTS ( SELECT 1 FROM dbo.ProjectTimelineMetricSettings WHERE ProjectID = @ProjectID AND MetricTypeID = @MetricTypeID ) THEN 1 ELSE 0 END AS bit); RETURN; END; END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_MoveForProject @MetricTypeID int, @ProjectID int, @Direction nvarchar(10) AS BEGIN SET NOCOUNT ON; DECLARE @NormalisedDirection nvarchar(10) = CASE WHEN UPPER(@Direction) = N'UP' THEN N'Up' ELSE N'Down' END; ;WITH CandidateMetrics AS ( SELECT mt.MetricTypeID, mt.ProjectID, mt.MetricName, COALESCE(ptms.SortOrder, mt.SortOrder) AS SortOrder, CAST(CASE WHEN mt.IsActive = 1 AND ptms.MetricTypeID IS NOT NULL THEN 1 ELSE 0 END AS bit) AS IsEnabledForProject, CAST(CASE WHEN mt.IsActive = 1 AND ptms.MetricTypeID IS NOT NULL THEN 0 ELSE 1 END AS int) AS GroupRank FROM dbo.SceneMetricTypes mt LEFT JOIN dbo.ProjectTimelineMetricSettings ptms ON ptms.ProjectID = @ProjectID AND ptms.MetricTypeID = mt.MetricTypeID WHERE mt.ProjectID = @ProjectID OR ptms.ProjectID = @ProjectID OR ( mt.ProjectID IS NULL AND mt.MetricName IN ( N'Overall Intensity', N'Tension', N'Emotional Weight', N'Action', N'Darkness', N'Romance', N'Sexual Charge', N'Violence', N'Hope / Lightness', N'Comedy', N'Mystery' ) ) ) SELECT *, ROW_NUMBER() OVER (PARTITION BY GroupRank ORDER BY SortOrder, MetricName, MetricTypeID) AS RowNumber, ROW_NUMBER() OVER (PARTITION BY GroupRank ORDER BY SortOrder, MetricName, MetricTypeID) * 10 AS NormalisedSortOrder INTO #OrderedMetrics FROM CandidateMetrics; UPDATE ptms SET SortOrder = ordered.NormalisedSortOrder FROM dbo.ProjectTimelineMetricSettings ptms INNER JOIN #OrderedMetrics ordered ON ordered.MetricTypeID = ptms.MetricTypeID WHERE ptms.ProjectID = @ProjectID AND ordered.IsEnabledForProject = 1; UPDATE mt SET SortOrder = ordered.NormalisedSortOrder FROM dbo.SceneMetricTypes mt INNER JOIN #OrderedMetrics ordered ON ordered.MetricTypeID = mt.MetricTypeID WHERE mt.ProjectID = @ProjectID AND ordered.IsEnabledForProject = 0; ;WITH OrderedMetrics AS ( SELECT MetricTypeID, ProjectID, MetricName, NormalisedSortOrder AS SortOrder, IsEnabledForProject, GroupRank, RowNumber FROM #OrderedMetrics ), MovePair AS ( SELECT currentMetric.MetricTypeID AS CurrentMetricTypeID, currentMetric.ProjectID AS CurrentProjectID, currentMetric.IsEnabledForProject AS CurrentIsEnabledForProject, currentMetric.SortOrder AS CurrentSortOrder, adjacentMetric.MetricTypeID AS AdjacentMetricTypeID, adjacentMetric.ProjectID AS AdjacentProjectID, adjacentMetric.IsEnabledForProject AS AdjacentIsEnabledForProject, adjacentMetric.SortOrder AS AdjacentSortOrder FROM OrderedMetrics currentMetric INNER JOIN OrderedMetrics adjacentMetric ON adjacentMetric.GroupRank = currentMetric.GroupRank AND adjacentMetric.RowNumber = CASE WHEN @NormalisedDirection = N'Up' THEN currentMetric.RowNumber - 1 ELSE currentMetric.RowNumber + 1 END WHERE currentMetric.MetricTypeID = @MetricTypeID ) SELECT * INTO #MovePair FROM MovePair; IF NOT EXISTS (SELECT 1 FROM #MovePair) BEGIN SELECT CAST(0 AS bit); RETURN; END; IF EXISTS (SELECT 1 FROM #MovePair WHERE CurrentIsEnabledForProject = 0 AND CurrentProjectID IS NULL) BEGIN SELECT CAST(0 AS bit); RETURN; END; IF EXISTS (SELECT 1 FROM #MovePair WHERE AdjacentIsEnabledForProject = 0 AND AdjacentProjectID IS NULL) BEGIN SELECT CAST(0 AS bit); RETURN; END; UPDATE ptms SET SortOrder = mp.AdjacentSortOrder FROM dbo.ProjectTimelineMetricSettings ptms INNER JOIN #MovePair mp ON mp.CurrentMetricTypeID = ptms.MetricTypeID WHERE ptms.ProjectID = @ProjectID AND mp.CurrentIsEnabledForProject = 1; UPDATE mt SET SortOrder = mp.AdjacentSortOrder FROM dbo.SceneMetricTypes mt INNER JOIN #MovePair mp ON mp.CurrentMetricTypeID = mt.MetricTypeID WHERE mt.ProjectID = @ProjectID AND mp.CurrentIsEnabledForProject = 0; UPDATE ptms SET SortOrder = mp.CurrentSortOrder FROM dbo.ProjectTimelineMetricSettings ptms INNER JOIN #MovePair mp ON mp.AdjacentMetricTypeID = ptms.MetricTypeID WHERE ptms.ProjectID = @ProjectID AND mp.AdjacentIsEnabledForProject = 1; UPDATE mt SET SortOrder = mp.CurrentSortOrder FROM dbo.SceneMetricTypes mt INNER JOIN #MovePair mp ON mp.AdjacentMetricTypeID = mt.MetricTypeID WHERE mt.ProjectID = @ProjectID AND mp.AdjacentIsEnabledForProject = 0; SELECT CAST(1 AS bit); END; GO CREATE OR ALTER PROCEDURE dbo.SceneMetricType_RemoveUnusedForProject @MetricTypeID int, @ProjectID int AS BEGIN SET NOCOUNT ON; IF EXISTS ( SELECT 1 FROM dbo.SceneMetricTypes mt WHERE mt.MetricTypeID = @MetricTypeID AND mt.ProjectID = @ProjectID AND mt.IsActive = 0 AND NOT EXISTS (SELECT 1 FROM dbo.SceneMetricValues smv WHERE smv.MetricTypeID = mt.MetricTypeID) ) BEGIN DELETE FROM dbo.ProjectTimelineMetricSettings WHERE ProjectID = @ProjectID AND MetricTypeID = @MetricTypeID; DELETE FROM dbo.SceneMetricTypes WHERE MetricTypeID = @MetricTypeID AND ProjectID = @ProjectID; SELECT CAST(1 AS bit); RETURN; END; SELECT CAST(0 AS bit); END; GO CREATE OR ALTER PROCEDURE dbo.ProjectTimelineSettings_EnsureDefaults @ProjectID int AS BEGIN SET NOCOUNT ON; IF NOT EXISTS (SELECT 1 FROM dbo.Projects WHERE ProjectID = @ProjectID AND IsArchived = 0) RETURN; IF NOT EXISTS (SELECT 1 FROM dbo.ProjectTimelineSettings WHERE ProjectID = @ProjectID) BEGIN INSERT dbo.ProjectTimelineSettings (ProjectID, ShowSceneCards, ShowMetricShape, ShowPlotLines, ShowStoryAssets, ShowCharacterAppearances, ShowWarnings) VALUES (@ProjectID, 1, 1, 1, 1, 1, 1); END; END; GO CREATE OR ALTER PROCEDURE dbo.ProjectTimelineSettings_MetricTypes @ProjectID int AS BEGIN SET NOCOUNT ON; SELECT mt.MetricTypeID, mt.ProjectID, mt.MetricName, mt.Description, mt.MinValue, mt.MaxValue, mt.DefaultValue, ptms.SortOrder, mt.IsActive FROM dbo.ProjectTimelineMetricSettings ptms INNER JOIN dbo.SceneMetricTypes mt ON mt.MetricTypeID = ptms.MetricTypeID WHERE ptms.ProjectID = @ProjectID AND mt.IsActive = 1 AND (mt.ProjectID IS NULL OR mt.ProjectID = @ProjectID) ORDER BY ptms.SortOrder, mt.SortOrder, mt.MetricName; END; GO