PlotDirector/PlotLine/Data/OnboardingBuildRepository.cs

286 lines
12 KiB
C#

using System.Data;
using System.Text.Json;
using Dapper;
using PlotLine.Models;
namespace PlotLine.Data;
public interface IOnboardingBuildRepository
{
Task<OnboardingManuscriptBuildResult?> BuildAsync(int userId, OnboardingManuscriptBuildRequest request);
Task<OnboardingManuscriptBuildResult?> GetByPreviewAsync(int userId, Guid previewId);
Task MarkMarkerSyncSentAsync(int buildId);
Task MarkMarkerSyncCompletedAsync(int buildId, string markerStatus, string message);
Task MarkMarkerSyncUserSaveRequiredAsync(int buildId, string message);
Task MarkMarkerSyncFailedAsync(int buildId, string message, string? diagnostics = null);
Task SaveStructuralChapterSourceAsync(StructuralChapterSourceSaveRequest request);
Task SaveSceneManuscriptSourceAsync(SceneManuscriptSourceSaveRequest request);
Task SaveSceneStructuralSummaryAsync(int sceneId, string structuralSummary);
Task<IReadOnlyList<SceneManuscriptSourceRecord>> ListSceneManuscriptSourcesByChapterAsync(int chapterId);
Task<IReadOnlyList<SceneManuscriptSourceRecord>> ListSceneManuscriptSourcesByBookAsync(int bookId);
}
public sealed class OnboardingBuildRepository(ISqlConnectionFactory connectionFactory) : IOnboardingBuildRepository
{
public async Task<OnboardingManuscriptBuildResult?> BuildAsync(int userId, OnboardingManuscriptBuildRequest request)
{
using var connection = connectionFactory.CreateConnection();
using var result = await connection.QueryMultipleAsync(
"dbo.Onboarding_ManuscriptBuild_Create",
new
{
UserID = userId,
request.PreviewID,
request.ProjectID,
request.BookID,
request.DocumentGuid,
BindingVersion = 1,
ChaptersJson = JsonSerializer.Serialize(request.Chapters),
ScenesJson = JsonSerializer.Serialize(request.Scenes),
CharactersJson = JsonSerializer.Serialize(request.Characters)
},
commandType: CommandType.StoredProcedure);
return await ReadBuildResultAsync(result);
}
public async Task<OnboardingManuscriptBuildResult?> GetByPreviewAsync(int userId, Guid previewId)
{
using var connection = connectionFactory.CreateConnection();
using var result = await connection.QueryMultipleAsync(
"dbo.Onboarding_ManuscriptBuild_GetByPreview",
new { UserID = userId, PreviewID = previewId },
commandType: CommandType.StoredProcedure);
return await ReadBuildResultAsync(result);
}
private static async Task<OnboardingManuscriptBuildResult?> ReadBuildResultAsync(SqlMapper.GridReader result)
{
var header = await result.ReadSingleOrDefaultAsync<OnboardingManuscriptBuildResult>();
if (header is null)
{
return null;
}
var chapters = (await result.ReadAsync<OnboardingBuildChapterMap>()).ToList();
var scenes = (await result.ReadAsync<OnboardingBuildSceneMap>()).ToList();
var characters = (await result.ReadAsync<OnboardingBuildCharacterMap>()).ToList();
return new OnboardingManuscriptBuildResult
{
BuildID = header.BuildID,
PreviewID = header.PreviewID,
ProjectID = header.ProjectID,
BookID = header.BookID,
ManuscriptDocumentID = header.ManuscriptDocumentID,
Status = header.Status,
MarkerStatus = header.MarkerStatus,
ChaptersCreated = header.ChaptersCreated,
ScenesCreated = header.ScenesCreated,
CharactersCreated = header.CharactersCreated,
CharactersReused = header.CharactersReused,
SceneAppearancesCreated = header.SceneAppearancesCreated,
AlreadyBuilt = header.AlreadyBuilt,
Message = header.Message,
ChapterMappings = chapters,
SceneMappings = scenes,
CharacterMappings = characters
};
}
public async Task MarkMarkerSyncSentAsync(int buildId)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.Onboarding_ManuscriptBuild_MarkerSyncSent",
new { BuildID = buildId },
commandType: CommandType.StoredProcedure);
}
public async Task MarkMarkerSyncCompletedAsync(int buildId, string markerStatus, string message)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.Onboarding_ManuscriptBuild_MarkerSyncCompleted",
new { BuildID = buildId, MarkerStatus = markerStatus, Message = message },
commandType: CommandType.StoredProcedure);
}
public async Task MarkMarkerSyncUserSaveRequiredAsync(int buildId, string message)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.Onboarding_ManuscriptBuild_MarkerSyncUserSaveRequired",
new { BuildID = buildId, Message = message },
commandType: CommandType.StoredProcedure);
}
public async Task MarkMarkerSyncFailedAsync(int buildId, string message, string? diagnostics = null)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.Onboarding_ManuscriptBuild_MarkerSyncFailed",
new { BuildID = buildId, Message = message, Diagnostics = diagnostics },
commandType: CommandType.StoredProcedure);
}
public async Task SaveStructuralChapterSourceAsync(StructuralChapterSourceSaveRequest request)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.ManuscriptStructuralChapterSource_Save",
new
{
request.ChapterID,
request.ProjectID,
request.BookID,
request.SourceText,
request.SourceWordCount,
request.SourceCharacterCount,
request.ParagraphMapJson,
request.StructuralParsedJson,
request.DefaultPovName,
request.ExplicitNarratorName
},
commandType: CommandType.StoredProcedure);
}
public async Task SaveSceneManuscriptSourceAsync(SceneManuscriptSourceSaveRequest request)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.SceneManuscriptSource_Save",
new
{
request.SceneID,
request.ProjectID,
request.BookID,
request.ChapterID,
request.SourceText,
request.SourceWordCount,
request.SourceCharacterCount,
request.SourceStartParagraph,
request.SourceEndParagraph,
request.WordStartParagraphIndex,
request.WordEndParagraphIndex,
request.SourceKind
},
commandType: CommandType.StoredProcedure);
}
public async Task SaveSceneStructuralSummaryAsync(int sceneId, string structuralSummary)
{
using var connection = connectionFactory.CreateConnection();
await connection.ExecuteAsync(
"dbo.SceneStructuralSummary_Save",
new { SceneID = sceneId, StructuralSummary = structuralSummary },
commandType: CommandType.StoredProcedure);
}
public async Task<IReadOnlyList<SceneManuscriptSourceRecord>> ListSceneManuscriptSourcesByBookAsync(int bookId)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<SceneManuscriptSourceRecord>(
"dbo.SceneManuscriptSource_ListByBook",
new { BookID = bookId },
commandType: CommandType.StoredProcedure);
return rows.ToList();
}
public async Task<IReadOnlyList<SceneManuscriptSourceRecord>> ListSceneManuscriptSourcesByChapterAsync(int chapterId)
{
using var connection = connectionFactory.CreateConnection();
var rows = await connection.QueryAsync<SceneManuscriptSourceRecord>(
"dbo.SceneManuscriptSource_ListByChapter",
new { ChapterID = chapterId },
commandType: CommandType.StoredProcedure);
return rows.ToList();
}
}
public sealed class OnboardingManuscriptBuildRequest
{
public Guid PreviewID { get; init; }
public int ProjectID { get; init; }
public int BookID { get; init; }
public Guid DocumentGuid { get; init; }
public IReadOnlyList<OnboardingBuildChapterRequest> Chapters { get; init; } = [];
public IReadOnlyList<OnboardingBuildSceneRequest> Scenes { get; init; } = [];
public IReadOnlyList<OnboardingBuildCharacterRequest> Characters { get; init; } = [];
}
public sealed class OnboardingBuildChapterRequest
{
public string TemporaryChapterKey { get; init; } = string.Empty;
public string Title { get; init; } = string.Empty;
public int SortOrder { get; init; }
public int WordCount { get; init; }
}
public sealed class OnboardingBuildSceneRequest
{
public string TemporarySceneKey { get; init; } = string.Empty;
public string TemporaryChapterKey { get; init; } = string.Empty;
public string Title { get; init; } = string.Empty;
public string? SuggestedTitle { get; init; }
public int SortOrder { get; init; }
public int WordCount { get; init; }
}
public sealed class OnboardingBuildCharacterRequest
{
public string TemporaryCharacterKey { get; init; } = string.Empty;
public string Name { get; init; } = string.Empty;
public int? ExistingCharacterID { get; init; }
}
public sealed class StructuralChapterSourceSaveRequest
{
public int ChapterID { get; init; }
public int ProjectID { get; init; }
public int BookID { get; init; }
public string SourceText { get; init; } = string.Empty;
public int SourceWordCount { get; init; }
public int SourceCharacterCount { get; init; }
public string? ParagraphMapJson { get; init; }
public string? StructuralParsedJson { get; init; }
public string? DefaultPovName { get; init; }
public string? ExplicitNarratorName { get; init; }
}
public sealed class SceneManuscriptSourceSaveRequest
{
public int SceneID { get; init; }
public int ProjectID { get; init; }
public int BookID { get; init; }
public int ChapterID { get; init; }
public string SourceText { get; init; } = string.Empty;
public int SourceWordCount { get; init; }
public int SourceCharacterCount { get; init; }
public int? SourceStartParagraph { get; init; }
public int? SourceEndParagraph { get; init; }
public int? WordStartParagraphIndex { get; init; }
public int? WordEndParagraphIndex { get; init; }
public string SourceKind { get; init; } = "StructuralImport";
}
public sealed class SceneManuscriptSourceRecord
{
public int SceneID { get; init; }
public decimal SceneNumber { get; init; }
public string? SceneTitle { get; init; }
public string? SceneTitleSource { get; init; }
public int ProjectID { get; init; }
public int BookID { get; init; }
public int ChapterID { get; init; }
public string SourceText { get; init; } = string.Empty;
public int SourceWordCount { get; init; }
public int SourceCharacterCount { get; init; }
public int? SourceStartParagraph { get; init; }
public int? SourceEndParagraph { get; init; }
public int? WordStartParagraphIndex { get; init; }
public int? WordEndParagraphIndex { get; init; }
public string SourceKind { get; init; } = string.Empty;
public string? StructuralSummary { get; init; }
}