2193 lines
104 KiB
C#
2193 lines
104 KiB
C#
using System.Data;
|
|
using Dapper;
|
|
using PlotLine.Models;
|
|
|
|
namespace PlotLine.Data;
|
|
|
|
public interface IProjectRepository
|
|
{
|
|
Task<IReadOnlyList<Project>> ListAsync();
|
|
Task<Project?> GetAsync(int projectId);
|
|
Task<IReadOnlyList<GenreMetricPreset>> ListGenreMetricPresetsAsync();
|
|
Task<int> SaveAsync(Project project);
|
|
Task<int> SaveAsync(Project project, string? genreMetricPresetKey);
|
|
Task ArchiveAsync(int projectId);
|
|
}
|
|
|
|
public interface IBookRepository
|
|
{
|
|
Task<IReadOnlyList<Book>> ListByProjectAsync(int projectId);
|
|
Task<Book?> GetAsync(int bookId);
|
|
Task<int> SaveAsync(Book book);
|
|
Task ArchiveAsync(int bookId);
|
|
}
|
|
|
|
public interface IChapterRepository
|
|
{
|
|
Task<IReadOnlyList<Chapter>> ListByBookAsync(int bookId);
|
|
Task<Chapter?> GetAsync(int chapterId);
|
|
Task<int> SaveAsync(Chapter chapter);
|
|
Task ArchiveAsync(int chapterId);
|
|
}
|
|
|
|
public interface ISceneRepository
|
|
{
|
|
Task<IReadOnlyList<Scene>> ListByChapterAsync(int chapterId);
|
|
Task<Scene?> GetAsync(int sceneId);
|
|
Task<int> SaveAsync(Scene scene);
|
|
Task SavePurposesAsync(int sceneId, IEnumerable<int> purposeIds);
|
|
Task SaveMetricValuesAsync(int sceneId, IEnumerable<SceneMetricValue> metricValues);
|
|
Task ArchiveAsync(int sceneId);
|
|
Task MoveAsync(int sceneId, string direction);
|
|
Task<IReadOnlyList<ChapterOption>> ListChapterOptionsAsync(int projectId);
|
|
Task<SceneMovePreview?> GetMovePreviewAsync(int sceneId, int targetChapterId, string position);
|
|
Task MoveToChapterAsync(int sceneId, int targetChapterId, string position, bool renumber);
|
|
Task MoveRelativeAsync(int sceneId, int anchorSceneId, string position);
|
|
Task RenumberChapterAsync(int chapterId);
|
|
}
|
|
|
|
public interface ILookupRepository
|
|
{
|
|
Task<LookupData> GetAllAsync();
|
|
}
|
|
|
|
public interface ITimelineRepository
|
|
{
|
|
Task<TimelineData> GetByProjectAsync(int projectId, int? bookId, bool includeMetrics, IEnumerable<int> metricTypeIds, bool includePlotLines);
|
|
}
|
|
|
|
public interface ITimelinePresetRepository
|
|
{
|
|
Task<IReadOnlyList<TimelineViewPreset>> ListAsync(int projectId, int? bookId);
|
|
Task<TimelineViewPreset?> GetAsync(int timelineViewPresetId);
|
|
Task<int> SaveAsync(TimelineViewPreset preset);
|
|
Task ArchiveAsync(int timelineViewPresetId);
|
|
}
|
|
|
|
public interface ITimelineSettingsRepository
|
|
{
|
|
Task<ProjectTimelineSettings?> GetAsync(int projectId);
|
|
Task<IReadOnlyList<SceneMetricType>> ListMetricTypesAsync(int projectId);
|
|
Task<IReadOnlyList<ProjectTimelineMetricSetting>> ListMetricSettingsAsync(int projectId);
|
|
Task SaveAsync(ProjectTimelineSettings settings, IEnumerable<int> metricTypeIds);
|
|
}
|
|
|
|
public interface ISceneMetricTypeRepository
|
|
{
|
|
Task<IReadOnlyList<SceneMetricType>> ListForManagementAsync(int projectId);
|
|
Task<SceneMetricType?> GetAsync(int metricTypeId, int projectId);
|
|
Task<int> SaveAsync(SceneMetricType metric);
|
|
Task<bool> SetActiveAsync(int metricTypeId, int projectId, bool isActive);
|
|
Task<bool> MoveAsync(int metricTypeId, int projectId, string direction);
|
|
Task AddMissingDefaultsAsync(int projectId);
|
|
Task<bool> RemoveUnusedAsync(int metricTypeId, int projectId);
|
|
Task<bool> HasProjectMetricsAsync(int projectId);
|
|
}
|
|
|
|
public interface IPlotRepository
|
|
{
|
|
Task<PlotLookupData> GetLookupsAsync();
|
|
Task<IReadOnlyList<PlotLineItem>> ListPlotLinesAsync(int projectId);
|
|
Task<PlotLineItem?> GetPlotLineAsync(int plotLineId);
|
|
Task<int> SavePlotLineAsync(PlotLineItem plotLine);
|
|
Task ArchivePlotLineAsync(int plotLineId);
|
|
Task<IReadOnlyList<PlotThread>> ListPlotThreadsByProjectAsync(int projectId);
|
|
Task<IReadOnlyList<PlotThread>> ListPlotThreadsByPlotLineAsync(int plotLineId);
|
|
Task<PlotThread?> GetPlotThreadAsync(int plotThreadId);
|
|
Task<int> SavePlotThreadAsync(PlotThread plotThread);
|
|
Task ArchivePlotThreadAsync(int plotThreadId);
|
|
Task<IReadOnlyList<ThreadEvent>> ListThreadEventsBySceneAsync(int sceneId);
|
|
Task<IReadOnlyList<ThreadEvent>> ListThreadEventsByPlotThreadAsync(int plotThreadId);
|
|
Task<int> SaveThreadEventAsync(ThreadEvent threadEvent);
|
|
Task DeleteThreadEventAsync(int threadEventId);
|
|
Task<IReadOnlyList<SceneOption>> ListSceneOptionsAsync(int projectId);
|
|
}
|
|
|
|
public interface IAssetRepository
|
|
{
|
|
Task<AssetLookupData> GetLookupsAsync();
|
|
Task<IReadOnlyList<StoryAsset>> ListAssetsAsync(int projectId);
|
|
Task<StoryAsset?> GetAssetAsync(int storyAssetId);
|
|
Task<int> SaveAssetAsync(StoryAsset asset);
|
|
Task ArchiveAssetAsync(int storyAssetId);
|
|
Task<IReadOnlyList<AssetEvent>> ListEventsBySceneAsync(int sceneId);
|
|
Task<IReadOnlyList<AssetEvent>> ListEventsByAssetAsync(int storyAssetId);
|
|
Task<int> SaveEventAsync(AssetEvent assetEvent);
|
|
Task DeleteEventAsync(int assetEventId);
|
|
Task<IReadOnlyList<AssetDependency>> ListDependenciesByAssetAsync(int storyAssetId);
|
|
Task<int> SaveDependencyAsync(AssetDependency dependency);
|
|
Task DeleteDependencyAsync(int assetDependencyId);
|
|
Task<IReadOnlyList<AssetCustodyEvent>> ListCustodyBySceneAsync(int sceneId);
|
|
Task<IReadOnlyList<AssetCustodyEvent>> ListCustodyByAssetAsync(int storyAssetId);
|
|
Task<int> SaveCustodyEventAsync(AssetCustodyEvent custodyEvent, string custodianNames, IEnumerable<int> custodianCharacterIds, int custodyRoleId);
|
|
Task DeleteCustodyEventAsync(int assetCustodyEventId);
|
|
Task<(IReadOnlyList<StoryAsset> Assets, IReadOnlyList<AssetEvent> Events)> GetTimelineAsync(int projectId, int? bookId);
|
|
Task<IReadOnlyList<AssetDependencyIssue>> CheckDependencyIssuesAsync(int storyAssetId, int sceneId, int? toStateId);
|
|
}
|
|
|
|
public interface ICharacterRepository
|
|
{
|
|
Task<CharacterLookupData> GetLookupsAsync();
|
|
Task<IReadOnlyList<Character>> ListCharactersAsync(int projectId);
|
|
Task<Character?> GetCharacterAsync(int characterId);
|
|
Task<int> SaveCharacterAsync(Character character);
|
|
Task ArchiveCharacterAsync(int characterId);
|
|
Task<IReadOnlyList<SceneCharacter>> ListSceneCharactersAsync(int sceneId);
|
|
Task<int> SaveSceneCharacterAsync(SceneCharacter sceneCharacter);
|
|
Task DeleteSceneCharacterAsync(int sceneCharacterId);
|
|
Task<IReadOnlyList<CharacterAttributeEvent>> ListAttributeEventsByCharacterAsync(int characterId);
|
|
Task<int> SaveAttributeEventAsync(CharacterAttributeEvent attributeEvent);
|
|
Task<IReadOnlyList<CharacterKnowledgeItem>> ListKnowledgeBySceneAsync(int sceneId);
|
|
Task<IReadOnlyList<CharacterKnowledgeItem>> ListKnowledgeByCharacterAsync(int characterId);
|
|
Task<int> SaveKnowledgeAsync(CharacterKnowledgeItem knowledge);
|
|
Task DeleteKnowledgeAsync(int characterKnowledgeId);
|
|
Task<IReadOnlyList<CharacterRelationship>> ListRelationshipsByProjectAsync(int projectId);
|
|
Task<IReadOnlyList<CharacterRelationship>> ListRelationshipsByCharacterAsync(int characterId);
|
|
Task<int> SaveRelationshipAsync(CharacterRelationship relationship);
|
|
Task ArchiveRelationshipAsync(int characterRelationshipId);
|
|
Task<IReadOnlyList<RelationshipEvent>> ListRelationshipEventsBySceneAsync(int sceneId);
|
|
Task<IReadOnlyList<RelationshipEvent>> ListRelationshipEventsByCharacterAsync(int characterId);
|
|
Task<int> SaveRelationshipEventAsync(RelationshipEvent relationshipEvent);
|
|
Task DeleteRelationshipEventAsync(int relationshipEventId);
|
|
Task<(IReadOnlyList<Character> Characters, IReadOnlyList<SceneCharacter> Appearances)> GetTimelineAsync(int projectId, int? bookId);
|
|
}
|
|
|
|
public interface ILocationRepository
|
|
{
|
|
Task<LocationLookupData> GetLookupsAsync();
|
|
Task<IReadOnlyList<LocationItem>> ListByProjectAsync(int projectId);
|
|
Task<LocationItem?> GetAsync(int locationId);
|
|
Task<int> SaveAsync(LocationItem location);
|
|
Task ArchiveAsync(int locationId);
|
|
Task<IReadOnlyList<LocationRelationship>> ListRelationshipsByProjectAsync(int projectId);
|
|
Task<IReadOnlyList<LocationRelationship>> ListRelationshipsByLocationAsync(int locationId);
|
|
Task<int> SaveRelationshipAsync(LocationRelationship relationship);
|
|
Task ArchiveRelationshipAsync(int locationRelationshipId);
|
|
Task<IReadOnlyList<SceneAssetLocation>> ListSceneAssetLocationsAsync(int sceneId);
|
|
Task<int> SaveSceneAssetLocationAsync(SceneAssetLocation sceneAssetLocation, bool updateCurrentLocation);
|
|
Task DeleteSceneAssetLocationAsync(int sceneAssetLocationId);
|
|
Task<IReadOnlyList<Scene>> ListScenesByLocationAsync(int locationId);
|
|
Task<IReadOnlyList<SceneCharacter>> ListCharactersByLocationAsync(int locationId);
|
|
Task<IReadOnlyList<SceneAssetLocation>> ListAssetsByLocationAsync(int locationId);
|
|
}
|
|
|
|
public interface IWarningRepository
|
|
{
|
|
Task<WarningLookupData> GetLookupsAsync();
|
|
Task<ContinuityValidationSummary> ValidateProjectAsync(int projectId);
|
|
Task<ContinuityValidationSummary> ValidateBookAsync(int bookId);
|
|
Task<ContinuityValidationSummary> ValidateSceneAsync(int sceneId);
|
|
Task<IReadOnlyList<ContinuityWarning>> ListAsync(int projectId, int? bookId, int? sceneId, int? warningTypeId, int? warningSeverityId, string? entityType, bool includeDismissed, bool includeIntentional);
|
|
Task<IReadOnlyList<ContinuityWarning>> ListBySceneAsync(int sceneId);
|
|
Task<IReadOnlyList<SceneWarningCount>> GetSceneCountsAsync(int projectId, int? bookId);
|
|
Task DismissAsync(int continuityWarningId);
|
|
Task MarkIntentionalAsync(int continuityWarningId);
|
|
Task RestoreAsync(int continuityWarningId);
|
|
Task FinaliseSceneDependencyWarningsAsync(int projectId, int? bookId);
|
|
}
|
|
|
|
public interface ISceneDependencyRepository
|
|
{
|
|
Task<SceneDependencyLookupData> GetLookupsAsync();
|
|
Task<IReadOnlyList<SceneDependency>> ListBySceneAsync(int sceneId);
|
|
Task<IReadOnlyList<SceneDependency>> ListByProjectAsync(int projectId, int? bookId);
|
|
Task<IReadOnlyList<SceneDependencyCount>> GetCountsAsync(int projectId, int? bookId);
|
|
Task<int> SaveAsync(SceneDependency dependency);
|
|
Task ArchiveAsync(int sceneDependencyId);
|
|
}
|
|
|
|
public interface IAnalyticsRepository
|
|
{
|
|
Task<AnalyticsData> GetStoryHealthAsync(int projectId, int? bookId, decimal? startChapterNumber, decimal? endChapterNumber, decimal? startSceneNumber, decimal? endSceneNumber);
|
|
}
|
|
|
|
public interface IStoryBibleRepository
|
|
{
|
|
Task<IReadOnlyList<StoryBibleCharacterSummary>> GetCharacterSummariesAsync(int projectId);
|
|
Task<IReadOnlyList<StoryBibleThreadSummary>> GetThreadSummariesAsync(int projectId);
|
|
Task<IReadOnlyList<StoryBibleAssetSummary>> GetAssetSummariesAsync(int projectId);
|
|
Task<IReadOnlyList<StoryBibleRelationshipSummary>> GetRelationshipSummariesAsync(int projectId);
|
|
Task<IReadOnlyList<StoryBibleLocationSummary>> GetLocationSummariesAsync(int projectId);
|
|
Task<IReadOnlyList<StoryBibleTimelineSummary>> GetTimelineSummaryAsync(int projectId);
|
|
Task<IReadOnlyList<StoryBibleAttentionItem>> GetOpenQuestionsAsync(int projectId);
|
|
Task<IReadOnlyList<StoryBibleRevisionItem>> GetRevisionProgressAsync(int projectId);
|
|
Task<IReadOnlyList<StoryBibleSearchResult>> SearchAsync(int projectId, string? query);
|
|
}
|
|
|
|
public interface IWriterWorkspaceRepository
|
|
{
|
|
Task<IReadOnlyList<SceneNoteType>> ListNoteTypesAsync();
|
|
Task<IReadOnlyList<SceneAttachmentType>> ListAttachmentTypesAsync();
|
|
Task<SceneWorkflow> GetWorkflowAsync(int sceneId);
|
|
Task SaveWorkflowAsync(SceneWorkflow workflow);
|
|
Task<IReadOnlyList<SceneNote>> ListNotesAsync(int sceneId);
|
|
Task<int> SaveNoteAsync(SceneNote note);
|
|
Task DeleteNoteAsync(int sceneNoteId);
|
|
Task<IReadOnlyList<SceneChecklistItem>> ListChecklistItemsAsync(int sceneId);
|
|
Task<int> SaveChecklistItemAsync(SceneChecklistItem item);
|
|
Task DeleteChecklistItemAsync(int sceneChecklistItemId);
|
|
Task<IReadOnlyList<SceneAttachment>> ListAttachmentsAsync(int sceneId);
|
|
Task<int> SaveAttachmentAsync(SceneAttachment attachment);
|
|
Task DeleteAttachmentAsync(int sceneAttachmentId);
|
|
Task<ChapterGoal> GetChapterGoalAsync(int chapterId);
|
|
Task SaveChapterGoalAsync(ChapterGoal goal);
|
|
Task<IReadOnlyList<ChapterWorkflowScene>> GetChapterWorkflowAsync(int chapterId);
|
|
Task<IReadOnlyList<WriterDashboardScene>> GetWriterDashboardScenesAsync(int? projectId, string queue);
|
|
Task<IReadOnlyList<StoryBibleAttentionItem>> GetWriterDashboardRemindersAsync(int? projectId);
|
|
}
|
|
|
|
public interface IExportRepository
|
|
{
|
|
Task<ExportPacket> GetStoryBibleAsync(int projectId);
|
|
Task<ExportPacket> GetNarrativeTimelineAsync(int projectId);
|
|
Task<ExportPacket> GetCharacterReferenceAsync(int characterId);
|
|
Task<ExportPacket> GetSceneBriefAsync(int sceneId);
|
|
Task<ExportPacket> GetChapterBriefAsync(int chapterId);
|
|
Task<ExportPacket> GetWriterSessionBriefAsync(int sceneId);
|
|
Task<ExportPacket> GetResearchPackAsync(int projectId);
|
|
Task<ExportPacket> GetSearchResultsAsync(int projectId, string query);
|
|
}
|
|
|
|
public interface IDynamicsRepository
|
|
{
|
|
Task<(Character? Character, IReadOnlyList<CharacterArcPoint> ArcPoints, IReadOnlyList<RelationshipEvent> RelationshipEvents, IReadOnlyList<CharacterKnowledgeItem> KnowledgeEvents, IReadOnlyList<DynamicsIssue> Issues)> GetCharacterArcAsync(int characterId);
|
|
Task<(CharacterRelationship? Relationship, IReadOnlyList<RelationshipProgressionPoint> Events, IReadOnlyList<DynamicsIssue> Issues)> GetRelationshipEvolutionAsync(int characterRelationshipId);
|
|
Task<(Character? Character, IReadOnlyList<CharacterKnowledgeItem> KnowledgeEvents, IReadOnlyList<DynamicsIssue> Issues)> GetKnowledgeTimelineAsync(int characterId);
|
|
Task<(IReadOnlyList<CharacterPairingSummary> Pairings, IReadOnlyList<DynamicsIssue> Issues)> GetStoryDynamicsAsync(int projectId);
|
|
Task RunContinuityChecksAsync(int projectId, int? bookId, int? sceneId);
|
|
}
|
|
|
|
public interface IScenarioRepository
|
|
{
|
|
Task<IReadOnlyList<Scenario>> ListAsync(int projectId);
|
|
Task<Scenario?> GetAsync(int scenarioId);
|
|
Task<int> CreateAsync(int projectId, int? bookId, string scenarioName, string? description);
|
|
Task<(IReadOnlyList<Book> Books, IReadOnlyList<Chapter> Chapters, IReadOnlyList<ScenarioScene> Scenes, IReadOnlyList<ScenarioWarning> Warnings)> GetTimelineAsync(int scenarioId);
|
|
Task MoveSceneAsync(int scenarioId, int sceneId, int? anchorSceneId, int? targetChapterId, string position);
|
|
Task<int> ValidateAsync(int scenarioId);
|
|
Task<(IReadOnlyList<ScenarioComparisonRow> MovedScenes, IReadOnlyList<ScenarioWarning> Warnings)> CompareAsync(int scenarioId);
|
|
Task ApplyAsync(int scenarioId);
|
|
Task ArchiveAsync(int scenarioId);
|
|
}
|
|
|
|
public interface IArchiveRepository
|
|
{
|
|
Task<IReadOnlyList<ArchivedItem>> ListAsync(int? projectId, string? entityType);
|
|
Task<IReadOnlyList<Project>> ListProjectFiltersAsync();
|
|
Task ArchiveAsync(string entityType, int entityId, string? reason);
|
|
Task RestoreAsync(string entityType, int entityId);
|
|
Task<ArchiveDeleteResult> DeleteForeverAsync(string entityType, int entityId, string confirmationText);
|
|
}
|
|
|
|
public interface IAcceptanceRepository
|
|
{
|
|
Task<IReadOnlyList<Phase1AcceptanceChecklistItem>> GetPhase1ChecklistAsync(int? projectId);
|
|
}
|
|
|
|
public sealed class TimelineData
|
|
{
|
|
public Project? Project { get; init; }
|
|
public IReadOnlyList<Book> Books { get; init; } = [];
|
|
public IReadOnlyList<Chapter> Chapters { get; init; } = [];
|
|
public IReadOnlyList<Scene> Scenes { get; init; } = [];
|
|
public IReadOnlyList<ScenePurposeLabel> PurposeLabels { get; init; } = [];
|
|
public IReadOnlyList<SceneMetricType> MetricTypes { get; init; } = [];
|
|
public IReadOnlyList<TimelineMetricPoint> MetricPoints { get; init; } = [];
|
|
public IReadOnlyList<PlotLineItem> PlotLines { get; init; } = [];
|
|
public IReadOnlyList<ThreadEvent> ThreadEvents { get; init; } = [];
|
|
}
|
|
|
|
public sealed class LocationRepository(ISqlConnectionFactory connectionFactory) : ILocationRepository
|
|
{
|
|
public async Task<LocationLookupData> GetLookupsAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.LocationLookup_All", commandType: CommandType.StoredProcedure);
|
|
return new LocationLookupData
|
|
{
|
|
LocationTypes = (await result.ReadAsync<LocationType>()).ToList(),
|
|
RelationshipTypes = (await result.ReadAsync<LocationRelationshipType>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<IReadOnlyList<LocationItem>> ListByProjectAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<LocationItem>("dbo.Location_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<LocationItem?> GetAsync(int locationId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<LocationItem>("dbo.Location_Get", new { LocationID = locationId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SaveAsync(LocationItem location)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.Location_Save",
|
|
new
|
|
{
|
|
location.LocationID,
|
|
location.ProjectID,
|
|
location.ParentLocationID,
|
|
location.LocationName,
|
|
location.LocationTypeID,
|
|
location.Description,
|
|
location.ShowInQuickAddBar
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveAsync(int locationId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Location_Archive", new { LocationID = locationId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<LocationRelationship>> ListRelationshipsByProjectAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<LocationRelationship>("dbo.LocationRelationship_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<LocationRelationship>> ListRelationshipsByLocationAsync(int locationId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<LocationRelationship>("dbo.LocationRelationship_ListByLocation", new { LocationID = locationId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveRelationshipAsync(LocationRelationship relationship)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.LocationRelationship_Save",
|
|
new
|
|
{
|
|
relationship.LocationRelationshipID,
|
|
relationship.FromLocationID,
|
|
relationship.ToLocationID,
|
|
relationship.LocationRelationshipTypeID,
|
|
relationship.IsBidirectional,
|
|
relationship.Strength,
|
|
relationship.CanHearNormalSpeech,
|
|
relationship.CanHearLoudNoise,
|
|
relationship.CanSeeMovement,
|
|
relationship.CanSeeClearly,
|
|
relationship.CanTravelDirectly,
|
|
relationship.Notes
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveRelationshipAsync(int locationRelationshipId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.LocationRelationship_Archive", new { LocationRelationshipID = locationRelationshipId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneAssetLocation>> ListSceneAssetLocationsAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneAssetLocation>("dbo.SceneAssetLocation_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveSceneAssetLocationAsync(SceneAssetLocation sceneAssetLocation, bool updateCurrentLocation)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.SceneAssetLocation_Save",
|
|
new
|
|
{
|
|
sceneAssetLocation.SceneAssetLocationID,
|
|
sceneAssetLocation.SceneID,
|
|
sceneAssetLocation.StoryAssetID,
|
|
sceneAssetLocation.LocationID,
|
|
sceneAssetLocation.Description,
|
|
UpdateCurrentLocation = updateCurrentLocation
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteSceneAssetLocationAsync(int sceneAssetLocationId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.SceneAssetLocation_Delete", new { SceneAssetLocationID = sceneAssetLocationId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<Scene>> ListScenesByLocationAsync(int locationId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Scene>("dbo.LocationScene_ListByLocation", new { LocationID = locationId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneCharacter>> ListCharactersByLocationAsync(int locationId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneCharacter>("dbo.LocationCharacter_ListByLocation", new { LocationID = locationId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneAssetLocation>> ListAssetsByLocationAsync(int locationId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneAssetLocation>("dbo.SceneAssetLocation_ListByLocation", new { LocationID = locationId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
}
|
|
|
|
public sealed class WarningRepository(ISqlConnectionFactory connectionFactory) : IWarningRepository
|
|
{
|
|
public async Task<WarningLookupData> GetLookupsAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.WarningLookup_All", commandType: CommandType.StoredProcedure);
|
|
return new WarningLookupData
|
|
{
|
|
WarningTypes = (await result.ReadAsync<WarningType>()).ToList(),
|
|
Severities = (await result.ReadAsync<WarningSeverity>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<ContinuityValidationSummary> ValidateProjectAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<ContinuityValidationSummary>(
|
|
"dbo.ContinuityValidation_ValidateProject",
|
|
new { ProjectID = projectId, TriggeredBy = "User" },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<ContinuityValidationSummary> ValidateBookAsync(int bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<ContinuityValidationSummary>(
|
|
"dbo.ContinuityValidation_ValidateBook",
|
|
new { BookID = bookId, TriggeredBy = "User" },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<ContinuityValidationSummary> ValidateSceneAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<ContinuityValidationSummary>(
|
|
"dbo.ContinuityValidation_ValidateScene",
|
|
new { SceneID = sceneId, TriggeredBy = "User" },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ContinuityWarning>> ListAsync(int projectId, int? bookId, int? sceneId, int? warningTypeId, int? warningSeverityId, string? entityType, bool includeDismissed, bool includeIntentional)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ContinuityWarning>(
|
|
"dbo.ContinuityWarning_List",
|
|
new
|
|
{
|
|
ProjectID = projectId,
|
|
BookID = bookId,
|
|
SceneID = sceneId,
|
|
WarningTypeID = warningTypeId,
|
|
WarningSeverityID = warningSeverityId,
|
|
EntityType = string.IsNullOrWhiteSpace(entityType) ? null : entityType,
|
|
IncludeDismissed = includeDismissed,
|
|
IncludeIntentional = includeIntentional
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ContinuityWarning>> ListBySceneAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ContinuityWarning>("dbo.ContinuityWarning_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneWarningCount>> GetSceneCountsAsync(int projectId, int? bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneWarningCount>("dbo.ContinuityWarning_SceneCounts", new { ProjectID = projectId, BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task DismissAsync(int continuityWarningId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.ContinuityWarning_Dismiss", new { ContinuityWarningID = continuityWarningId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task MarkIntentionalAsync(int continuityWarningId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.ContinuityWarning_MarkIntentional", new { ContinuityWarningID = continuityWarningId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task RestoreAsync(int continuityWarningId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.ContinuityWarning_Restore", new { ContinuityWarningID = continuityWarningId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task FinaliseSceneDependencyWarningsAsync(int projectId, int? bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.SceneDependency_FinaliseWarnings", new { ProjectID = projectId, BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class AnalyticsData
|
|
{
|
|
public IReadOnlyList<AnalyticsMetricPoint> MetricPoints { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsRevisionSummary> RevisionSummary { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsRevisionScene> RevisionAttentionScenes { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsPurposeSummary> PurposeSummary { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsSceneGap> SceneGaps { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsThreadHealth> ThreadHealth { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsAssetHealth> AssetHealth { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsWarningSeverityCount> WarningSeverityCounts { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsWarningTypeCount> WarningTypeCounts { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsWarningHotspot> WarningHotspots { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsPovSummary> PovSummary { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsMultiPovChapter> MultiPovChapters { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsCharacterAppearance> CharacterAppearances { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsBusyScene> BusyScenes { get; init; } = [];
|
|
public IReadOnlyList<AnalyticsLocationUsage> LocationUsage { get; init; } = [];
|
|
}
|
|
|
|
public sealed class AnalyticsRepository(ISqlConnectionFactory connectionFactory) : IAnalyticsRepository
|
|
{
|
|
public async Task<AnalyticsData> GetStoryHealthAsync(int projectId, int? bookId, decimal? startChapterNumber, decimal? endChapterNumber, decimal? startSceneNumber, decimal? endSceneNumber)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync(
|
|
"dbo.Analytics_StoryHealth",
|
|
new
|
|
{
|
|
ProjectID = projectId,
|
|
BookID = bookId,
|
|
StartChapterNumber = startChapterNumber,
|
|
EndChapterNumber = endChapterNumber,
|
|
StartSceneNumber = startSceneNumber,
|
|
EndSceneNumber = endSceneNumber
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
|
|
return new AnalyticsData
|
|
{
|
|
MetricPoints = (await result.ReadAsync<AnalyticsMetricPoint>()).ToList(),
|
|
RevisionSummary = (await result.ReadAsync<AnalyticsRevisionSummary>()).ToList(),
|
|
RevisionAttentionScenes = (await result.ReadAsync<AnalyticsRevisionScene>()).ToList(),
|
|
PurposeSummary = (await result.ReadAsync<AnalyticsPurposeSummary>()).ToList(),
|
|
SceneGaps = (await result.ReadAsync<AnalyticsSceneGap>()).ToList(),
|
|
ThreadHealth = (await result.ReadAsync<AnalyticsThreadHealth>()).ToList(),
|
|
AssetHealth = (await result.ReadAsync<AnalyticsAssetHealth>()).ToList(),
|
|
WarningSeverityCounts = (await result.ReadAsync<AnalyticsWarningSeverityCount>()).ToList(),
|
|
WarningTypeCounts = (await result.ReadAsync<AnalyticsWarningTypeCount>()).ToList(),
|
|
WarningHotspots = (await result.ReadAsync<AnalyticsWarningHotspot>()).ToList(),
|
|
PovSummary = (await result.ReadAsync<AnalyticsPovSummary>()).ToList(),
|
|
MultiPovChapters = (await result.ReadAsync<AnalyticsMultiPovChapter>()).ToList(),
|
|
CharacterAppearances = (await result.ReadAsync<AnalyticsCharacterAppearance>()).ToList(),
|
|
BusyScenes = (await result.ReadAsync<AnalyticsBusyScene>()).ToList(),
|
|
LocationUsage = (await result.ReadAsync<AnalyticsLocationUsage>()).ToList()
|
|
};
|
|
}
|
|
}
|
|
|
|
public sealed class StoryBibleRepository(ISqlConnectionFactory connectionFactory) : IStoryBibleRepository
|
|
{
|
|
public async Task<IReadOnlyList<StoryBibleCharacterSummary>> GetCharacterSummariesAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleCharacterSummary>("dbo.StoryBible_CharacterSummaries", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleThreadSummary>> GetThreadSummariesAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleThreadSummary>("dbo.StoryBible_ThreadSummaries", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleAssetSummary>> GetAssetSummariesAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleAssetSummary>("dbo.StoryBible_AssetSummaries", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleRelationshipSummary>> GetRelationshipSummariesAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleRelationshipSummary>("dbo.StoryBible_RelationshipSummaries", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleLocationSummary>> GetLocationSummariesAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleLocationSummary>("dbo.StoryBible_LocationSummaries", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleTimelineSummary>> GetTimelineSummaryAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleTimelineSummary>("dbo.StoryBible_TimelineSummary", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleAttentionItem>> GetOpenQuestionsAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleAttentionItem>("dbo.StoryBible_OpenQuestions", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleRevisionItem>> GetRevisionProgressAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleRevisionItem>("dbo.StoryBible_RevisionProgress", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleSearchResult>> SearchAsync(int projectId, string? query)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return (await connection.QueryAsync<StoryBibleSearchResult>("dbo.StoryBible_Search", new { ProjectID = projectId, Query = query }, commandType: CommandType.StoredProcedure)).ToList();
|
|
}
|
|
}
|
|
|
|
public sealed class WriterWorkspaceRepository(ISqlConnectionFactory connectionFactory) : IWriterWorkspaceRepository
|
|
{
|
|
public async Task<IReadOnlyList<SceneNoteType>> ListNoteTypesAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneNoteType>("dbo.SceneNoteType_List", commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneAttachmentType>> ListAttachmentTypesAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneAttachmentType>("dbo.SceneAttachmentType_List", commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<SceneWorkflow> GetWorkflowAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<SceneWorkflow>("dbo.SceneWorkflow_Get", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task SaveWorkflowAsync(SceneWorkflow workflow)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.SceneWorkflow_Save",
|
|
new
|
|
{
|
|
workflow.SceneID,
|
|
workflow.DraftStatus,
|
|
workflow.EstimatedWordCount,
|
|
workflow.ActualWordCount,
|
|
workflow.DraftedDate,
|
|
workflow.LastWorkedOn,
|
|
workflow.ReadyForDraft,
|
|
workflow.ReadyForRevision,
|
|
workflow.ReadyForPolish,
|
|
workflow.IsBlocked,
|
|
workflow.BlockedReason,
|
|
workflow.Priority
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneNote>> ListNotesAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneNote>("dbo.SceneNote_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveNoteAsync(SceneNote note)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.SceneNote_Save",
|
|
new { note.SceneNoteID, note.SceneID, note.SceneNoteTypeID, note.NoteTitle, note.NoteText, note.SortOrder, note.IsPinned, note.IsResolved },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteNoteAsync(int sceneNoteId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.SceneNote_Delete", new { SceneNoteID = sceneNoteId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneChecklistItem>> ListChecklistItemsAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneChecklistItem>("dbo.SceneChecklist_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveChecklistItemAsync(SceneChecklistItem item)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.SceneChecklist_Save",
|
|
new { item.SceneChecklistItemID, item.SceneID, item.ItemText, item.IsCompleted, item.SortOrder },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteChecklistItemAsync(int sceneChecklistItemId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.SceneChecklist_Delete", new { SceneChecklistItemID = sceneChecklistItemId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneAttachment>> ListAttachmentsAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneAttachment>("dbo.SceneAttachment_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveAttachmentAsync(SceneAttachment attachment)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.SceneAttachment_Save",
|
|
new { attachment.SceneAttachmentID, attachment.SceneID, attachment.SceneAttachmentTypeID, attachment.Title, attachment.FilePath, attachment.ExternalUrl, attachment.Notes },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteAttachmentAsync(int sceneAttachmentId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.SceneAttachment_Delete", new { SceneAttachmentID = sceneAttachmentId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<ChapterGoal> GetChapterGoalAsync(int chapterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<ChapterGoal>("dbo.ChapterGoal_Get", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task SaveChapterGoalAsync(ChapterGoal goal)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.ChapterGoal_Save",
|
|
new { goal.ChapterID, goal.GoalSummary, goal.EmotionalGoal, goal.ReaderTakeaway, goal.MustInclude, goal.RisksOrConcerns },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ChapterWorkflowScene>> GetChapterWorkflowAsync(int chapterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ChapterWorkflowScene>("dbo.ChapterWorkflow_Summary", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<WriterDashboardScene>> GetWriterDashboardScenesAsync(int? projectId, string queue)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<WriterDashboardScene>("dbo.WriterDashboard_Scenes", new { ProjectID = projectId, Queue = queue }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryBibleAttentionItem>> GetWriterDashboardRemindersAsync(int? projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<StoryBibleAttentionItem>("dbo.WriterDashboard_Reminders", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
}
|
|
|
|
public sealed class ExportRepository(ISqlConnectionFactory connectionFactory) : IExportRepository
|
|
{
|
|
public async Task<ExportPacket> GetStoryBibleAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Export_StoryBible", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return new ExportPacket
|
|
{
|
|
Title = "Story Bible",
|
|
Characters = (await result.ReadAsync<StoryBibleCharacterSummary>()).ToList(),
|
|
PlotThreads = (await result.ReadAsync<StoryBibleThreadSummary>()).ToList(),
|
|
Assets = (await result.ReadAsync<StoryBibleAssetSummary>()).ToList(),
|
|
Relationships = (await result.ReadAsync<StoryBibleRelationshipSummary>()).ToList(),
|
|
Locations = (await result.ReadAsync<StoryBibleLocationSummary>()).ToList(),
|
|
Timeline = (await result.ReadAsync<StoryBibleTimelineSummary>()).ToList(),
|
|
OpenQuestions = (await result.ReadAsync<StoryBibleAttentionItem>()).ToList(),
|
|
RevisionItems = (await result.ReadAsync<StoryBibleRevisionItem>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<ExportPacket> GetNarrativeTimelineAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<StoryBibleTimelineSummary>("dbo.Export_NarrativeTimeline", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return new ExportPacket { Title = "Narrative Timeline Summary", Timeline = rows.ToList() };
|
|
}
|
|
|
|
public async Task<ExportPacket> GetCharacterReferenceAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Export_CharacterReference", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
return new ExportPacket
|
|
{
|
|
Title = "Character Reference Pack",
|
|
Character = await result.ReadSingleOrDefaultAsync<Character>(),
|
|
CharacterRelationships = (await result.ReadAsync<CharacterRelationship>()).ToList(),
|
|
CharacterAppearances = (await result.ReadAsync<SceneCharacter>()).ToList(),
|
|
CharacterKnowledge = (await result.ReadAsync<CharacterKnowledgeItem>()).ToList(),
|
|
CharacterCustody = (await result.ReadAsync<AssetCustodyEvent>()).ToList(),
|
|
Items = (await result.ReadAsync<ExportBriefItem>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<ExportPacket> GetSceneBriefAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Export_SceneBrief", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return new ExportPacket
|
|
{
|
|
Title = "Scene Brief",
|
|
SceneHeader = await result.ReadSingleOrDefaultAsync<ExportSceneBriefHeader>(),
|
|
Items = (await result.ReadAsync<ExportBriefItem>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<ExportPacket> GetChapterBriefAsync(int chapterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Export_ChapterBrief", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
|
|
return new ExportPacket
|
|
{
|
|
Title = "Chapter Brief",
|
|
ChapterHeader = await result.ReadSingleOrDefaultAsync<ExportChapterBriefHeader>(),
|
|
Timeline = (await result.ReadAsync<StoryBibleTimelineSummary>()).ToList(),
|
|
Items = (await result.ReadAsync<ExportBriefItem>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<ExportPacket> GetWriterSessionBriefAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Export_WriterSessionBrief", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return new ExportPacket
|
|
{
|
|
Title = "Writing Session Brief",
|
|
SceneHeader = await result.ReadSingleOrDefaultAsync<ExportSceneBriefHeader>(),
|
|
Items = (await result.ReadAsync<ExportBriefItem>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<ExportPacket> GetResearchPackAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ExportResearchItem>("dbo.Export_ResearchPack", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return new ExportPacket { Title = "Research Pack", ResearchItems = rows.ToList() };
|
|
}
|
|
|
|
public async Task<ExportPacket> GetSearchResultsAsync(int projectId, string query)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<StoryBibleSearchResult>("dbo.Export_SearchResults", new { ProjectID = projectId, Query = query }, commandType: CommandType.StoredProcedure);
|
|
return new ExportPacket { Title = $"Search Results: {query}", SearchResults = rows.ToList() };
|
|
}
|
|
}
|
|
|
|
public sealed class DynamicsRepository(ISqlConnectionFactory connectionFactory) : IDynamicsRepository
|
|
{
|
|
public async Task<(Character? Character, IReadOnlyList<CharacterArcPoint> ArcPoints, IReadOnlyList<RelationshipEvent> RelationshipEvents, IReadOnlyList<CharacterKnowledgeItem> KnowledgeEvents, IReadOnlyList<DynamicsIssue> Issues)> GetCharacterArcAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Dynamics_CharacterArc", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
return (
|
|
await result.ReadSingleOrDefaultAsync<Character>(),
|
|
(await result.ReadAsync<CharacterArcPoint>()).ToList(),
|
|
(await result.ReadAsync<RelationshipEvent>()).ToList(),
|
|
(await result.ReadAsync<CharacterKnowledgeItem>()).ToList(),
|
|
(await result.ReadAsync<DynamicsIssue>()).ToList());
|
|
}
|
|
|
|
public async Task<(CharacterRelationship? Relationship, IReadOnlyList<RelationshipProgressionPoint> Events, IReadOnlyList<DynamicsIssue> Issues)> GetRelationshipEvolutionAsync(int characterRelationshipId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Dynamics_RelationshipEvolution", new { CharacterRelationshipID = characterRelationshipId }, commandType: CommandType.StoredProcedure);
|
|
return (
|
|
await result.ReadSingleOrDefaultAsync<CharacterRelationship>(),
|
|
(await result.ReadAsync<RelationshipProgressionPoint>()).ToList(),
|
|
(await result.ReadAsync<DynamicsIssue>()).ToList());
|
|
}
|
|
|
|
public async Task<(Character? Character, IReadOnlyList<CharacterKnowledgeItem> KnowledgeEvents, IReadOnlyList<DynamicsIssue> Issues)> GetKnowledgeTimelineAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Dynamics_KnowledgeTimeline", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
return (
|
|
await result.ReadSingleOrDefaultAsync<Character>(),
|
|
(await result.ReadAsync<CharacterKnowledgeItem>()).ToList(),
|
|
(await result.ReadAsync<DynamicsIssue>()).ToList());
|
|
}
|
|
|
|
public async Task<(IReadOnlyList<CharacterPairingSummary> Pairings, IReadOnlyList<DynamicsIssue> Issues)> GetStoryDynamicsAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Dynamics_StoryDashboard", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return ((await result.ReadAsync<CharacterPairingSummary>()).ToList(), (await result.ReadAsync<DynamicsIssue>()).ToList());
|
|
}
|
|
|
|
public async Task RunContinuityChecksAsync(int projectId, int? bookId, int? sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Dynamics_RunContinuityChecks", new { ProjectID = projectId, BookID = bookId, SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class ScenarioRepository(ISqlConnectionFactory connectionFactory) : IScenarioRepository
|
|
{
|
|
public async Task<IReadOnlyList<Scenario>> ListAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Scenario>("dbo.Scenario_List", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<Scenario?> GetAsync(int scenarioId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<Scenario>("dbo.Scenario_Get", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> CreateAsync(int projectId, int? bookId, string scenarioName, string? description)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.Scenario_Create",
|
|
new { ProjectID = projectId, BookID = bookId, ScenarioName = scenarioName, Description = description },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<(IReadOnlyList<Book> Books, IReadOnlyList<Chapter> Chapters, IReadOnlyList<ScenarioScene> Scenes, IReadOnlyList<ScenarioWarning> Warnings)> GetTimelineAsync(int scenarioId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Scenario_Timeline", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
|
|
return (
|
|
(await result.ReadAsync<Book>()).ToList(),
|
|
(await result.ReadAsync<Chapter>()).ToList(),
|
|
(await result.ReadAsync<ScenarioScene>()).ToList(),
|
|
(await result.ReadAsync<ScenarioWarning>()).ToList());
|
|
}
|
|
|
|
public async Task MoveSceneAsync(int scenarioId, int sceneId, int? anchorSceneId, int? targetChapterId, string position)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.Scenario_MoveScene",
|
|
new { ScenarioID = scenarioId, SceneID = sceneId, AnchorSceneID = anchorSceneId, TargetChapterID = targetChapterId, Position = position },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> ValidateAsync(int scenarioId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>("dbo.Scenario_Validate", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<(IReadOnlyList<ScenarioComparisonRow> MovedScenes, IReadOnlyList<ScenarioWarning> Warnings)> CompareAsync(int scenarioId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Scenario_Compare", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
|
|
return ((await result.ReadAsync<ScenarioComparisonRow>()).ToList(), (await result.ReadAsync<ScenarioWarning>()).ToList());
|
|
}
|
|
|
|
public async Task ApplyAsync(int scenarioId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Scenario_ApplyToMain", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveAsync(int scenarioId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Scenario_Archive", new { ScenarioID = scenarioId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class ArchiveRepository(ISqlConnectionFactory connectionFactory) : IArchiveRepository
|
|
{
|
|
public async Task<IReadOnlyList<ArchivedItem>> ListAsync(int? projectId, string? entityType)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ArchivedItem>(
|
|
"dbo.Archive_List",
|
|
new { ProjectID = projectId, EntityType = entityType },
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<Project>> ListProjectFiltersAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Project>(
|
|
"dbo.Archive_ProjectFilterList",
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task ArchiveAsync(string entityType, int entityId, string? reason)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.Archive_Entity",
|
|
new { EntityType = entityType, EntityID = entityId, ArchivedReason = reason },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task RestoreAsync(string entityType, int entityId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.Archive_Restore",
|
|
new { EntityType = entityType, EntityID = entityId },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<ArchiveDeleteResult> DeleteForeverAsync(string entityType, int entityId, string confirmationText)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<ArchiveDeleteResult>(
|
|
"dbo.Archive_DeleteForever",
|
|
new { EntityType = entityType, EntityID = entityId, ConfirmationText = confirmationText },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class AcceptanceRepository(ISqlConnectionFactory connectionFactory) : IAcceptanceRepository
|
|
{
|
|
public async Task<IReadOnlyList<Phase1AcceptanceChecklistItem>> GetPhase1ChecklistAsync(int? projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Phase1AcceptanceChecklistItem>(
|
|
"dbo.Acceptance_Phase1Checklist",
|
|
new { ProjectID = projectId },
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
}
|
|
|
|
public sealed class SceneDependencyRepository(ISqlConnectionFactory connectionFactory) : ISceneDependencyRepository
|
|
{
|
|
public async Task<SceneDependencyLookupData> GetLookupsAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneDependencyType>("dbo.SceneDependencyLookup_All", commandType: CommandType.StoredProcedure);
|
|
return new SceneDependencyLookupData { DependencyTypes = rows.ToList() };
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneDependency>> ListBySceneAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneDependency>("dbo.SceneDependency_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneDependency>> ListByProjectAsync(int projectId, int? bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneDependency>("dbo.SceneDependency_ListByProject", new { ProjectID = projectId, BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneDependencyCount>> GetCountsAsync(int projectId, int? bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneDependencyCount>("dbo.SceneDependency_Counts", new { ProjectID = projectId, BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveAsync(SceneDependency dependency)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.SceneDependency_Save",
|
|
new
|
|
{
|
|
dependency.SceneDependencyID,
|
|
dependency.ProjectID,
|
|
dependency.SourceSceneID,
|
|
dependency.TargetSceneID,
|
|
dependency.SceneDependencyTypeID,
|
|
dependency.Description,
|
|
dependency.IsHardDependency
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveAsync(int sceneDependencyId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.SceneDependency_Archive", new { SceneDependencyID = sceneDependencyId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class ProjectRepository(ISqlConnectionFactory connectionFactory) : IProjectRepository
|
|
{
|
|
public async Task<IReadOnlyList<Project>> ListAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Project>("dbo.Project_List", commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<Project?> GetAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<Project>("dbo.Project_Get", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<GenreMetricPreset>> ListGenreMetricPresetsAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<GenreMetricPreset>("dbo.GenreMetricPreset_List", commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public Task<int> SaveAsync(Project project) => SaveAsync(project, null);
|
|
|
|
public async Task<int> SaveAsync(Project project, string? genreMetricPresetKey)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.Project_Save",
|
|
new { project.ProjectID, project.ProjectName, project.Description, GenreMetricPresetKey = genreMetricPresetKey },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Project_Archive", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class BookRepository(ISqlConnectionFactory connectionFactory) : IBookRepository
|
|
{
|
|
public async Task<IReadOnlyList<Book>> ListByProjectAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Book>("dbo.Book_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<Book?> GetAsync(int bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<Book>("dbo.Book_Get", new { BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SaveAsync(Book book)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.Book_Save",
|
|
new { book.BookID, book.ProjectID, book.BookTitle, book.BookNumber, book.Description },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveAsync(int bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Book_Archive", new { BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class ChapterRepository(ISqlConnectionFactory connectionFactory) : IChapterRepository
|
|
{
|
|
public async Task<IReadOnlyList<Chapter>> ListByBookAsync(int bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Chapter>("dbo.Chapter_ListByBook", new { BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<Chapter?> GetAsync(int chapterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<Chapter>("dbo.Chapter_Get", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SaveAsync(Chapter chapter)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.Chapter_Save",
|
|
new { chapter.ChapterID, chapter.BookID, chapter.ChapterNumber, chapter.ChapterTitle, chapter.Summary, chapter.RevisionStatusID },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveAsync(int chapterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Chapter_Archive", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class SceneRepository(ISqlConnectionFactory connectionFactory) : ISceneRepository
|
|
{
|
|
public async Task<IReadOnlyList<Scene>> ListByChapterAsync(int chapterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Scene>("dbo.Scene_ListByChapter", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<Scene?> GetAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Scene_Get", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
var scene = await result.ReadSingleOrDefaultAsync<Scene>();
|
|
if (scene is null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
scene.SelectedPurposeTypeIds = (await result.ReadAsync<int>()).ToList();
|
|
scene.Metrics = (await result.ReadAsync<SceneMetricValue>()).ToList();
|
|
return scene;
|
|
}
|
|
|
|
public async Task<int> SaveAsync(Scene scene)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.Scene_Save",
|
|
new
|
|
{
|
|
scene.SceneID,
|
|
scene.ChapterID,
|
|
scene.SceneNumber,
|
|
scene.SceneTitle,
|
|
scene.Summary,
|
|
scene.TimeModeID,
|
|
scene.StartDateTime,
|
|
scene.EndDateTime,
|
|
scene.DurationAmount,
|
|
scene.DurationUnitID,
|
|
scene.RelativeTimeText,
|
|
scene.TimeConfidenceID,
|
|
scene.ScenePurposeNotes,
|
|
scene.SceneOutcomeNotes,
|
|
scene.RevisionStatusID,
|
|
scene.PrimaryLocationID
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task SavePurposesAsync(int sceneId, IEnumerable<int> purposeIds)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var cleanPurposeIds = purposeIds
|
|
.Where(id => id > 0)
|
|
.Distinct()
|
|
.ToList();
|
|
|
|
await connection.ExecuteAsync(
|
|
"dbo.Scene_SavePurposes",
|
|
new { SceneID = sceneId, PurposeIds = string.Join(',', cleanPurposeIds) },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task SaveMetricValuesAsync(int sceneId, IEnumerable<SceneMetricValue> metricValues)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
foreach (var metric in metricValues)
|
|
{
|
|
await connection.ExecuteAsync(
|
|
"dbo.SceneMetric_SaveValue",
|
|
new { SceneID = sceneId, metric.MetricTypeID, metric.Value, metric.Notes },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public async Task ArchiveAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Scene_Archive", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task MoveAsync(int sceneId, string direction)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Scene_Move", new { SceneID = sceneId, Direction = direction }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ChapterOption>> ListChapterOptionsAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ChapterOption>("dbo.ChapterOption_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<SceneMovePreview?> GetMovePreviewAsync(int sceneId, int targetChapterId, string position)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<SceneMovePreview>(
|
|
"dbo.SceneMovePreview_Get",
|
|
new { SceneID = sceneId, TargetChapterID = targetChapterId, Position = position },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task MoveToChapterAsync(int sceneId, int targetChapterId, string position, bool renumber)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.Scene_MoveToChapter",
|
|
new { SceneID = sceneId, TargetChapterID = targetChapterId, Position = position, Renumber = renumber },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task MoveRelativeAsync(int sceneId, int anchorSceneId, string position)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.Scene_MoveRelative",
|
|
new { SceneID = sceneId, AnchorSceneID = anchorSceneId, Position = position },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task RenumberChapterAsync(int chapterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Scene_RenumberChapter", new { ChapterID = chapterId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class LookupRepository(ISqlConnectionFactory connectionFactory) : ILookupRepository
|
|
{
|
|
public async Task<LookupData> GetAllAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.Lookup_All", commandType: CommandType.StoredProcedure);
|
|
return new LookupData
|
|
{
|
|
RevisionStatuses = (await result.ReadAsync<RevisionStatus>()).ToList(),
|
|
TimeModes = (await result.ReadAsync<TimeMode>()).ToList(),
|
|
DurationUnits = (await result.ReadAsync<DurationUnit>()).ToList(),
|
|
TimeConfidences = (await result.ReadAsync<TimeConfidence>()).ToList(),
|
|
ScenePurposeTypes = (await result.ReadAsync<ScenePurposeType>()).ToList()
|
|
};
|
|
}
|
|
}
|
|
|
|
public sealed class TimelineRepository(ISqlConnectionFactory connectionFactory) : ITimelineRepository
|
|
{
|
|
public async Task<TimelineData> GetByProjectAsync(int projectId, int? bookId, bool includeMetrics, IEnumerable<int> metricTypeIds, bool includePlotLines)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var selectedMetricIds = string.Join(',', metricTypeIds.Where(id => id > 0).Distinct());
|
|
using var result = await connection.QueryMultipleAsync(
|
|
"dbo.Timeline_GetByProject",
|
|
new
|
|
{
|
|
ProjectID = projectId,
|
|
BookID = bookId,
|
|
IncludeMetrics = includeMetrics,
|
|
SelectedMetricTypeIDs = selectedMetricIds,
|
|
IncludePlotLines = includePlotLines
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
|
|
var project = await result.ReadSingleOrDefaultAsync<Project>();
|
|
var books = (await result.ReadAsync<Book>()).ToList();
|
|
var chapters = (await result.ReadAsync<Chapter>()).ToList();
|
|
var scenes = (await result.ReadAsync<Scene>()).ToList();
|
|
var purposeLabels = (await result.ReadAsync<ScenePurposeLabel>()).ToList();
|
|
var metricTypes = (await result.ReadAsync<SceneMetricType>()).ToList();
|
|
var metricPoints = (await result.ReadAsync<TimelineMetricPoint>()).ToList();
|
|
var plotLines = (await result.ReadAsync<PlotLineItem>()).ToList();
|
|
var threadEvents = (await result.ReadAsync<ThreadEvent>()).ToList();
|
|
|
|
return new TimelineData
|
|
{
|
|
Project = project,
|
|
Books = books,
|
|
Chapters = chapters,
|
|
Scenes = scenes,
|
|
PurposeLabels = purposeLabels,
|
|
MetricTypes = metricTypes,
|
|
MetricPoints = metricPoints,
|
|
PlotLines = plotLines,
|
|
ThreadEvents = threadEvents
|
|
};
|
|
}
|
|
}
|
|
|
|
public sealed class TimelineSettingsRepository(ISqlConnectionFactory connectionFactory) : ITimelineSettingsRepository
|
|
{
|
|
public async Task<ProjectTimelineSettings?> GetAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<ProjectTimelineSettings>(
|
|
"dbo.ProjectTimelineSettings_Get",
|
|
new { ProjectID = projectId },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneMetricType>> ListMetricTypesAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneMetricType>(
|
|
"dbo.ProjectTimelineSettings_MetricTypes",
|
|
new { ProjectID = projectId },
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ProjectTimelineMetricSetting>> ListMetricSettingsAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ProjectTimelineMetricSetting>(
|
|
"dbo.ProjectTimelineMetricSettings_List",
|
|
new { ProjectID = projectId },
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task SaveAsync(ProjectTimelineSettings settings, IEnumerable<int> metricTypeIds)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.ProjectTimelineSettings_Save",
|
|
new
|
|
{
|
|
settings.ProjectID,
|
|
settings.ShowSceneCards,
|
|
settings.ShowMetricShape,
|
|
settings.ShowPlotLines,
|
|
settings.ShowStoryAssets,
|
|
settings.ShowCharacterAppearances,
|
|
settings.ShowWarnings,
|
|
MetricTypeIDs = string.Join(',', metricTypeIds.Where(id => id > 0).Distinct())
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class SceneMetricTypeRepository(ISqlConnectionFactory connectionFactory) : ISceneMetricTypeRepository
|
|
{
|
|
public async Task<IReadOnlyList<SceneMetricType>> ListForManagementAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneMetricType>(
|
|
"dbo.SceneMetricType_ListForProjectManagement",
|
|
new { ProjectID = projectId },
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<SceneMetricType?> GetAsync(int metricTypeId, int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<SceneMetricType>(
|
|
"dbo.SceneMetricType_GetForProject",
|
|
new { MetricTypeID = metricTypeId, ProjectID = projectId },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SaveAsync(SceneMetricType metric)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.SceneMetricType_SaveForProject",
|
|
new
|
|
{
|
|
metric.MetricTypeID,
|
|
metric.ProjectID,
|
|
metric.MetricName,
|
|
metric.Description,
|
|
metric.MinValue,
|
|
metric.MaxValue,
|
|
metric.DefaultValue,
|
|
metric.SortOrder,
|
|
metric.IsActive
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<bool> SetActiveAsync(int metricTypeId, int projectId, bool isActive)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<bool>(
|
|
"dbo.SceneMetricType_SetActiveForProject",
|
|
new { MetricTypeID = metricTypeId, ProjectID = projectId, IsActive = isActive },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<bool> MoveAsync(int metricTypeId, int projectId, string direction)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<bool>(
|
|
"dbo.SceneMetricType_MoveForProject",
|
|
new { MetricTypeID = metricTypeId, ProjectID = projectId, Direction = direction },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task AddMissingDefaultsAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.SceneMetricType_AddMissingDefaultsForProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<bool> RemoveUnusedAsync(int metricTypeId, int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<bool>(
|
|
"dbo.SceneMetricType_RemoveUnusedForProject",
|
|
new { MetricTypeID = metricTypeId, ProjectID = projectId },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<bool> HasProjectMetricsAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<bool>("dbo.SceneMetricType_ProjectHasMetrics", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class TimelinePresetRepository(ISqlConnectionFactory connectionFactory) : ITimelinePresetRepository
|
|
{
|
|
public async Task<IReadOnlyList<TimelineViewPreset>> ListAsync(int projectId, int? bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<TimelineViewPreset>(
|
|
"dbo.TimelineViewPreset_List",
|
|
new { ProjectID = projectId, BookID = bookId },
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<TimelineViewPreset?> GetAsync(int timelineViewPresetId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<TimelineViewPreset>(
|
|
"dbo.TimelineViewPreset_Get",
|
|
new { TimelineViewPresetID = timelineViewPresetId },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SaveAsync(TimelineViewPreset preset)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.TimelineViewPreset_Save",
|
|
new
|
|
{
|
|
preset.TimelineViewPresetID,
|
|
preset.ProjectID,
|
|
preset.BookID,
|
|
preset.PresetName,
|
|
preset.FilterJson,
|
|
preset.VisibilityJson,
|
|
preset.FocusType,
|
|
preset.FocusID
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveAsync(int timelineViewPresetId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync(
|
|
"dbo.TimelineViewPreset_Archive",
|
|
new { TimelineViewPresetID = timelineViewPresetId },
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
}
|
|
|
|
public sealed class PlotRepository(ISqlConnectionFactory connectionFactory) : IPlotRepository
|
|
{
|
|
public async Task<PlotLookupData> GetLookupsAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.PlotLookup_All", commandType: CommandType.StoredProcedure);
|
|
return new PlotLookupData
|
|
{
|
|
PlotLineTypes = (await result.ReadAsync<PlotLineType>()).ToList(),
|
|
ThreadTypes = (await result.ReadAsync<ThreadType>()).ToList(),
|
|
ThreadStatuses = (await result.ReadAsync<ThreadStatus>()).ToList(),
|
|
ThreadEventTypes = (await result.ReadAsync<ThreadEventType>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<IReadOnlyList<PlotLineItem>> ListPlotLinesAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<PlotLineItem>("dbo.PlotLine_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<PlotLineItem?> GetPlotLineAsync(int plotLineId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<PlotLineItem>("dbo.PlotLine_Get", new { PlotLineID = plotLineId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SavePlotLineAsync(PlotLineItem plotLine)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.PlotLine_Save",
|
|
new
|
|
{
|
|
plotLine.PlotLineID,
|
|
plotLine.ProjectID,
|
|
plotLine.BookID,
|
|
plotLine.PlotLineName,
|
|
plotLine.PlotLineTypeID,
|
|
plotLine.Description,
|
|
plotLine.ParentPlotLineID,
|
|
plotLine.SortOrder,
|
|
plotLine.Colour,
|
|
plotLine.IsMainPlot,
|
|
plotLine.IsVisibleOnTimeline
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchivePlotLineAsync(int plotLineId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.PlotLine_Archive", new { PlotLineID = plotLineId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<PlotThread>> ListPlotThreadsByProjectAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<PlotThread>("dbo.PlotThread_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<PlotThread>> ListPlotThreadsByPlotLineAsync(int plotLineId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<PlotThread>("dbo.PlotThread_ListByPlotLine", new { PlotLineID = plotLineId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<PlotThread?> GetPlotThreadAsync(int plotThreadId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<PlotThread>("dbo.PlotThread_Get", new { PlotThreadID = plotThreadId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SavePlotThreadAsync(PlotThread plotThread)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.PlotThread_Save",
|
|
new
|
|
{
|
|
plotThread.PlotThreadID,
|
|
plotThread.PlotLineID,
|
|
plotThread.ThreadTitle,
|
|
plotThread.ThreadTypeID,
|
|
plotThread.ThreadStatusID,
|
|
plotThread.Importance,
|
|
plotThread.Summary,
|
|
plotThread.IntroducedSceneID,
|
|
plotThread.PlannedResolutionSceneID,
|
|
plotThread.ActualResolutionSceneID
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchivePlotThreadAsync(int plotThreadId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.PlotThread_Archive", new { PlotThreadID = plotThreadId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ThreadEvent>> ListThreadEventsBySceneAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ThreadEvent>("dbo.ThreadEvent_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<ThreadEvent>> ListThreadEventsByPlotThreadAsync(int plotThreadId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<ThreadEvent>("dbo.ThreadEvent_ListByPlotThread", new { PlotThreadID = plotThreadId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveThreadEventAsync(ThreadEvent threadEvent)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.ThreadEvent_Save",
|
|
new
|
|
{
|
|
threadEvent.ThreadEventID,
|
|
threadEvent.PlotThreadID,
|
|
threadEvent.SceneID,
|
|
threadEvent.EventTypeID,
|
|
threadEvent.EventTitle,
|
|
threadEvent.EventDescription
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteThreadEventAsync(int threadEventId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.ThreadEvent_Delete", new { ThreadEventID = threadEventId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneOption>> ListSceneOptionsAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneOption>("dbo.SceneOption_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
}
|
|
|
|
public sealed class AssetRepository(ISqlConnectionFactory connectionFactory) : IAssetRepository
|
|
{
|
|
public async Task<AssetLookupData> GetLookupsAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.AssetLookup_All", commandType: CommandType.StoredProcedure);
|
|
return new AssetLookupData
|
|
{
|
|
AssetKinds = (await result.ReadAsync<AssetKind>()).ToList(),
|
|
AssetStates = (await result.ReadAsync<AssetState>()).ToList(),
|
|
AssetEventTypes = (await result.ReadAsync<AssetEventType>()).ToList(),
|
|
AssetDependencyTypes = (await result.ReadAsync<AssetDependencyType>()).ToList(),
|
|
AssetCustodyEventTypes = (await result.ReadAsync<AssetCustodyEventType>()).ToList(),
|
|
CustodyRoles = (await result.ReadAsync<CustodyRole>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<IReadOnlyList<StoryAsset>> ListAssetsAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<StoryAsset>("dbo.StoryAsset_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<StoryAsset?> GetAssetAsync(int storyAssetId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<StoryAsset>("dbo.StoryAsset_Get", new { StoryAssetID = storyAssetId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SaveAssetAsync(StoryAsset asset)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.StoryAsset_Save",
|
|
new
|
|
{
|
|
asset.StoryAssetID,
|
|
asset.ProjectID,
|
|
asset.AssetName,
|
|
asset.AssetKindID,
|
|
asset.Description,
|
|
asset.Importance,
|
|
asset.CurrentStateID,
|
|
asset.CurrentLocationID,
|
|
asset.IsResolved,
|
|
asset.ShowInQuickAddBar
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveAssetAsync(int storyAssetId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.StoryAsset_Archive", new { StoryAssetID = storyAssetId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<AssetEvent>> ListEventsBySceneAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<AssetEvent>("dbo.AssetEvent_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<AssetEvent>> ListEventsByAssetAsync(int storyAssetId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<AssetEvent>("dbo.AssetEvent_ListByAsset", new { StoryAssetID = storyAssetId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveEventAsync(AssetEvent assetEvent)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.AssetEvent_Save",
|
|
new
|
|
{
|
|
assetEvent.AssetEventID,
|
|
assetEvent.StoryAssetID,
|
|
assetEvent.SceneID,
|
|
assetEvent.AssetEventTypeID,
|
|
assetEvent.FromStateID,
|
|
assetEvent.ToStateID,
|
|
assetEvent.EventTitle,
|
|
assetEvent.EventDescription
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteEventAsync(int assetEventId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.AssetEvent_Delete", new { AssetEventID = assetEventId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<AssetDependency>> ListDependenciesByAssetAsync(int storyAssetId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<AssetDependency>("dbo.AssetDependency_ListByAsset", new { StoryAssetID = storyAssetId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveDependencyAsync(AssetDependency dependency)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.AssetDependency_Save",
|
|
new
|
|
{
|
|
dependency.AssetDependencyID,
|
|
dependency.SourceAssetID,
|
|
dependency.TargetAssetID,
|
|
dependency.AssetDependencyTypeID,
|
|
dependency.SourceRequiredStateID,
|
|
dependency.TargetBlockedStateID,
|
|
dependency.Description
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteDependencyAsync(int assetDependencyId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.AssetDependency_Delete", new { AssetDependencyID = assetDependencyId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<AssetCustodyEvent>> ListCustodyBySceneAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<AssetCustodyEvent>("dbo.AssetCustodyEvent_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<AssetCustodyEvent>> ListCustodyByAssetAsync(int storyAssetId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<AssetCustodyEvent>("dbo.AssetCustodyEvent_ListByAsset", new { StoryAssetID = storyAssetId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveCustodyEventAsync(AssetCustodyEvent custodyEvent, string custodianNames, IEnumerable<int> custodianCharacterIds, int custodyRoleId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.AssetCustodyEvent_Save",
|
|
new
|
|
{
|
|
custodyEvent.AssetCustodyEventID,
|
|
custodyEvent.StoryAssetID,
|
|
custodyEvent.SceneID,
|
|
custodyEvent.AssetCustodyEventTypeID,
|
|
custodyEvent.Description,
|
|
CustodianNames = custodianNames,
|
|
CustodianCharacterIds = string.Join(',', custodianCharacterIds.Where(id => id > 0).Distinct()),
|
|
CustodyRoleID = custodyRoleId
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteCustodyEventAsync(int assetCustodyEventId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.AssetCustodyEvent_Delete", new { AssetCustodyEventID = assetCustodyEventId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<(IReadOnlyList<StoryAsset> Assets, IReadOnlyList<AssetEvent> Events)> GetTimelineAsync(int projectId, int? bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.AssetTimeline_GetByProject", new { ProjectID = projectId, BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
var assets = (await result.ReadAsync<StoryAsset>()).ToList();
|
|
var events = (await result.ReadAsync<AssetEvent>()).ToList();
|
|
return (assets, events);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<AssetDependencyIssue>> CheckDependencyIssuesAsync(int storyAssetId, int sceneId, int? toStateId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<AssetDependencyIssue>(
|
|
"dbo.AssetDependency_CheckForEvent",
|
|
new { StoryAssetID = storyAssetId, SceneID = sceneId, ToStateID = toStateId },
|
|
commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
}
|
|
|
|
public sealed class CharacterRepository(ISqlConnectionFactory connectionFactory) : ICharacterRepository
|
|
{
|
|
public async Task<CharacterLookupData> GetLookupsAsync()
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.CharacterLookup_All", commandType: CommandType.StoredProcedure);
|
|
return new CharacterLookupData
|
|
{
|
|
RoleTypes = (await result.ReadAsync<CharacterRoleInSceneType>()).ToList(),
|
|
PresenceTypes = (await result.ReadAsync<PresenceType>()).ToList(),
|
|
AttributeTypes = (await result.ReadAsync<CharacterAttributeType>()).ToList(),
|
|
KnowledgeStates = (await result.ReadAsync<KnowledgeState>()).ToList(),
|
|
RelationshipTypes = (await result.ReadAsync<RelationshipType>()).ToList(),
|
|
RelationshipStates = (await result.ReadAsync<RelationshipState>()).ToList()
|
|
};
|
|
}
|
|
|
|
public async Task<IReadOnlyList<Character>> ListCharactersAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<Character>("dbo.Character_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<Character?> GetCharacterAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleOrDefaultAsync<Character>("dbo.Character_Get", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<int> SaveCharacterAsync(Character character)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.Character_Save",
|
|
new
|
|
{
|
|
character.CharacterID,
|
|
character.ProjectID,
|
|
character.CharacterName,
|
|
character.ShortName,
|
|
character.Sex,
|
|
character.BirthDate,
|
|
character.AgeAtSeriesStart,
|
|
character.AgeReferenceSceneID,
|
|
character.Height,
|
|
character.EyeColour,
|
|
character.CharacterImportance,
|
|
character.ShowInQuickAddBar,
|
|
character.DefaultDescription
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveCharacterAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Character_Archive", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<SceneCharacter>> ListSceneCharactersAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<SceneCharacter>("dbo.SceneCharacter_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveSceneCharacterAsync(SceneCharacter sceneCharacter)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.SceneCharacter_Save",
|
|
new
|
|
{
|
|
sceneCharacter.SceneCharacterID,
|
|
sceneCharacter.SceneID,
|
|
sceneCharacter.CharacterID,
|
|
sceneCharacter.RoleInSceneTypeID,
|
|
sceneCharacter.PresenceTypeID,
|
|
sceneCharacter.LocationID,
|
|
sceneCharacter.EntryLocationID,
|
|
sceneCharacter.ExitLocationID,
|
|
sceneCharacter.AppearanceNotes,
|
|
sceneCharacter.OutfitDescription,
|
|
sceneCharacter.PhysicalCondition,
|
|
sceneCharacter.EmotionalState,
|
|
sceneCharacter.KnowledgeNotes
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteSceneCharacterAsync(int sceneCharacterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.SceneCharacter_Delete", new { SceneCharacterID = sceneCharacterId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<CharacterAttributeEvent>> ListAttributeEventsByCharacterAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<CharacterAttributeEvent>("dbo.CharacterAttributeEvent_ListByCharacter", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveAttributeEventAsync(CharacterAttributeEvent attributeEvent)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.CharacterAttributeEvent_Save",
|
|
new
|
|
{
|
|
attributeEvent.CharacterAttributeEventID,
|
|
attributeEvent.CharacterID,
|
|
attributeEvent.CharacterAttributeTypeID,
|
|
attributeEvent.SceneID,
|
|
attributeEvent.AttributeValue,
|
|
attributeEvent.Description
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<CharacterKnowledgeItem>> ListKnowledgeBySceneAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<CharacterKnowledgeItem>("dbo.CharacterKnowledge_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<CharacterKnowledgeItem>> ListKnowledgeByCharacterAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<CharacterKnowledgeItem>("dbo.CharacterKnowledge_ListByCharacter", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveKnowledgeAsync(CharacterKnowledgeItem knowledge)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.CharacterKnowledge_Save",
|
|
new
|
|
{
|
|
knowledge.CharacterKnowledgeID,
|
|
knowledge.CharacterID,
|
|
knowledge.StoryAssetID,
|
|
knowledge.PlotThreadID,
|
|
knowledge.SceneID,
|
|
knowledge.KnowledgeStateID,
|
|
knowledge.SourceEventID,
|
|
knowledge.Description
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteKnowledgeAsync(int characterKnowledgeId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.CharacterKnowledge_Delete", new { CharacterKnowledgeID = characterKnowledgeId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<CharacterRelationship>> ListRelationshipsByProjectAsync(int projectId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<CharacterRelationship>("dbo.Relationship_ListByProject", new { ProjectID = projectId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<CharacterRelationship>> ListRelationshipsByCharacterAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<CharacterRelationship>("dbo.Relationship_ListByCharacter", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveRelationshipAsync(CharacterRelationship relationship)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.Relationship_Save",
|
|
new
|
|
{
|
|
relationship.CharacterRelationshipID,
|
|
relationship.ProjectID,
|
|
relationship.CharacterAID,
|
|
relationship.CharacterBID,
|
|
relationship.RelationshipTypeID,
|
|
relationship.IsPermanent,
|
|
relationship.StartSceneID,
|
|
relationship.EndSceneID,
|
|
relationship.IsKnownToReader,
|
|
relationship.Notes
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task ArchiveRelationshipAsync(int characterRelationshipId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.Relationship_Archive", new { CharacterRelationshipID = characterRelationshipId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<IReadOnlyList<RelationshipEvent>> ListRelationshipEventsBySceneAsync(int sceneId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<RelationshipEvent>("dbo.RelationshipEvent_ListByScene", new { SceneID = sceneId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<IReadOnlyList<RelationshipEvent>> ListRelationshipEventsByCharacterAsync(int characterId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
var rows = await connection.QueryAsync<RelationshipEvent>("dbo.RelationshipEvent_ListByCharacter", new { CharacterID = characterId }, commandType: CommandType.StoredProcedure);
|
|
return rows.ToList();
|
|
}
|
|
|
|
public async Task<int> SaveRelationshipEventAsync(RelationshipEvent relationshipEvent)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
return await connection.QuerySingleAsync<int>(
|
|
"dbo.RelationshipEvent_Save",
|
|
new
|
|
{
|
|
relationshipEvent.RelationshipEventID,
|
|
relationshipEvent.CharacterRelationshipID,
|
|
relationshipEvent.SceneID,
|
|
relationshipEvent.RelationshipStateID,
|
|
relationshipEvent.Intensity,
|
|
relationshipEvent.IsKnownToOtherCharacter,
|
|
relationshipEvent.Description
|
|
},
|
|
commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task DeleteRelationshipEventAsync(int relationshipEventId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
await connection.ExecuteAsync("dbo.RelationshipEvent_Delete", new { RelationshipEventID = relationshipEventId }, commandType: CommandType.StoredProcedure);
|
|
}
|
|
|
|
public async Task<(IReadOnlyList<Character> Characters, IReadOnlyList<SceneCharacter> Appearances)> GetTimelineAsync(int projectId, int? bookId)
|
|
{
|
|
using var connection = connectionFactory.CreateConnection();
|
|
using var result = await connection.QueryMultipleAsync("dbo.CharacterTimeline_GetByProject", new { ProjectID = projectId, BookID = bookId }, commandType: CommandType.StoredProcedure);
|
|
var characters = (await result.ReadAsync<Character>()).ToList();
|
|
var appearances = (await result.ReadAsync<SceneCharacter>()).ToList();
|
|
return (characters, appearances);
|
|
}
|
|
}
|