421 lines
18 KiB
C#
421 lines
18 KiB
C#
using System.Text.Json;
|
|
using System.Text.Json.Serialization;
|
|
|
|
namespace PlotLine.Models;
|
|
|
|
public sealed class PlotLineImportPackage
|
|
{
|
|
public string? PackageVersion { get; set; }
|
|
public string? Source { get; set; }
|
|
public string? CreatedBy { get; set; }
|
|
public string? CreatedDate { get; set; }
|
|
public string? Description { get; set; }
|
|
public ImportProjectDto Project { get; set; } = new();
|
|
public ImportAliasesDto Aliases { get; set; } = new();
|
|
public List<ImportCharacterDto> Characters { get; set; } = [];
|
|
public List<ImportLocationDto> Locations { get; set; } = [];
|
|
public List<ImportPlotLineDto> PlotLines { get; set; } = [];
|
|
public List<ImportStoryAssetDto> StoryAssets { get; set; } = [];
|
|
public List<ImportSceneDependencyDto> SceneDependencies { get; set; } = [];
|
|
public List<ImportBookDto> Books { get; set; } = [];
|
|
|
|
[JsonExtensionData]
|
|
public Dictionary<string, object>? ExtensionData { get; set; }
|
|
}
|
|
|
|
public sealed class ImportAliasesDto
|
|
{
|
|
public Dictionary<string, string> Characters { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
|
public Dictionary<string, string> Locations { get; set; } = new(StringComparer.OrdinalIgnoreCase);
|
|
}
|
|
|
|
public sealed class ProjectBackupRelationshipImportLookups
|
|
{
|
|
public IReadOnlyList<ProjectBackupLookupRow> RevisionStatuses { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> TimeModes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> DurationUnits { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> TimeConfidences { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> LocationTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> LocationRelationshipTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> PlotLineTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> PlotImportance { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> ThreadTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> ThreadStatuses { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> ThreadEventTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> PlotEventTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> AssetKinds { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> AssetStates { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> AssetEventTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> AssetDependencyTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> AssetCustodyEventTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> CustodyRoles { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> CharacterRoleTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> PresenceTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> KnowledgeStates { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> SceneDependencyTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> SceneNoteTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> SceneAttachmentTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> ScenePurposeTypes { get; init; } = [];
|
|
public IReadOnlyList<ProjectBackupLookupRow> SceneMetricTypes { get; init; } = [];
|
|
public IReadOnlyList<RelationshipType> RelationshipTypes { get; init; } = [];
|
|
public IReadOnlyList<RelationshipState> RelationshipStates { get; init; } = [];
|
|
}
|
|
|
|
public sealed class ProjectBackupLookupRow
|
|
{
|
|
public int LookupID { get; set; }
|
|
public string LookupName { get; set; } = string.Empty;
|
|
}
|
|
|
|
public sealed class ProjectBackupRelationshipImportDiagnostics
|
|
{
|
|
public int CharacterCount { get; set; }
|
|
public int RelationshipCount { get; set; }
|
|
public int RelationshipEventCount { get; set; }
|
|
public int MapRelationshipCount { get; set; }
|
|
public int MissingSourceCharacterCount { get; set; }
|
|
public int MissingTargetCharacterCount { get; set; }
|
|
public int MissingRelationshipTypeCount { get; set; }
|
|
public int MissingInitialStateCount { get; set; }
|
|
public int MissingEventRelationshipCount { get; set; }
|
|
public int MissingEventSceneCount { get; set; }
|
|
public int MissingEventStateCount { get; set; }
|
|
public int ArchivedRelationshipCount { get; set; }
|
|
public int ArchivedCharacterEndpointCount { get; set; }
|
|
}
|
|
|
|
public sealed class ImportProjectDto
|
|
{
|
|
public string Title { get; set; } = string.Empty;
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class ImportBookDto
|
|
{
|
|
public string Title { get; set; } = string.Empty;
|
|
public string? Subtitle { get; set; }
|
|
public int SeriesOrder { get; set; }
|
|
public bool DependsOnPreviousBook { get; set; }
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
public List<ImportChapterDto> Chapters { get; set; } = [];
|
|
}
|
|
|
|
public sealed class ImportCharacterDto
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string? DisplayName { get; set; }
|
|
public string? ShortName { get; set; }
|
|
public string? Role { get; set; }
|
|
[JsonConverter(typeof(ImportImportanceJsonConverter))]
|
|
public int? Importance { get; set; }
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class ImportLocationDto
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string? DisplayName { get; set; }
|
|
public string? Type { get; set; }
|
|
public string? ParentLocationName { get; set; }
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class ImportPlotLineDto
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string? DisplayName { get; set; }
|
|
public string? Type { get; set; }
|
|
public int? Importance { get; set; }
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
public List<ImportPlotThreadDto> Threads { get; set; } = [];
|
|
}
|
|
|
|
public sealed class ImportPlotThreadDto
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string? DisplayName { get; set; }
|
|
public string? Type { get; set; }
|
|
public string? Status { get; set; }
|
|
public int? Importance { get; set; }
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
public List<ImportThreadEventDto> Events { get; set; } = [];
|
|
}
|
|
|
|
public sealed class ImportThreadEventDto
|
|
{
|
|
public string? SceneRef { get; set; }
|
|
public int? BookOrder { get; set; }
|
|
public int? ChapterOrder { get; set; }
|
|
public int? SceneOrder { get; set; }
|
|
public string? Title { get; set; }
|
|
public string? EventType { get; set; }
|
|
public string? Summary { get; set; }
|
|
public string? Impact { get; set; }
|
|
public int? Importance { get; set; }
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class ImportStoryAssetDto
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string? DisplayName { get; set; }
|
|
public string? Type { get; set; }
|
|
public int? Importance { get; set; }
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
public string? InitialOwnerCharacterName { get; set; }
|
|
public string? InitialLocationName { get; set; }
|
|
public List<ImportAssetStateDto> States { get; set; } = [];
|
|
public List<ImportAssetEventDto> Events { get; set; } = [];
|
|
}
|
|
|
|
public sealed class ImportAssetStateDto
|
|
{
|
|
public string? SceneRef { get; set; }
|
|
public int? BookOrder { get; set; }
|
|
public int? ChapterOrder { get; set; }
|
|
public int? SceneOrder { get; set; }
|
|
public string? StateName { get; set; }
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class ImportAssetEventDto
|
|
{
|
|
public string? SceneRef { get; set; }
|
|
public int? BookOrder { get; set; }
|
|
public int? ChapterOrder { get; set; }
|
|
public int? SceneOrder { get; set; }
|
|
public string? EventType { get; set; }
|
|
public string? Title { get; set; }
|
|
public string? Summary { get; set; }
|
|
public string? CharacterName { get; set; }
|
|
public string? LocationName { get; set; }
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class ImportSceneDependencyDto
|
|
{
|
|
public ImportSceneReferenceDto? FromScene { get; set; }
|
|
public ImportSceneReferenceDto? ToScene { get; set; }
|
|
public string? DependencyType { get; set; }
|
|
public string? Strength { get; set; }
|
|
public string? Description { get; set; }
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class ImportSceneReferenceDto
|
|
{
|
|
public string? SceneRef { get; set; }
|
|
public int? BookOrder { get; set; }
|
|
public int? ChapterOrder { get; set; }
|
|
public int? SceneOrder { get; set; }
|
|
}
|
|
|
|
public sealed class ImportChapterDto
|
|
{
|
|
public decimal ChapterNumber { get; set; }
|
|
public string Title { get; set; } = string.Empty;
|
|
public int Order { get; set; }
|
|
public string? Summary { get; set; }
|
|
public string? Notes { get; set; }
|
|
public List<ImportSceneDto> Scenes { get; set; } = [];
|
|
}
|
|
|
|
public sealed class ImportSceneDto
|
|
{
|
|
public decimal SceneNumber { get; set; }
|
|
public string Title { get; set; } = string.Empty;
|
|
public int Order { get; set; }
|
|
public string? Summary { get; set; }
|
|
public string? PovCharacterName { get; set; }
|
|
public string? LocationName { get; set; }
|
|
public string? DateTimeText { get; set; }
|
|
public string? PurposeText { get; set; }
|
|
public string? OutcomeText { get; set; }
|
|
public string? Notes { get; set; }
|
|
public List<ImportSceneCharacterDto> SceneCharacters { get; set; } = [];
|
|
}
|
|
|
|
public sealed class ImportSceneCharacterDto
|
|
{
|
|
public string CharacterName { get; set; } = string.Empty;
|
|
public string? RoleInScene { get; set; }
|
|
public int? Importance { get; set; }
|
|
public string? Notes { get; set; }
|
|
}
|
|
|
|
public sealed class ImportValidationResult
|
|
{
|
|
public bool IsValid => Errors.Count == 0;
|
|
public List<string> Errors { get; } = [];
|
|
public List<string> Warnings { get; } = [];
|
|
public List<string> AutoCorrections { get; } = [];
|
|
public List<string> SkippedItems { get; } = [];
|
|
public List<string> UnresolvedReferences { get; } = [];
|
|
}
|
|
|
|
public sealed class ImportPreview
|
|
{
|
|
public PlotLineImportPackage? Package { get; init; }
|
|
public ImportValidationResult Validation { get; init; } = new();
|
|
public bool HasDuplicateProjectTitle { get; init; }
|
|
public string? DuplicateImportProjectTitle { get; init; }
|
|
public IReadOnlyList<ImportBookPreview> BookPreviews { get; init; } = [];
|
|
public IReadOnlyList<string> UnmatchedPovNames { get; init; } = [];
|
|
public IReadOnlyList<string> UnmatchedLocationNames { get; init; } = [];
|
|
public IReadOnlyList<string> UnmatchedSceneCharacterNames { get; init; } = [];
|
|
public IReadOnlyList<string> UnresolvedThreadEventReferences { get; init; } = [];
|
|
public IReadOnlyList<string> UnresolvedAssetReferences { get; init; } = [];
|
|
public IReadOnlyList<string> UnmatchedAssetOwnerNames { get; init; } = [];
|
|
public IReadOnlyList<string> UnmatchedAssetLocationNames { get; init; } = [];
|
|
public IReadOnlyList<string> UnresolvedSceneDependencyReferences { get; init; } = [];
|
|
public IReadOnlyList<string> AliasWarnings { get; init; } = [];
|
|
public IReadOnlyList<string> AutoCorrections { get; init; } = [];
|
|
public IReadOnlyList<string> SkippedItems { get; init; } = [];
|
|
public int ErrorCount => Validation.Errors.Count;
|
|
public int WarningCount => Validation.Warnings.Count;
|
|
public int AutoCorrectionCount => AutoCorrections.Count + Validation.AutoCorrections.Count;
|
|
public int SkippedItemCount => SkippedItems.Count + Validation.SkippedItems.Count;
|
|
public int UnresolvedReferenceCount => UnresolvedThreadEventReferences.Count + UnresolvedAssetReferences.Count + UnresolvedSceneDependencyReferences.Count;
|
|
public int BookCount => Package?.Books.Count ?? 0;
|
|
public int CharacterCount => Package?.Characters.Count ?? 0;
|
|
public int LocationCount => Package?.Locations.Count ?? 0;
|
|
public int PlotLineCount => Package?.PlotLines.Count ?? 0;
|
|
public int PlotThreadCount => Package?.PlotLines.Sum(plotLine => plotLine.Threads.Count) ?? 0;
|
|
public int ThreadEventCount => Package?.PlotLines.Sum(plotLine => plotLine.Threads.Sum(thread => thread.Events.Count)) ?? 0;
|
|
public int ResolvedThreadEventCount => Math.Max(0, ThreadEventCount - UnresolvedThreadEventReferences.Count);
|
|
public int StoryAssetCount => Package?.StoryAssets.Count ?? 0;
|
|
public int AssetStateCount => Package?.StoryAssets.Sum(asset => asset.States.Count) ?? 0;
|
|
public int AssetEventCount => Package?.StoryAssets.Sum(asset => asset.Events.Count) ?? 0;
|
|
public int AssetCustodyCount => Package?.StoryAssets.Count(asset => !string.IsNullOrWhiteSpace(asset.InitialOwnerCharacterName)) ?? 0;
|
|
public int ResolvedAssetStateCount => Math.Max(0, AssetStateCount - UnresolvedAssetReferences.Count(x => x.Contains("state", StringComparison.OrdinalIgnoreCase)));
|
|
public int ResolvedAssetEventCount => Math.Max(0, AssetEventCount - UnresolvedAssetReferences.Count(x => x.Contains("event", StringComparison.OrdinalIgnoreCase)));
|
|
public int ResolvedAssetCustodyCount => Math.Max(0, AssetCustodyCount - Package?.StoryAssets.Count(asset => !string.IsNullOrWhiteSpace(asset.InitialOwnerCharacterName) && UnmatchedAssetOwnerNames.Contains(asset.InitialOwnerCharacterName.Trim(), StringComparer.OrdinalIgnoreCase)) ?? 0);
|
|
public int SceneDependencyCount => Package?.SceneDependencies.Count ?? 0;
|
|
public int ResolvedSceneDependencyCount => Math.Max(0, SceneDependencyCount - UnresolvedSceneDependencyReferences.Count);
|
|
public int ChapterCount => Package?.Books.Sum(book => book.Chapters.Count) ?? 0;
|
|
public int SceneCount => Package?.Books.Sum(book => book.Chapters.Sum(chapter => chapter.Scenes.Count)) ?? 0;
|
|
public int SceneCharacterAppearanceCount => Package?.Books.Sum(book => book.Chapters.Sum(chapter => chapter.Scenes.Sum(scene => scene.SceneCharacters.Count + AutoPovAppearanceCount(scene)))) ?? 0;
|
|
|
|
private static int AutoPovAppearanceCount(ImportSceneDto scene)
|
|
=> !string.IsNullOrWhiteSpace(scene.PovCharacterName)
|
|
&& scene.SceneCharacters.All(x => !string.Equals(x.CharacterName?.Trim(), scene.PovCharacterName.Trim(), StringComparison.OrdinalIgnoreCase))
|
|
? 1
|
|
: 0;
|
|
}
|
|
|
|
public sealed class ImportBookPreview
|
|
{
|
|
public ImportBookDto Book { get; init; } = new();
|
|
public int ChapterCount => Book.Chapters.Count;
|
|
public int SceneCount => Book.Chapters.Sum(chapter => chapter.Scenes.Count);
|
|
public IReadOnlyList<ImportChapterPreview> ChapterPreviews { get; init; } = [];
|
|
}
|
|
|
|
public sealed class ImportChapterPreview
|
|
{
|
|
public ImportChapterDto Chapter { get; init; } = new();
|
|
public int SceneCount => Chapter.Scenes.Count;
|
|
}
|
|
|
|
public sealed record ImportResult
|
|
{
|
|
public bool Succeeded { get; init; }
|
|
public int? ProjectID { get; init; }
|
|
public string ProjectName { get; init; } = string.Empty;
|
|
public int BooksCreated { get; init; }
|
|
public int ChaptersCreated { get; init; }
|
|
public int ScenesCreated { get; init; }
|
|
public int CharactersCreated { get; init; }
|
|
public int LocationsCreated { get; init; }
|
|
public int SceneCharacterAppearancesCreated { get; init; }
|
|
public int PlotLinesCreated { get; init; }
|
|
public int PlotThreadsCreated { get; init; }
|
|
public int ThreadEventsCreated { get; init; }
|
|
public int StoryAssetsCreated { get; init; }
|
|
public int AssetStatesCreated { get; init; }
|
|
public int AssetEventsCreated { get; init; }
|
|
public int AssetCustodyEventsCreated { get; init; }
|
|
public int SceneDependenciesCreated { get; init; }
|
|
public bool IsDryRun { get; init; }
|
|
public int ErrorCount { get; init; }
|
|
public int WarningCount { get; init; }
|
|
public int AutoCorrectionCount { get; init; }
|
|
public int SkippedItemCount { get; init; }
|
|
public int UnresolvedReferenceCount { get; init; }
|
|
public IReadOnlyList<string> AutoCorrections { get; init; } = [];
|
|
public IReadOnlyList<string> SkippedOptionalFields { get; init; } = [];
|
|
public IReadOnlyList<string> UnresolvedWarnings { get; init; } = [];
|
|
public string? TechnicalDetail { get; init; }
|
|
public string Message { get; init; } = string.Empty;
|
|
}
|
|
|
|
public sealed class ImportImportanceJsonConverter : JsonConverter<int?>
|
|
{
|
|
public override int? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options)
|
|
{
|
|
if (reader.TokenType == JsonTokenType.Null)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (reader.TokenType == JsonTokenType.Number && reader.TryGetInt32(out var numeric))
|
|
{
|
|
return Math.Clamp(numeric, 1, 10);
|
|
}
|
|
|
|
if (reader.TokenType != JsonTokenType.String)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
var text = reader.GetString()?.Trim();
|
|
if (string.IsNullOrWhiteSpace(text))
|
|
{
|
|
return null;
|
|
}
|
|
|
|
if (int.TryParse(text, out var parsed))
|
|
{
|
|
return Math.Clamp(parsed, 1, 10);
|
|
}
|
|
|
|
var normalized = text.ToLowerInvariant();
|
|
if (normalized.Contains("major") || normalized.Contains("main") || normalized.Contains("protagonist"))
|
|
{
|
|
return 10;
|
|
}
|
|
|
|
if (normalized.Contains("support") || normalized.Contains("secondary"))
|
|
{
|
|
return 6;
|
|
}
|
|
|
|
if (normalized.Contains("minor") || normalized.Contains("background") || normalized.Contains("cameo"))
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public override void Write(Utf8JsonWriter writer, int? value, JsonSerializerOptions options)
|
|
{
|
|
if (value.HasValue)
|
|
{
|
|
writer.WriteNumberValue(value.Value);
|
|
}
|
|
else
|
|
{
|
|
writer.WriteNullValue();
|
|
}
|
|
}
|
|
}
|