Converted all inline SQL to stored procedures.
This commit is contained in:
parent
d255407193
commit
a3ce37c66c
@ -24,16 +24,9 @@ public sealed class ImportRepository(ISqlConnectionFactory connectionFactory) :
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var count = await connection.ExecuteScalarAsync<int>(
|
||||
"""
|
||||
SELECT COUNT(1)
|
||||
FROM dbo.Projects p
|
||||
INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID
|
||||
WHERE p.IsArchived = 0
|
||||
AND p.ProjectName = @ProjectName
|
||||
AND pua.UserID = @UserID
|
||||
AND pua.IsActive = 1;
|
||||
""",
|
||||
new { ProjectName = projectTitle, UserID = userId });
|
||||
"dbo.Project_ExistsByNameForUser",
|
||||
new { ProjectName = projectTitle, UserID = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
return count > 0;
|
||||
}
|
||||
@ -329,9 +322,10 @@ public sealed class ImportRepository(ISqlConnectionFactory connectionFactory) :
|
||||
if (povCharacterId.HasValue)
|
||||
{
|
||||
await connection.ExecuteAsync(
|
||||
"UPDATE dbo.Scenes SET POVCharacterID = @POVCharacterID, UpdatedDate = SYSUTCDATETIME() WHERE SceneID = @SceneID;",
|
||||
"dbo.Scene_UpdatePovCharacter",
|
||||
new { SceneID = sceneId, POVCharacterID = povCharacterId.Value },
|
||||
transaction);
|
||||
transaction,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
foreach (var appearance in BuildSceneAppearances(scene, povCharacterId, characterMap, package.Aliases.Characters))
|
||||
@ -666,68 +660,35 @@ public sealed class ImportRepository(ISqlConnectionFactory connectionFactory) :
|
||||
|
||||
private static async Task<ImportLookupIds> GetLookupIdsAsync(IDbConnection connection, IDbTransaction transaction)
|
||||
{
|
||||
var revisionStatusId = await connection.QuerySingleOrDefaultAsync<int?>(
|
||||
"SELECT TOP (1) RevisionStatusID FROM dbo.RevisionStatuses WHERE StatusName = N'Planned' ORDER BY SortOrder;",
|
||||
transaction: transaction);
|
||||
var timeModeId = await connection.QuerySingleOrDefaultAsync<int?>(
|
||||
"SELECT TOP (1) TimeModeID FROM dbo.TimeModes WHERE TimeModeName = N'Relative' ORDER BY SortOrder;",
|
||||
transaction: transaction);
|
||||
var timeConfidenceId = await connection.QuerySingleOrDefaultAsync<int?>(
|
||||
"SELECT TOP (1) TimeConfidenceID FROM dbo.TimeConfidences WHERE TimeConfidenceName = N'Unknown' ORDER BY SortOrder;",
|
||||
transaction: transaction);
|
||||
using var result = await connection.QueryMultipleAsync(
|
||||
"dbo.Import_LookupIds",
|
||||
transaction: transaction,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
var requiredLookups = await result.ReadSingleAsync<ImportRequiredLookupRow>();
|
||||
var locationTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var roleTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var presenceTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var plotLineTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var plotImportance = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var threadTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var threadStatuses = (await result.ReadAsync<ImportStatusLookupRow>()).ToDictionary(x => x.StatusName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var threadEventTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var sceneDependencyTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var assetKinds = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var assetStates = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var assetEventTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var assetCustodyEventTypes = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var custodyRoles = (await result.ReadAsync<ImportLookupRow>()).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
var locationTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT LocationTypeID AS LookupID, TypeName FROM dbo.LocationTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var roleTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT CharacterRoleInSceneTypeID AS LookupID, TypeName FROM dbo.CharacterRoleInSceneTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var presenceTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT PresenceTypeID AS LookupID, TypeName FROM dbo.PresenceTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var plotLineTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT PlotLineTypeID AS LookupID, TypeName FROM dbo.PlotLineTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var plotImportance = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT PlotImportanceID AS LookupID, ImportanceName AS TypeName FROM dbo.PlotImportance WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var threadTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT ThreadTypeID AS LookupID, TypeName FROM dbo.ThreadTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var threadStatuses = (await connection.QueryAsync<ImportStatusLookupRow>(
|
||||
"SELECT ThreadStatusID AS LookupID, StatusName FROM dbo.ThreadStatuses WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.StatusName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var threadEventTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT ThreadEventTypeID AS LookupID, TypeName FROM dbo.ThreadEventTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var sceneDependencyTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT SceneDependencyTypeID AS LookupID, TypeName FROM dbo.SceneDependencyTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var assetKinds = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT AssetKindID AS LookupID, KindName AS TypeName FROM dbo.AssetKinds WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var assetStates = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT AssetStateID AS LookupID, StateName AS TypeName FROM dbo.AssetStates WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var assetEventTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT AssetEventTypeID AS LookupID, TypeName FROM dbo.AssetEventTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var assetCustodyEventTypes = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT AssetCustodyEventTypeID AS LookupID, TypeName FROM dbo.AssetCustodyEventTypes WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
var custodyRoles = (await connection.QueryAsync<ImportLookupRow>(
|
||||
"SELECT CustodyRoleID AS LookupID, RoleName AS TypeName FROM dbo.CustodyRoles WHERE IsActive = 1;",
|
||||
transaction: transaction)).ToDictionary(x => x.TypeName, x => x.LookupID, StringComparer.OrdinalIgnoreCase);
|
||||
|
||||
if (!revisionStatusId.HasValue || !timeModeId.HasValue || !timeConfidenceId.HasValue)
|
||||
if (!requiredLookups.RevisionStatusID.HasValue || !requiredLookups.TimeModeID.HasValue || !requiredLookups.TimeConfidenceID.HasValue)
|
||||
{
|
||||
throw new InvalidOperationException("Import lookup values are missing. Run the core database script before importing.");
|
||||
}
|
||||
|
||||
return new ImportLookupIds(
|
||||
revisionStatusId.Value,
|
||||
timeModeId.Value,
|
||||
timeConfidenceId.Value,
|
||||
requiredLookups.RevisionStatusID.Value,
|
||||
requiredLookups.TimeModeID.Value,
|
||||
requiredLookups.TimeConfidenceID.Value,
|
||||
locationTypes,
|
||||
roleTypes,
|
||||
presenceTypes,
|
||||
@ -1432,6 +1393,13 @@ public sealed class ImportRepository(ISqlConnectionFactory connectionFactory) :
|
||||
public string TypeName { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
private sealed class ImportRequiredLookupRow
|
||||
{
|
||||
public int? RevisionStatusID { get; set; }
|
||||
public int? TimeModeID { get; set; }
|
||||
public int? TimeConfidenceID { get; set; }
|
||||
}
|
||||
|
||||
private sealed class ImportStatusLookupRow
|
||||
{
|
||||
public int LookupID { get; set; }
|
||||
|
||||
@ -81,13 +81,9 @@ public sealed class ProjectBackupRepository(ISqlConnectionFactory connectionFact
|
||||
}
|
||||
|
||||
var bookMetadata = (await connection.QueryAsync<Book>(
|
||||
"""
|
||||
SELECT BookID, Subtitle, Tagline, ShortDescription, Status, TargetWordCount, WritingStartDate, TargetCompletionDate, PublicationDate
|
||||
, CoverThumbnailPath, CoverStandardPath
|
||||
FROM dbo.Books
|
||||
WHERE ProjectID = @ProjectID;
|
||||
""",
|
||||
new { ProjectID = projectId })).ToDictionary(x => x.BookID);
|
||||
"dbo.ProjectBackup_BookMetadata_List",
|
||||
new { ProjectID = projectId },
|
||||
commandType: CommandType.StoredProcedure)).ToDictionary(x => x.BookID);
|
||||
foreach (var book in data.Books)
|
||||
{
|
||||
if (!bookMetadata.TryGetValue(book.BookID, out var metadata))
|
||||
|
||||
@ -792,44 +792,9 @@ public sealed class WarningRepository(ISqlConnectionFactory connectionFactory) :
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<ContinuityWarning>(
|
||||
"""
|
||||
SELECT TOP (@Count)
|
||||
cw.ContinuityWarningID,
|
||||
cw.ValidationRunID,
|
||||
cw.ProjectID,
|
||||
cw.BookID,
|
||||
b.BookTitle,
|
||||
b.Subtitle AS BookSubtitle,
|
||||
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 cw.IsDismissed = 0
|
||||
AND cw.IsIntentional = 0
|
||||
ORDER BY ws.SortOrder DESC, cw.LastDetectedDate DESC, cw.ContinuityWarningID DESC;
|
||||
""",
|
||||
new { ProjectID = projectId, Count = count });
|
||||
"dbo.ContinuityWarning_ListDashboardUnresolved",
|
||||
new { ProjectID = projectId, Count = count },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -1349,45 +1314,9 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<WritingPlanManagementItem>(
|
||||
"""
|
||||
SELECT wp.WritingPlanID,
|
||||
wp.UserID,
|
||||
wp.ProjectID,
|
||||
p.ProjectName,
|
||||
wp.BookID,
|
||||
b.BookTitle,
|
||||
b.Subtitle AS BookSubtitle,
|
||||
wp.PlanName,
|
||||
wp.GoalType,
|
||||
wp.StartDate,
|
||||
wp.DeadlineDate,
|
||||
wp.AvailableDaysJson,
|
||||
wp.SessionLengthMinutes,
|
||||
wp.IsActive,
|
||||
SUM(CASE WHEN wsi.ScheduleStatus = N'Planned' THEN 1 ELSE 0 END) AS PlannedTaskCount,
|
||||
MAX(CASE WHEN wsi.ScheduleStatus = N'Planned' THEN wsi.ScheduledDate ELSE NULL END) AS ProjectedFinishDate
|
||||
FROM dbo.WritingPlans wp
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = wp.ProjectID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = wp.BookID
|
||||
LEFT JOIN dbo.WritingScheduleItems wsi ON wsi.WritingPlanID = wp.WritingPlanID
|
||||
WHERE wp.UserID = @UserID
|
||||
GROUP BY wp.WritingPlanID,
|
||||
wp.UserID,
|
||||
wp.ProjectID,
|
||||
p.ProjectName,
|
||||
wp.BookID,
|
||||
b.BookTitle,
|
||||
b.Subtitle,
|
||||
wp.PlanName,
|
||||
wp.GoalType,
|
||||
wp.StartDate,
|
||||
wp.DeadlineDate,
|
||||
wp.AvailableDaysJson,
|
||||
wp.SessionLengthMinutes,
|
||||
wp.IsActive
|
||||
ORDER BY wp.IsActive DESC, wp.DeadlineDate, wp.PlanName, wp.WritingPlanID;
|
||||
""",
|
||||
new { UserID = userId });
|
||||
"dbo.WritingPlan_ListForManagement",
|
||||
new { UserID = userId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -1395,29 +1324,9 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<WritingScheduleScene>(
|
||||
"""
|
||||
SELECT s.SceneID,
|
||||
rs.StatusName AS RevisionStatusName,
|
||||
sw.EstimatedWordCount,
|
||||
sw.Priority,
|
||||
COALESCE(sw.IsBlocked, 0) AS IsBlocked,
|
||||
b.SortOrder AS BookSortOrder,
|
||||
c.SortOrder AS ChapterSortOrder,
|
||||
s.SortOrder AS SceneSortOrder
|
||||
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
|
||||
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE p.ProjectID = @ProjectID
|
||||
AND p.IsArchived = 0
|
||||
AND (@BookID IS NULL OR b.BookID = @BookID)
|
||||
AND rs.StatusName IN (N'Outlined', N'Needs Work', N'Revised')
|
||||
AND (@IncludeBlockedScenes = 1 OR COALESCE(sw.IsBlocked, 0) = 0)
|
||||
ORDER BY COALESCE(sw.Priority, 0) DESC, b.SortOrder, c.SortOrder, s.SortOrder, s.SceneID;
|
||||
""",
|
||||
new { ProjectID = projectId, BookID = bookId, IncludeBlockedScenes = includeBlockedScenes });
|
||||
"dbo.WritingScheduleScene_ListForPlanning",
|
||||
new { ProjectID = projectId, BookID = bookId, IncludeBlockedScenes = includeBlockedScenes },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -1430,25 +1339,15 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
try
|
||||
{
|
||||
await connection.ExecuteAsync(
|
||||
"DELETE FROM dbo.WritingScheduleItems WHERE WritingPlanID = @WritingPlanID;",
|
||||
"dbo.WritingScheduleItem_DeleteByPlan",
|
||||
new { WritingPlanID = writingPlanId },
|
||||
transaction);
|
||||
transaction,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
foreach (var item in items)
|
||||
{
|
||||
await connection.ExecuteAsync(
|
||||
"""
|
||||
INSERT dbo.WritingScheduleItems
|
||||
(
|
||||
WritingPlanID, SceneID, ScheduledDate, TaskType, EstimatedMinutes, TargetWords,
|
||||
ScheduleStatus, CompletedDateUtc, Notes
|
||||
)
|
||||
VALUES
|
||||
(
|
||||
@WritingPlanID, @SceneID, @ScheduledDate, @TaskType, @EstimatedMinutes, @TargetWords,
|
||||
@ScheduleStatus, @CompletedDateUtc, @Notes
|
||||
);
|
||||
""",
|
||||
"dbo.CreateScheduleItem",
|
||||
new
|
||||
{
|
||||
item.WritingPlanID,
|
||||
@ -1461,7 +1360,8 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
item.CompletedDateUtc,
|
||||
item.Notes
|
||||
},
|
||||
transaction);
|
||||
transaction,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
@ -1477,25 +1377,9 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<WritingPlanSummary>(
|
||||
"""
|
||||
SELECT wp.WritingPlanID,
|
||||
wp.UserID,
|
||||
wp.ProjectID,
|
||||
p.ProjectName,
|
||||
wp.BookID,
|
||||
b.BookTitle,
|
||||
b.Subtitle AS BookSubtitle,
|
||||
wp.PlanName,
|
||||
wp.DeadlineDate,
|
||||
wp.IsActive
|
||||
FROM dbo.WritingPlans wp
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = wp.ProjectID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = wp.BookID
|
||||
WHERE wp.UserID = @UserID
|
||||
AND (@ProjectID IS NULL OR wp.ProjectID = @ProjectID)
|
||||
ORDER BY wp.IsActive DESC, wp.DeadlineDate, wp.PlanName, wp.WritingPlanID;
|
||||
""",
|
||||
new { UserID = userId, ProjectID = projectId });
|
||||
"dbo.WritingPlanSummary_ListByUser",
|
||||
new { UserID = userId, ProjectID = projectId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -1503,33 +1387,9 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<WritingSchedulePreviewItem>(
|
||||
"""
|
||||
SELECT wsi.WritingScheduleItemID,
|
||||
wsi.WritingPlanID,
|
||||
wsi.SceneID,
|
||||
wsi.ScheduledDate,
|
||||
wsi.TaskType,
|
||||
b.BookTitle,
|
||||
b.Subtitle AS BookSubtitle,
|
||||
c.ChapterNumber,
|
||||
c.ChapterTitle,
|
||||
s.SceneNumber,
|
||||
s.SceneTitle,
|
||||
sw.Priority,
|
||||
wsi.EstimatedMinutes,
|
||||
wsi.TargetWords,
|
||||
wsi.ScheduleStatus,
|
||||
wsi.Notes,
|
||||
COALESCE(sw.IsBlocked, 0) AS IsBlocked
|
||||
FROM dbo.WritingScheduleItems wsi
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = wsi.SceneID
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE wsi.WritingPlanID = @WritingPlanID
|
||||
ORDER BY wsi.ScheduledDate, wsi.WritingScheduleItemID;
|
||||
""",
|
||||
new { WritingPlanID = writingPlanId });
|
||||
"dbo.WritingSchedulePreview_ListByPlan",
|
||||
new { WritingPlanID = writingPlanId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -1537,46 +1397,18 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
await connection.ExecuteAsync(
|
||||
"DELETE FROM dbo.WritingScheduleItems WHERE WritingPlanID = @WritingPlanID;",
|
||||
new { WritingPlanID = writingPlanId });
|
||||
"dbo.WritingScheduleItem_DeleteByPlan",
|
||||
new { WritingPlanID = writingPlanId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<WritingScheduleDashboardItem>> GetActiveScheduleDashboardItemsAsync(int userId, int? projectId = null)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<WritingScheduleDashboardItem>(
|
||||
"""
|
||||
SELECT wsi.WritingScheduleItemID,
|
||||
wsi.WritingPlanID,
|
||||
wp.PlanName,
|
||||
wsi.SceneID,
|
||||
wsi.ScheduledDate,
|
||||
wsi.TaskType,
|
||||
b.BookTitle,
|
||||
b.Subtitle AS BookSubtitle,
|
||||
c.ChapterNumber,
|
||||
c.ChapterTitle,
|
||||
c.SortOrder AS ChapterSortOrder,
|
||||
s.SceneNumber,
|
||||
s.SceneTitle,
|
||||
s.SortOrder AS SceneSortOrder,
|
||||
sw.Priority,
|
||||
wsi.EstimatedMinutes,
|
||||
wsi.TargetWords,
|
||||
wsi.ScheduleStatus,
|
||||
COALESCE(sw.IsBlocked, 0) AS IsBlocked
|
||||
FROM dbo.WritingPlans wp
|
||||
INNER JOIN dbo.WritingScheduleItems wsi ON wsi.WritingPlanID = wp.WritingPlanID
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = wsi.SceneID
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE wp.UserID = @UserID
|
||||
AND wp.IsActive = 1
|
||||
AND (@ProjectID IS NULL OR wp.ProjectID = @ProjectID)
|
||||
ORDER BY wsi.ScheduledDate, wsi.WritingScheduleItemID;
|
||||
""",
|
||||
new { UserID = userId, ProjectID = projectId });
|
||||
"dbo.WritingScheduleDashboard_ListActive",
|
||||
new { UserID = userId, ProjectID = projectId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -1584,59 +1416,24 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
return await connection.ExecuteAsync(
|
||||
"""
|
||||
UPDATE wsi
|
||||
SET ScheduleStatus = @ScheduleStatus,
|
||||
CompletedDateUtc = @CompletedDateUtc,
|
||||
ModifiedDateUtc = SYSUTCDATETIME()
|
||||
FROM dbo.WritingScheduleItems wsi
|
||||
INNER JOIN dbo.WritingPlans wp ON wp.WritingPlanID = wsi.WritingPlanID
|
||||
WHERE wsi.WritingScheduleItemID = @WritingScheduleItemID
|
||||
AND wp.UserID = @UserID
|
||||
AND wp.IsActive = 1
|
||||
AND wsi.ScheduleStatus = N'Planned';
|
||||
""",
|
||||
"dbo.WritingScheduleItem_UpdatePlannedStatusForUser",
|
||||
new
|
||||
{
|
||||
WritingScheduleItemID = writingScheduleItemId,
|
||||
UserID = userId,
|
||||
ScheduleStatus = scheduleStatus,
|
||||
CompletedDateUtc = completedDateUtc
|
||||
});
|
||||
},
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<WritingPlanSummary>> GetPlansRequiringRebalanceAsync(int userId, DateTime today, int? projectId = null)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<WritingPlanSummary>(
|
||||
"""
|
||||
SELECT wp.WritingPlanID,
|
||||
wp.UserID,
|
||||
wp.ProjectID,
|
||||
p.ProjectName,
|
||||
wp.BookID,
|
||||
b.BookTitle,
|
||||
b.Subtitle AS BookSubtitle,
|
||||
wp.PlanName,
|
||||
wp.DeadlineDate,
|
||||
wp.IsActive
|
||||
FROM dbo.WritingPlans wp
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = wp.ProjectID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = wp.BookID
|
||||
WHERE wp.UserID = @UserID
|
||||
AND wp.IsActive = 1
|
||||
AND (@ProjectID IS NULL OR wp.ProjectID = @ProjectID)
|
||||
AND EXISTS
|
||||
(
|
||||
SELECT 1
|
||||
FROM dbo.WritingScheduleItems wsi
|
||||
WHERE wsi.WritingPlanID = wp.WritingPlanID
|
||||
AND wsi.ScheduleStatus = N'Planned'
|
||||
AND wsi.ScheduledDate < @Today
|
||||
)
|
||||
ORDER BY wp.DeadlineDate, wp.PlanName, wp.WritingPlanID;
|
||||
""",
|
||||
new { UserID = userId, Today = today.Date, ProjectID = projectId });
|
||||
"dbo.WritingPlanSummary_ListRequiringRebalance",
|
||||
new { UserID = userId, Today = today.Date, ProjectID = projectId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -1656,21 +1453,15 @@ public sealed class WritingScheduleRepository(ISqlConnectionFactory connectionFa
|
||||
foreach (var change in changes)
|
||||
{
|
||||
await connection.ExecuteAsync(
|
||||
"""
|
||||
UPDATE dbo.WritingScheduleItems
|
||||
SET ScheduledDate = @ScheduledDate,
|
||||
ModifiedDateUtc = SYSUTCDATETIME()
|
||||
WHERE WritingPlanID = @WritingPlanID
|
||||
AND WritingScheduleItemID = @WritingScheduleItemID
|
||||
AND ScheduleStatus = N'Planned';
|
||||
""",
|
||||
"dbo.WritingScheduleItem_UpdatePlannedDate",
|
||||
new
|
||||
{
|
||||
WritingPlanID = writingPlanId,
|
||||
change.WritingScheduleItemID,
|
||||
ScheduledDate = change.ScheduledDate.Date
|
||||
},
|
||||
transaction);
|
||||
transaction,
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
transaction.Commit();
|
||||
@ -2095,62 +1886,18 @@ public sealed class ProjectRepository(ISqlConnectionFactory connectionFactory) :
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
return await connection.QuerySingleAsync<ProjectDashboardMetrics>(
|
||||
"""
|
||||
SELECT
|
||||
(SELECT COUNT(1) FROM dbo.Characters WHERE ProjectID = @ProjectID AND IsArchived = 0) AS CharacterCount,
|
||||
(SELECT COUNT(1) FROM dbo.Locations WHERE ProjectID = @ProjectID AND IsArchived = 0) AS LocationCount,
|
||||
(
|
||||
SELECT COUNT(1)
|
||||
FROM dbo.PlotThreads pt
|
||||
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
|
||||
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
|
||||
WHERE pl.ProjectID = @ProjectID
|
||||
AND pl.IsArchived = 0
|
||||
AND pt.IsArchived = 0
|
||||
AND ts.IsOpenStatus = 1
|
||||
) AS ActivePlotThreadCount,
|
||||
(
|
||||
SELECT COUNT(1)
|
||||
FROM dbo.ContinuityWarnings
|
||||
WHERE ProjectID = @ProjectID
|
||||
AND IsDismissed = 0
|
||||
AND IsIntentional = 0
|
||||
) AS ContinuityWarningCount;
|
||||
""",
|
||||
new { ProjectID = projectId });
|
||||
"dbo.ProjectDashboard_Metrics",
|
||||
new { ProjectID = projectId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<ProjectDashboardAttentionThread>> ListDashboardAttentionThreadsAsync(int projectId, int count)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<ProjectDashboardAttentionThread>(
|
||||
"""
|
||||
SELECT TOP (@Count)
|
||||
pt.PlotThreadID,
|
||||
pt.ThreadTitle,
|
||||
pt.PlotLineID,
|
||||
pl.PlotLineName,
|
||||
pt.Importance,
|
||||
ts.StatusName AS ThreadStatusName,
|
||||
ts.IsOpenStatus,
|
||||
ts.IsResolvedStatus
|
||||
FROM dbo.PlotThreads pt
|
||||
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
|
||||
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
|
||||
WHERE pl.ProjectID = @ProjectID
|
||||
AND pl.IsArchived = 0
|
||||
AND pt.IsArchived = 0
|
||||
AND (
|
||||
ts.StatusName = N'Dormant'
|
||||
OR (ts.IsOpenStatus = 1 AND pt.Importance >= 8)
|
||||
)
|
||||
ORDER BY
|
||||
CASE WHEN ts.StatusName = N'Dormant' THEN 0 ELSE 1 END,
|
||||
pt.Importance DESC,
|
||||
pt.UpdatedDate DESC,
|
||||
pt.PlotThreadID DESC;
|
||||
""",
|
||||
new { ProjectID = projectId, Count = count });
|
||||
"dbo.ProjectDashboard_AttentionThreads",
|
||||
new { ProjectID = projectId, Count = count },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -2333,25 +2080,9 @@ public sealed class ProjectActivityRepository(ISqlConnectionFactory connectionFa
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<ProjectActivity>(
|
||||
"""
|
||||
SELECT TOP (@Count)
|
||||
pa.ProjectActivityID,
|
||||
pa.ProjectID,
|
||||
pa.UserID,
|
||||
u.DisplayName,
|
||||
u.Email,
|
||||
pa.ActivityType,
|
||||
pa.EntityType,
|
||||
pa.EntityID,
|
||||
pa.EntityName,
|
||||
pa.Description,
|
||||
pa.CreatedDateUTC
|
||||
FROM dbo.ProjectActivity pa
|
||||
LEFT JOIN dbo.AppUser u ON u.UserID = pa.UserID
|
||||
WHERE pa.ProjectID = @ProjectID
|
||||
ORDER BY pa.CreatedDateUTC DESC, pa.ProjectActivityID DESC;
|
||||
""",
|
||||
new { ProjectID = projectId, Count = count });
|
||||
"dbo.ProjectActivity_ListRecent",
|
||||
new { ProjectID = projectId, Count = count },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -2393,54 +2124,9 @@ public sealed class BookRepository(ISqlConnectionFactory connectionFactory) : IB
|
||||
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<Book>(
|
||||
"""
|
||||
SELECT
|
||||
b.BookID,
|
||||
b.ProjectID,
|
||||
b.BookTitle,
|
||||
b.Subtitle,
|
||||
b.Tagline,
|
||||
b.ShortDescription,
|
||||
b.Status,
|
||||
b.BookNumber,
|
||||
b.Description,
|
||||
b.TargetWordCount,
|
||||
b.WritingStartDate,
|
||||
b.TargetCompletionDate,
|
||||
b.PublicationDate,
|
||||
b.CoverThumbnailPath,
|
||||
b.CoverStandardPath,
|
||||
ISNULL(wordCounts.CurrentWordCount, 0) AS CurrentWordCount,
|
||||
ISNULL(counts.ChapterCount, 0) AS ChapterCount,
|
||||
ISNULL(counts.SceneCount, 0) AS SceneCount,
|
||||
b.SortOrder,
|
||||
b.CreatedDate,
|
||||
b.UpdatedDate,
|
||||
b.IsArchived,
|
||||
b.ArchivedDate,
|
||||
b.ArchivedReason
|
||||
FROM dbo.Books b
|
||||
OUTER APPLY
|
||||
(
|
||||
SELECT
|
||||
COUNT(DISTINCT c.ChapterID) AS ChapterCount,
|
||||
COUNT(s.SceneID) AS SceneCount
|
||||
FROM dbo.Chapters c
|
||||
LEFT JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
|
||||
WHERE c.BookID = b.BookID AND c.IsArchived = 0
|
||||
) counts
|
||||
OUTER APPLY
|
||||
(
|
||||
SELECT SUM(ISNULL(sw.ActualWordCount, 0)) AS CurrentWordCount
|
||||
FROM dbo.Chapters c
|
||||
INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE c.BookID = b.BookID AND c.IsArchived = 0
|
||||
) wordCounts
|
||||
WHERE b.ProjectID IN @ProjectIDs AND b.IsArchived = 0
|
||||
ORDER BY b.ProjectID, b.SortOrder, b.BookNumber, b.BookTitle;
|
||||
""",
|
||||
new { ProjectIDs = ids });
|
||||
"dbo.Book_ListByProjects",
|
||||
new { ProjectIDs = string.Join(',', ids) },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -2454,12 +2140,9 @@ public sealed class BookRepository(ISqlConnectionFactory connectionFactory) : IB
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
return await connection.QuerySingleOrDefaultAsync<Book>(
|
||||
"""
|
||||
SELECT BookID, ProjectID, CoverThumbnailPath, CoverStandardPath
|
||||
FROM dbo.Books
|
||||
WHERE BookID = @BookID;
|
||||
""",
|
||||
new { BookID = bookId });
|
||||
"dbo.Book_GetCoverPaths",
|
||||
new { BookID = bookId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task<int> SaveAsync(Book book)
|
||||
@ -2632,17 +2315,9 @@ public sealed class SceneRepository(ISqlConnectionFactory connectionFactory) : I
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
await connection.ExecuteAsync(
|
||||
"""
|
||||
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
|
||||
WHEN NOT MATCHED THEN
|
||||
INSERT (SceneID, MetricTypeID, Value, Notes)
|
||||
VALUES (@SceneID, @MetricTypeID, @Value, NULL);
|
||||
""",
|
||||
new { SceneID = sceneId, MetricTypeID = metricTypeId, Value = value });
|
||||
"dbo.SceneMetric_SaveValue",
|
||||
new { SceneID = sceneId, MetricTypeID = metricTypeId, Value = value, Notes = (string?)null },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task ArchiveAsync(int sceneId)
|
||||
@ -2824,8 +2499,9 @@ public sealed class TimelineRepository(ISqlConnectionFactory connectionFactory)
|
||||
}
|
||||
|
||||
var targetRows = await connection.QueryAsync<ThreadEventTargetRow>(
|
||||
"SELECT PlotEventID, PlotLineID FROM dbo.PlotEventTargetPlotLines WHERE PlotEventID IN @ThreadEventIDs;",
|
||||
new { ThreadEventIDs = threadEventIds });
|
||||
"dbo.ThreadEventTarget_ListByEvents",
|
||||
new { ThreadEventIDs = string.Join(',', threadEventIds) },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
var targetsByEvent = targetRows
|
||||
.GroupBy(x => x.PlotEventID)
|
||||
.ToDictionary(x => x.Key, x => (IReadOnlyList<int>)x.Select(row => row.PlotLineID).ToList());
|
||||
@ -3159,8 +2835,9 @@ public sealed class PlotRepository(ISqlConnectionFactory connectionFactory) : IP
|
||||
}
|
||||
|
||||
var targetRows = await connection.QueryAsync<ThreadEventTargetRow>(
|
||||
"SELECT PlotEventID, PlotLineID FROM dbo.PlotEventTargetPlotLines WHERE PlotEventID IN @ThreadEventIDs;",
|
||||
new { ThreadEventIDs = threadEventIds });
|
||||
"dbo.ThreadEventTarget_ListByEvents",
|
||||
new { ThreadEventIDs = string.Join(',', threadEventIds) },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
var targetsByEvent = targetRows
|
||||
.GroupBy(x => x.PlotEventID)
|
||||
.ToDictionary(x => x.Key, x => (IReadOnlyList<int>)x.Select(row => row.PlotLineID).ToList());
|
||||
@ -3204,17 +2881,9 @@ public sealed class PlotRepository(ISqlConnectionFactory connectionFactory) : IP
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var targetIds = plotLineIds.Where(id => id > 0).Distinct().ToList();
|
||||
await connection.ExecuteAsync(
|
||||
"DELETE FROM dbo.PlotEventTargetPlotLines WHERE PlotEventID = @ThreadEventID;",
|
||||
new { ThreadEventID = threadEventId });
|
||||
|
||||
if (targetIds.Count == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await connection.ExecuteAsync(
|
||||
"INSERT dbo.PlotEventTargetPlotLines (PlotEventID, PlotLineID) VALUES (@ThreadEventID, @PlotLineID);",
|
||||
targetIds.Select(plotLineId => new { ThreadEventID = threadEventId, PlotLineID = plotLineId }));
|
||||
"dbo.ThreadEventTarget_SaveForEvent",
|
||||
new { ThreadEventID = threadEventId, PlotLineIDs = string.Join(',', targetIds) },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task DeleteThreadEventAsync(int threadEventId)
|
||||
@ -3387,26 +3056,9 @@ public sealed class AssetRepository(ISqlConnectionFactory connectionFactory) : I
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<AssetCustodyCharacter>(
|
||||
"""
|
||||
SELECT acec.AssetCustodyEventID,
|
||||
ace.StoryAssetID,
|
||||
ace.SceneID,
|
||||
acec.CharacterID,
|
||||
c.CharacterName,
|
||||
acec.CharacterNameText,
|
||||
cr.RoleName,
|
||||
ace.UpdatedDate
|
||||
FROM dbo.AssetCustodyEventCharacters acec
|
||||
INNER JOIN dbo.AssetCustodyEvents ace ON ace.AssetCustodyEventID = acec.AssetCustodyEventID
|
||||
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID
|
||||
LEFT JOIN dbo.Characters c ON c.CharacterID = acec.CharacterID
|
||||
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
|
||||
WHERE sa.ProjectID = @ProjectID
|
||||
AND sa.IsArchived = 0
|
||||
AND acec.CharacterID IS NOT NULL
|
||||
AND NULLIF(LTRIM(RTRIM(c.CharacterName)), N'') IS NOT NULL;
|
||||
""",
|
||||
new { ProjectID = projectId });
|
||||
"dbo.AssetCustodyCharacter_ListByProject",
|
||||
new { ProjectID = projectId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -3419,18 +3071,9 @@ public sealed class AssetRepository(ISqlConnectionFactory connectionFactory) : I
|
||||
|
||||
var eventIds = rows.Select(x => x.AssetCustodyEventID).Distinct().ToList();
|
||||
var validCustodians = await connection.QueryAsync<AssetCustodySummaryRow>(
|
||||
"""
|
||||
SELECT acec.AssetCustodyEventID,
|
||||
c.CharacterName,
|
||||
cr.RoleName
|
||||
FROM dbo.AssetCustodyEventCharacters acec
|
||||
INNER JOIN dbo.Characters c ON c.CharacterID = acec.CharacterID
|
||||
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
|
||||
WHERE acec.AssetCustodyEventID IN @AssetCustodyEventIDs
|
||||
AND acec.CharacterID IS NOT NULL
|
||||
AND NULLIF(LTRIM(RTRIM(c.CharacterName)), N'') IS NOT NULL;
|
||||
""",
|
||||
new { AssetCustodyEventIDs = eventIds });
|
||||
"dbo.AssetCustodySummary_ListByEvents",
|
||||
new { AssetCustodyEventIDs = string.Join(',', eventIds) },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
|
||||
var summaries = validCustodians
|
||||
.GroupBy(x => x.AssetCustodyEventID)
|
||||
@ -3565,14 +3208,9 @@ public sealed class CharacterRepository(ISqlConnectionFactory connectionFactory)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<CharacterSexValue>(
|
||||
"""
|
||||
SELECT CharacterSexValueID, OwnerUserID, SexName, IsSystem, SortOrder, IsActive
|
||||
FROM dbo.CharacterSexValues
|
||||
WHERE IsActive = 1
|
||||
AND (IsSystem = 1 OR OwnerUserID = @OwnerUserID)
|
||||
ORDER BY IsSystem DESC, SortOrder, SexName;
|
||||
""",
|
||||
new { OwnerUserID = ownerUserId });
|
||||
"dbo.CharacterSexValue_ListForOwner",
|
||||
new { OwnerUserID = ownerUserId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -3580,14 +3218,9 @@ public sealed class CharacterRepository(ISqlConnectionFactory connectionFactory)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
return await connection.QuerySingleOrDefaultAsync<CharacterSexValue>(
|
||||
"""
|
||||
SELECT CharacterSexValueID, OwnerUserID, SexName, IsSystem, SortOrder, IsActive
|
||||
FROM dbo.CharacterSexValues
|
||||
WHERE CharacterSexValueID = @CharacterSexValueID
|
||||
AND IsActive = 1
|
||||
AND (IsSystem = 1 OR OwnerUserID = @OwnerUserID);
|
||||
""",
|
||||
new { CharacterSexValueID = characterSexValueId, OwnerUserID = ownerUserId });
|
||||
"dbo.CharacterSexValue_Get",
|
||||
new { CharacterSexValueID = characterSexValueId, OwnerUserID = ownerUserId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task<int> GetOrCreateSexValueAsync(int ownerUserId, string sexName)
|
||||
@ -3627,13 +3260,9 @@ public sealed class CharacterRepository(ISqlConnectionFactory connectionFactory)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<CharacterImage>(
|
||||
"""
|
||||
SELECT CharacterImageID, CharacterID, ProjectID, ImagePath, ThumbnailPath, Caption, SortOrder, UploadedDateUTC, IsDeleted
|
||||
FROM dbo.CharacterImages
|
||||
WHERE CharacterID = @CharacterID AND IsDeleted = 0
|
||||
ORDER BY SortOrder, UploadedDateUTC, CharacterImageID;
|
||||
""",
|
||||
new { CharacterID = characterId });
|
||||
"dbo.CharacterImage_ListByCharacter",
|
||||
new { CharacterID = characterId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
@ -3641,26 +3270,16 @@ public sealed class CharacterRepository(ISqlConnectionFactory connectionFactory)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
return await connection.QuerySingleOrDefaultAsync<CharacterImage>(
|
||||
"""
|
||||
SELECT CharacterImageID, CharacterID, ProjectID, ImagePath, ThumbnailPath, Caption, SortOrder, UploadedDateUTC, IsDeleted
|
||||
FROM dbo.CharacterImages
|
||||
WHERE CharacterImageID = @CharacterImageID AND IsDeleted = 0;
|
||||
""",
|
||||
new { CharacterImageID = characterImageId });
|
||||
"dbo.CharacterImage_Get",
|
||||
new { CharacterImageID = characterImageId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task<int> CreateCharacterImageAsync(CharacterImage image)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
return await connection.QuerySingleAsync<int>(
|
||||
"""
|
||||
DECLARE @SortOrder int = COALESCE(@InputSortOrder, (SELECT ISNULL(MAX(SortOrder), 0) + 1 FROM dbo.CharacterImages WHERE CharacterID = @CharacterID));
|
||||
|
||||
INSERT dbo.CharacterImages (CharacterID, ProjectID, ImagePath, ThumbnailPath, Caption, SortOrder)
|
||||
VALUES (@CharacterID, @ProjectID, @ImagePath, @ThumbnailPath, @Caption, @SortOrder);
|
||||
|
||||
SELECT CAST(SCOPE_IDENTITY() AS int);
|
||||
""",
|
||||
"dbo.CharacterImage_Create",
|
||||
new
|
||||
{
|
||||
image.CharacterID,
|
||||
@ -3669,20 +3288,17 @@ public sealed class CharacterRepository(ISqlConnectionFactory connectionFactory)
|
||||
image.ThumbnailPath,
|
||||
image.Caption,
|
||||
InputSortOrder = image.SortOrder == 0 ? (int?)null : image.SortOrder
|
||||
});
|
||||
},
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task DeleteCharacterImageAsync(int characterImageId)
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
await connection.ExecuteAsync(
|
||||
"""
|
||||
UPDATE dbo.CharacterImages
|
||||
SET IsDeleted = 1,
|
||||
DeletedDateUTC = SYSUTCDATETIME()
|
||||
WHERE CharacterImageID = @CharacterImageID;
|
||||
""",
|
||||
new { CharacterImageID = characterImageId });
|
||||
"dbo.CharacterImage_Delete",
|
||||
new { CharacterImageID = characterImageId },
|
||||
commandType: CommandType.StoredProcedure);
|
||||
}
|
||||
|
||||
public async Task ArchiveCharacterAsync(int characterId)
|
||||
|
||||
@ -36,23 +36,9 @@ public sealed class WordCompanionRepository(ISqlConnectionFactory connectionFact
|
||||
{
|
||||
using var connection = connectionFactory.CreateConnection();
|
||||
var rows = await connection.QueryAsync<WordCompanionBookDto>(
|
||||
"""
|
||||
SELECT b.BookID AS BookId,
|
||||
b.BookTitle AS Title,
|
||||
b.Subtitle,
|
||||
b.SortOrder
|
||||
FROM dbo.Books b
|
||||
INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = b.ProjectID
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID
|
||||
WHERE b.ProjectID = @ProjectID
|
||||
AND pua.UserID = @UserID
|
||||
AND pua.IsActive = 1
|
||||
AND p.IsArchived = 0
|
||||
AND b.IsArchived = 0
|
||||
ORDER BY b.SortOrder, b.BookNumber, b.BookTitle, b.BookID;
|
||||
""",
|
||||
"dbo.WordCompanion_Book_ListByProject",
|
||||
new { ProjectID = projectId, UserID = userId },
|
||||
commandType: CommandType.Text);
|
||||
commandType: CommandType.StoredProcedure);
|
||||
return rows.ToList();
|
||||
}
|
||||
|
||||
|
||||
619
PlotLine/Sql/088_InlineSqlStoredProcedureRefactor.sql
Normal file
619
PlotLine/Sql/088_InlineSqlStoredProcedureRefactor.sql
Normal file
@ -0,0 +1,619 @@
|
||||
SET ANSI_NULLS ON;
|
||||
GO
|
||||
SET QUOTED_IDENTIFIER ON;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Project_ExistsByNameForUser
|
||||
@ProjectName nvarchar(200),
|
||||
@UserID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT COUNT(1)
|
||||
FROM dbo.Projects p
|
||||
INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = p.ProjectID
|
||||
WHERE p.IsArchived = 0
|
||||
AND p.ProjectName = @ProjectName
|
||||
AND pua.UserID = @UserID
|
||||
AND pua.IsActive = 1;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Scene_UpdatePovCharacter
|
||||
@SceneID int,
|
||||
@POVCharacterID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE dbo.Scenes
|
||||
SET POVCharacterID = @POVCharacterID,
|
||||
UpdatedDate = SYSUTCDATETIME()
|
||||
WHERE SceneID = @SceneID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Import_LookupIds
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT
|
||||
(SELECT TOP (1) RevisionStatusID FROM dbo.RevisionStatuses WHERE StatusName = N'Planned' ORDER BY SortOrder) AS RevisionStatusID,
|
||||
(SELECT TOP (1) TimeModeID FROM dbo.TimeModes WHERE TimeModeName = N'Relative' ORDER BY SortOrder) AS TimeModeID,
|
||||
(SELECT TOP (1) TimeConfidenceID FROM dbo.TimeConfidences WHERE TimeConfidenceName = N'Unknown' ORDER BY SortOrder) AS TimeConfidenceID;
|
||||
|
||||
SELECT LocationTypeID AS LookupID, TypeName FROM dbo.LocationTypes WHERE IsActive = 1;
|
||||
SELECT CharacterRoleInSceneTypeID AS LookupID, TypeName FROM dbo.CharacterRoleInSceneTypes WHERE IsActive = 1;
|
||||
SELECT PresenceTypeID AS LookupID, TypeName FROM dbo.PresenceTypes WHERE IsActive = 1;
|
||||
SELECT PlotLineTypeID AS LookupID, TypeName FROM dbo.PlotLineTypes WHERE IsActive = 1;
|
||||
SELECT PlotImportanceID AS LookupID, ImportanceName AS TypeName FROM dbo.PlotImportance WHERE IsActive = 1;
|
||||
SELECT ThreadTypeID AS LookupID, TypeName FROM dbo.ThreadTypes WHERE IsActive = 1;
|
||||
SELECT ThreadStatusID AS LookupID, StatusName FROM dbo.ThreadStatuses WHERE IsActive = 1;
|
||||
SELECT ThreadEventTypeID AS LookupID, TypeName FROM dbo.ThreadEventTypes WHERE IsActive = 1;
|
||||
SELECT SceneDependencyTypeID AS LookupID, TypeName FROM dbo.SceneDependencyTypes WHERE IsActive = 1;
|
||||
SELECT AssetKindID AS LookupID, KindName AS TypeName FROM dbo.AssetKinds WHERE IsActive = 1;
|
||||
SELECT AssetStateID AS LookupID, StateName AS TypeName FROM dbo.AssetStates WHERE IsActive = 1;
|
||||
SELECT AssetEventTypeID AS LookupID, TypeName FROM dbo.AssetEventTypes WHERE IsActive = 1;
|
||||
SELECT AssetCustodyEventTypeID AS LookupID, TypeName FROM dbo.AssetCustodyEventTypes WHERE IsActive = 1;
|
||||
SELECT CustodyRoleID AS LookupID, RoleName AS TypeName FROM dbo.CustodyRoles WHERE IsActive = 1;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ProjectBackup_BookMetadata_List
|
||||
@ProjectID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT BookID, Subtitle, Tagline, ShortDescription, Status, TargetWordCount, WritingStartDate, TargetCompletionDate, PublicationDate,
|
||||
CoverThumbnailPath, CoverStandardPath
|
||||
FROM dbo.Books
|
||||
WHERE ProjectID = @ProjectID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WordCompanion_Book_ListByProject
|
||||
@ProjectID int,
|
||||
@UserID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT b.BookID AS BookId,
|
||||
b.BookTitle AS Title,
|
||||
b.Subtitle,
|
||||
b.SortOrder
|
||||
FROM dbo.Books b
|
||||
INNER JOIN dbo.ProjectUserAccess pua ON pua.ProjectID = b.ProjectID
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = b.ProjectID
|
||||
WHERE b.ProjectID = @ProjectID
|
||||
AND pua.UserID = @UserID
|
||||
AND pua.IsActive = 1
|
||||
AND p.IsArchived = 0
|
||||
AND b.IsArchived = 0
|
||||
ORDER BY b.SortOrder, b.BookNumber, b.BookTitle, b.BookID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ContinuityWarning_ListDashboardUnresolved
|
||||
@ProjectID int,
|
||||
@Count int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT TOP (@Count)
|
||||
cw.ContinuityWarningID,
|
||||
cw.ValidationRunID,
|
||||
cw.ProjectID,
|
||||
cw.BookID,
|
||||
b.BookTitle,
|
||||
b.Subtitle AS BookSubtitle,
|
||||
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 cw.IsDismissed = 0
|
||||
AND cw.IsIntentional = 0
|
||||
ORDER BY ws.SortOrder DESC, cw.LastDetectedDate DESC, cw.ContinuityWarningID DESC;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingPlan_ListForManagement
|
||||
@UserID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT wp.WritingPlanID, wp.UserID, wp.ProjectID, p.ProjectName, wp.BookID, b.BookTitle, b.Subtitle AS BookSubtitle,
|
||||
wp.PlanName, wp.GoalType, wp.StartDate, wp.DeadlineDate, wp.AvailableDaysJson, wp.SessionLengthMinutes, wp.IsActive,
|
||||
SUM(CASE WHEN wsi.ScheduleStatus = N'Planned' THEN 1 ELSE 0 END) AS PlannedTaskCount,
|
||||
MAX(CASE WHEN wsi.ScheduleStatus = N'Planned' THEN wsi.ScheduledDate ELSE NULL END) AS ProjectedFinishDate
|
||||
FROM dbo.WritingPlans wp
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = wp.ProjectID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = wp.BookID
|
||||
LEFT JOIN dbo.WritingScheduleItems wsi ON wsi.WritingPlanID = wp.WritingPlanID
|
||||
WHERE wp.UserID = @UserID
|
||||
GROUP BY wp.WritingPlanID, wp.UserID, wp.ProjectID, p.ProjectName, wp.BookID, b.BookTitle, b.Subtitle,
|
||||
wp.PlanName, wp.GoalType, wp.StartDate, wp.DeadlineDate, wp.AvailableDaysJson, wp.SessionLengthMinutes, wp.IsActive
|
||||
ORDER BY wp.IsActive DESC, wp.DeadlineDate, wp.PlanName, wp.WritingPlanID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingScheduleScene_ListForPlanning
|
||||
@ProjectID int,
|
||||
@BookID int = NULL,
|
||||
@IncludeBlockedScenes bit = 0
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT s.SceneID, rs.StatusName AS RevisionStatusName, sw.EstimatedWordCount, sw.Priority,
|
||||
COALESCE(sw.IsBlocked, 0) AS IsBlocked, b.SortOrder AS BookSortOrder,
|
||||
c.SortOrder AS ChapterSortOrder, s.SortOrder AS SceneSortOrder
|
||||
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
|
||||
INNER JOIN dbo.RevisionStatuses rs ON rs.RevisionStatusID = s.RevisionStatusID
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE p.ProjectID = @ProjectID
|
||||
AND p.IsArchived = 0
|
||||
AND (@BookID IS NULL OR b.BookID = @BookID)
|
||||
AND rs.StatusName IN (N'Outlined', N'Needs Work', N'Revised')
|
||||
AND (@IncludeBlockedScenes = 1 OR COALESCE(sw.IsBlocked, 0) = 0)
|
||||
ORDER BY COALESCE(sw.Priority, 0) DESC, b.SortOrder, c.SortOrder, s.SortOrder, s.SceneID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingScheduleItem_DeleteByPlan
|
||||
@WritingPlanID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
DELETE FROM dbo.WritingScheduleItems
|
||||
WHERE WritingPlanID = @WritingPlanID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingPlanSummary_ListByUser
|
||||
@UserID int,
|
||||
@ProjectID int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT wp.WritingPlanID, wp.UserID, wp.ProjectID, p.ProjectName, wp.BookID, b.BookTitle, b.Subtitle AS BookSubtitle,
|
||||
wp.PlanName, wp.DeadlineDate, wp.IsActive
|
||||
FROM dbo.WritingPlans wp
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = wp.ProjectID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = wp.BookID
|
||||
WHERE wp.UserID = @UserID
|
||||
AND (@ProjectID IS NULL OR wp.ProjectID = @ProjectID)
|
||||
ORDER BY wp.IsActive DESC, wp.DeadlineDate, wp.PlanName, wp.WritingPlanID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingSchedulePreview_ListByPlan
|
||||
@WritingPlanID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT wsi.WritingScheduleItemID, wsi.WritingPlanID, wsi.SceneID, wsi.ScheduledDate, wsi.TaskType,
|
||||
b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterNumber, c.ChapterTitle, s.SceneNumber, s.SceneTitle,
|
||||
sw.Priority, wsi.EstimatedMinutes, wsi.TargetWords, wsi.ScheduleStatus, wsi.Notes,
|
||||
COALESCE(sw.IsBlocked, 0) AS IsBlocked
|
||||
FROM dbo.WritingScheduleItems wsi
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = wsi.SceneID
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE wsi.WritingPlanID = @WritingPlanID
|
||||
ORDER BY wsi.ScheduledDate, wsi.WritingScheduleItemID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingScheduleDashboard_ListActive
|
||||
@UserID int,
|
||||
@ProjectID int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT wsi.WritingScheduleItemID, wsi.WritingPlanID, wp.PlanName, wsi.SceneID, wsi.ScheduledDate, wsi.TaskType,
|
||||
b.BookTitle, b.Subtitle AS BookSubtitle, c.ChapterNumber, c.ChapterTitle, c.SortOrder AS ChapterSortOrder,
|
||||
s.SceneNumber, s.SceneTitle, s.SortOrder AS SceneSortOrder, sw.Priority, wsi.EstimatedMinutes,
|
||||
wsi.TargetWords, wsi.ScheduleStatus, COALESCE(sw.IsBlocked, 0) AS IsBlocked
|
||||
FROM dbo.WritingPlans wp
|
||||
INNER JOIN dbo.WritingScheduleItems wsi ON wsi.WritingPlanID = wp.WritingPlanID
|
||||
INNER JOIN dbo.Scenes s ON s.SceneID = wsi.SceneID
|
||||
INNER JOIN dbo.Chapters c ON c.ChapterID = s.ChapterID
|
||||
INNER JOIN dbo.Books b ON b.BookID = c.BookID
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE wp.UserID = @UserID
|
||||
AND wp.IsActive = 1
|
||||
AND (@ProjectID IS NULL OR wp.ProjectID = @ProjectID)
|
||||
ORDER BY wsi.ScheduledDate, wsi.WritingScheduleItemID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingScheduleItem_UpdatePlannedStatusForUser
|
||||
@WritingScheduleItemID int,
|
||||
@UserID int,
|
||||
@ScheduleStatus nvarchar(50),
|
||||
@CompletedDateUtc datetime2 = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE wsi
|
||||
SET ScheduleStatus = @ScheduleStatus,
|
||||
CompletedDateUtc = @CompletedDateUtc,
|
||||
ModifiedDateUtc = SYSUTCDATETIME()
|
||||
FROM dbo.WritingScheduleItems wsi
|
||||
INNER JOIN dbo.WritingPlans wp ON wp.WritingPlanID = wsi.WritingPlanID
|
||||
WHERE wsi.WritingScheduleItemID = @WritingScheduleItemID
|
||||
AND wp.UserID = @UserID
|
||||
AND wp.IsActive = 1
|
||||
AND wsi.ScheduleStatus = N'Planned';
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingPlanSummary_ListRequiringRebalance
|
||||
@UserID int,
|
||||
@Today date,
|
||||
@ProjectID int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT wp.WritingPlanID, wp.UserID, wp.ProjectID, p.ProjectName, wp.BookID, b.BookTitle, b.Subtitle AS BookSubtitle,
|
||||
wp.PlanName, wp.DeadlineDate, wp.IsActive
|
||||
FROM dbo.WritingPlans wp
|
||||
INNER JOIN dbo.Projects p ON p.ProjectID = wp.ProjectID
|
||||
LEFT JOIN dbo.Books b ON b.BookID = wp.BookID
|
||||
WHERE wp.UserID = @UserID
|
||||
AND wp.IsActive = 1
|
||||
AND (@ProjectID IS NULL OR wp.ProjectID = @ProjectID)
|
||||
AND EXISTS
|
||||
(
|
||||
SELECT 1
|
||||
FROM dbo.WritingScheduleItems wsi
|
||||
WHERE wsi.WritingPlanID = wp.WritingPlanID
|
||||
AND wsi.ScheduleStatus = N'Planned'
|
||||
AND wsi.ScheduledDate < @Today
|
||||
)
|
||||
ORDER BY wp.DeadlineDate, wp.PlanName, wp.WritingPlanID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.WritingScheduleItem_UpdatePlannedDate
|
||||
@WritingPlanID int,
|
||||
@WritingScheduleItemID int,
|
||||
@ScheduledDate date
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE dbo.WritingScheduleItems
|
||||
SET ScheduledDate = @ScheduledDate,
|
||||
ModifiedDateUtc = SYSUTCDATETIME()
|
||||
WHERE WritingPlanID = @WritingPlanID
|
||||
AND WritingScheduleItemID = @WritingScheduleItemID
|
||||
AND ScheduleStatus = N'Planned';
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ProjectDashboard_Metrics
|
||||
@ProjectID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT
|
||||
(SELECT COUNT(1) FROM dbo.Characters WHERE ProjectID = @ProjectID AND IsArchived = 0) AS CharacterCount,
|
||||
(SELECT COUNT(1) FROM dbo.Locations WHERE ProjectID = @ProjectID AND IsArchived = 0) AS LocationCount,
|
||||
(
|
||||
SELECT COUNT(1)
|
||||
FROM dbo.PlotThreads pt
|
||||
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
|
||||
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
|
||||
WHERE pl.ProjectID = @ProjectID
|
||||
AND pl.IsArchived = 0
|
||||
AND pt.IsArchived = 0
|
||||
AND ts.IsOpenStatus = 1
|
||||
) AS ActivePlotThreadCount,
|
||||
(
|
||||
SELECT COUNT(1)
|
||||
FROM dbo.ContinuityWarnings
|
||||
WHERE ProjectID = @ProjectID
|
||||
AND IsDismissed = 0
|
||||
AND IsIntentional = 0
|
||||
) AS ContinuityWarningCount;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ProjectDashboard_AttentionThreads
|
||||
@ProjectID int,
|
||||
@Count int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT TOP (@Count) pt.PlotThreadID, pt.ThreadTitle, pt.PlotLineID, pl.PlotLineName,
|
||||
pt.Importance, ts.StatusName AS ThreadStatusName, ts.IsOpenStatus, ts.IsResolvedStatus
|
||||
FROM dbo.PlotThreads pt
|
||||
INNER JOIN dbo.PlotLines pl ON pl.PlotLineID = pt.PlotLineID
|
||||
INNER JOIN dbo.ThreadStatuses ts ON ts.ThreadStatusID = pt.ThreadStatusID
|
||||
WHERE pl.ProjectID = @ProjectID
|
||||
AND pl.IsArchived = 0
|
||||
AND pt.IsArchived = 0
|
||||
AND (ts.StatusName = N'Dormant' OR (ts.IsOpenStatus = 1 AND pt.Importance >= 8))
|
||||
ORDER BY CASE WHEN ts.StatusName = N'Dormant' THEN 0 ELSE 1 END,
|
||||
pt.Importance DESC, pt.UpdatedDate DESC, pt.PlotThreadID DESC;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ProjectActivity_ListRecent
|
||||
@ProjectID int,
|
||||
@Count int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT TOP (@Count) pa.ProjectActivityID, pa.ProjectID, pa.UserID, u.DisplayName, u.Email,
|
||||
pa.ActivityType, pa.EntityType, pa.EntityID, pa.EntityName, pa.Description, pa.CreatedDateUTC
|
||||
FROM dbo.ProjectActivity pa
|
||||
LEFT JOIN dbo.AppUser u ON u.UserID = pa.UserID
|
||||
WHERE pa.ProjectID = @ProjectID
|
||||
ORDER BY pa.CreatedDateUTC DESC, pa.ProjectActivityID DESC;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Book_ListByProjects
|
||||
@ProjectIDs nvarchar(max)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
;WITH ProjectIDs AS
|
||||
(
|
||||
SELECT DISTINCT TRY_CONVERT(int, value) AS ProjectID
|
||||
FROM STRING_SPLIT(COALESCE(@ProjectIDs, N''), N',')
|
||||
WHERE TRY_CONVERT(int, value) IS NOT NULL
|
||||
)
|
||||
SELECT b.BookID, b.ProjectID, b.BookTitle, b.Subtitle, b.Tagline, b.ShortDescription, b.Status, b.BookNumber,
|
||||
b.Description, b.TargetWordCount, b.WritingStartDate, b.TargetCompletionDate, b.PublicationDate,
|
||||
b.CoverThumbnailPath, b.CoverStandardPath, ISNULL(wordCounts.CurrentWordCount, 0) AS CurrentWordCount,
|
||||
ISNULL(counts.ChapterCount, 0) AS ChapterCount, ISNULL(counts.SceneCount, 0) AS SceneCount,
|
||||
b.SortOrder, b.CreatedDate, b.UpdatedDate, b.IsArchived, b.ArchivedDate, b.ArchivedReason
|
||||
FROM dbo.Books b
|
||||
INNER JOIN ProjectIDs ids ON ids.ProjectID = b.ProjectID
|
||||
OUTER APPLY
|
||||
(
|
||||
SELECT COUNT(DISTINCT c.ChapterID) AS ChapterCount, COUNT(s.SceneID) AS SceneCount
|
||||
FROM dbo.Chapters c
|
||||
LEFT JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
|
||||
WHERE c.BookID = b.BookID AND c.IsArchived = 0
|
||||
) counts
|
||||
OUTER APPLY
|
||||
(
|
||||
SELECT SUM(ISNULL(sw.ActualWordCount, 0)) AS CurrentWordCount
|
||||
FROM dbo.Chapters c
|
||||
INNER JOIN dbo.Scenes s ON s.ChapterID = c.ChapterID AND s.IsArchived = 0
|
||||
LEFT JOIN dbo.SceneWorkflow sw ON sw.SceneID = s.SceneID
|
||||
WHERE c.BookID = b.BookID AND c.IsArchived = 0
|
||||
) wordCounts
|
||||
WHERE b.IsArchived = 0
|
||||
ORDER BY b.ProjectID, b.SortOrder, b.BookNumber, b.BookTitle;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.Book_GetCoverPaths
|
||||
@BookID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT BookID, ProjectID, CoverThumbnailPath, CoverStandardPath
|
||||
FROM dbo.Books
|
||||
WHERE BookID = @BookID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ThreadEventTarget_ListByEvents
|
||||
@ThreadEventIDs nvarchar(max)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
;WITH ThreadEventIDs AS
|
||||
(
|
||||
SELECT DISTINCT TRY_CONVERT(int, value) AS ThreadEventID
|
||||
FROM STRING_SPLIT(COALESCE(@ThreadEventIDs, N''), N',')
|
||||
WHERE TRY_CONVERT(int, value) IS NOT NULL
|
||||
)
|
||||
SELECT petpl.PlotEventID, petpl.PlotLineID
|
||||
FROM dbo.PlotEventTargetPlotLines petpl
|
||||
INNER JOIN ThreadEventIDs ids ON ids.ThreadEventID = petpl.PlotEventID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.ThreadEventTarget_SaveForEvent
|
||||
@ThreadEventID int,
|
||||
@PlotLineIDs nvarchar(max) = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
DELETE FROM dbo.PlotEventTargetPlotLines
|
||||
WHERE PlotEventID = @ThreadEventID;
|
||||
|
||||
;WITH PlotLineIDs AS
|
||||
(
|
||||
SELECT DISTINCT TRY_CONVERT(int, value) AS PlotLineID
|
||||
FROM STRING_SPLIT(COALESCE(@PlotLineIDs, N''), N',')
|
||||
WHERE TRY_CONVERT(int, value) IS NOT NULL
|
||||
AND TRY_CONVERT(int, value) > 0
|
||||
)
|
||||
INSERT dbo.PlotEventTargetPlotLines (PlotEventID, PlotLineID)
|
||||
SELECT @ThreadEventID, PlotLineID
|
||||
FROM PlotLineIDs;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.AssetCustodyCharacter_ListByProject
|
||||
@ProjectID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT acec.AssetCustodyEventID, ace.StoryAssetID, ace.SceneID, acec.CharacterID, c.CharacterName,
|
||||
acec.CharacterNameText, cr.RoleName, ace.UpdatedDate
|
||||
FROM dbo.AssetCustodyEventCharacters acec
|
||||
INNER JOIN dbo.AssetCustodyEvents ace ON ace.AssetCustodyEventID = acec.AssetCustodyEventID
|
||||
INNER JOIN dbo.StoryAssets sa ON sa.StoryAssetID = ace.StoryAssetID
|
||||
LEFT JOIN dbo.Characters c ON c.CharacterID = acec.CharacterID
|
||||
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
|
||||
WHERE sa.ProjectID = @ProjectID
|
||||
AND sa.IsArchived = 0
|
||||
AND acec.CharacterID IS NOT NULL
|
||||
AND NULLIF(LTRIM(RTRIM(c.CharacterName)), N'') IS NOT NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.AssetCustodySummary_ListByEvents
|
||||
@AssetCustodyEventIDs nvarchar(max)
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
;WITH AssetCustodyEventIDs AS
|
||||
(
|
||||
SELECT DISTINCT TRY_CONVERT(int, value) AS AssetCustodyEventID
|
||||
FROM STRING_SPLIT(COALESCE(@AssetCustodyEventIDs, N''), N',')
|
||||
WHERE TRY_CONVERT(int, value) IS NOT NULL
|
||||
)
|
||||
SELECT acec.AssetCustodyEventID, c.CharacterName, cr.RoleName
|
||||
FROM dbo.AssetCustodyEventCharacters acec
|
||||
INNER JOIN AssetCustodyEventIDs ids ON ids.AssetCustodyEventID = acec.AssetCustodyEventID
|
||||
INNER JOIN dbo.Characters c ON c.CharacterID = acec.CharacterID
|
||||
LEFT JOIN dbo.CustodyRoles cr ON cr.CustodyRoleID = acec.CustodyRoleID
|
||||
WHERE acec.CharacterID IS NOT NULL
|
||||
AND NULLIF(LTRIM(RTRIM(c.CharacterName)), N'') IS NOT NULL;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.CharacterSexValue_ListForOwner
|
||||
@OwnerUserID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT CharacterSexValueID, OwnerUserID, SexName, IsSystem, SortOrder, IsActive
|
||||
FROM dbo.CharacterSexValues
|
||||
WHERE IsActive = 1
|
||||
AND (IsSystem = 1 OR OwnerUserID = @OwnerUserID)
|
||||
ORDER BY IsSystem DESC, SortOrder, SexName;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.CharacterSexValue_Get
|
||||
@CharacterSexValueID int,
|
||||
@OwnerUserID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT CharacterSexValueID, OwnerUserID, SexName, IsSystem, SortOrder, IsActive
|
||||
FROM dbo.CharacterSexValues
|
||||
WHERE CharacterSexValueID = @CharacterSexValueID
|
||||
AND IsActive = 1
|
||||
AND (IsSystem = 1 OR OwnerUserID = @OwnerUserID);
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.CharacterImage_ListByCharacter
|
||||
@CharacterID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT CharacterImageID, CharacterID, ProjectID, ImagePath, ThumbnailPath, Caption, SortOrder, UploadedDateUTC, IsDeleted
|
||||
FROM dbo.CharacterImages
|
||||
WHERE CharacterID = @CharacterID
|
||||
AND IsDeleted = 0
|
||||
ORDER BY SortOrder, UploadedDateUTC, CharacterImageID;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.CharacterImage_Get
|
||||
@CharacterImageID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
SELECT CharacterImageID, CharacterID, ProjectID, ImagePath, ThumbnailPath, Caption, SortOrder, UploadedDateUTC, IsDeleted
|
||||
FROM dbo.CharacterImages
|
||||
WHERE CharacterImageID = @CharacterImageID
|
||||
AND IsDeleted = 0;
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.CharacterImage_Create
|
||||
@CharacterID int,
|
||||
@ProjectID int,
|
||||
@ImagePath nvarchar(500),
|
||||
@ThumbnailPath nvarchar(500) = NULL,
|
||||
@Caption nvarchar(500) = NULL,
|
||||
@InputSortOrder int = NULL
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
DECLARE @SortOrder int = COALESCE(@InputSortOrder, (SELECT ISNULL(MAX(SortOrder), 0) + 1 FROM dbo.CharacterImages WHERE CharacterID = @CharacterID));
|
||||
|
||||
INSERT dbo.CharacterImages (CharacterID, ProjectID, ImagePath, ThumbnailPath, Caption, SortOrder)
|
||||
VALUES (@CharacterID, @ProjectID, @ImagePath, @ThumbnailPath, @Caption, @SortOrder);
|
||||
|
||||
SELECT CAST(SCOPE_IDENTITY() AS int);
|
||||
END;
|
||||
GO
|
||||
|
||||
CREATE OR ALTER PROCEDURE dbo.CharacterImage_Delete
|
||||
@CharacterImageID int
|
||||
AS
|
||||
BEGIN
|
||||
SET NOCOUNT ON;
|
||||
|
||||
UPDATE dbo.CharacterImages
|
||||
SET IsDeleted = 1,
|
||||
DeletedDateUTC = SYSUTCDATETIME()
|
||||
WHERE CharacterImageID = @CharacterImageID;
|
||||
END;
|
||||
GO
|
||||
Loading…
x
Reference in New Issue
Block a user