PlotDirector/PlotLine/Models/IllustrationLibraryModels.cs

264 lines
8.9 KiB
C#

using System.Text.Json.Serialization;
namespace PlotLine.Models;
public static class IllustrationLibraryCategories
{
public const string Character = "Character";
public const string Location = "Location";
public const string Asset = "Asset";
public static readonly IReadOnlyList<string> All = [Character, Location, Asset];
public static bool IsValid(string? category)
=> All.Any(item => string.Equals(item, category, StringComparison.OrdinalIgnoreCase));
public static string StorageFolder(string category)
=> category switch
{
Character => "characters",
Location => "locations",
Asset => "assets",
_ => throw new ArgumentOutOfRangeException(nameof(category), category, "Unknown illustration category.")
};
}
public static class IllustrationLibraryStatuses
{
public const string Planned = "Planned";
public const string Queued = "Queued";
public const string Generating = "Generating";
public const string Generated = "Generated";
public const string Approved = "Approved";
public const string Rejected = "Rejected";
public const string Failed = "Failed";
public const string Superseded = "Superseded";
public const string Disabled = "Disabled";
public static readonly IReadOnlySet<string> All = new HashSet<string>(StringComparer.OrdinalIgnoreCase)
{
Planned,
Queued,
Generating,
Generated,
Approved,
Rejected,
Failed,
Superseded,
Disabled
};
public static bool IsValid(string? status)
=> !string.IsNullOrWhiteSpace(status) && All.Contains(status);
public static bool CanTransition(string from, string to)
=> (from, to) switch
{
(Planned, Queued) => true,
(Queued, Generating) => true,
(Generating, Generated) => true,
(Generating, Failed) => true,
(Generated, Approved) => true,
(Generated, Rejected) => true,
(Approved, Superseded) => true,
(Rejected, Queued) => true,
(Failed, Queued) => true,
(Approved, Disabled) => true,
(Disabled, Queued) => true,
_ when string.Equals(from, to, StringComparison.OrdinalIgnoreCase) => true,
_ => false
};
}
public sealed class IllustrationGenerationSpecification
{
public string Category { get; init; } = string.Empty;
public string Code { get; init; } = string.Empty;
public string Title { get; init; } = string.Empty;
public string Description { get; init; } = string.Empty;
public string VisualSubject { get; init; } = string.Empty;
public string Composition { get; init; } = string.Empty;
public string Mood { get; init; } = string.Empty;
public string? ApparentAgeBand { get; init; }
public string? Presentation { get; init; }
public string? HairColour { get; init; }
public string? HairLength { get; init; }
public string? SkinTone { get; init; }
public string? ClothingEra { get; init; }
public string? ClothingStyle { get; init; }
public string? LocationType { get; init; }
public string? Era { get; init; }
public string? Setting { get; init; }
public string? ObjectType { get; init; }
public string? Colour { get; init; }
public IReadOnlyList<string> Features { get; init; } = [];
public IReadOnlyList<string> Palette { get; init; } = [];
public IReadOnlyList<string> MetadataTags { get; init; } = [];
public Dictionary<string, string> Metadata { get; init; } = new(StringComparer.OrdinalIgnoreCase);
public IReadOnlyList<string> Validate()
{
var errors = new List<string>();
if (!IllustrationLibraryCategories.IsValid(Category))
{
errors.Add("Category must be Character, Location or Asset.");
}
if (string.IsNullOrWhiteSpace(Code))
{
errors.Add("Stable code is required.");
}
if (string.IsNullOrWhiteSpace(Title))
{
errors.Add("Title is required.");
}
if (string.IsNullOrWhiteSpace(Mood))
{
errors.Add("Mood is required.");
}
if (Category == IllustrationLibraryCategories.Character)
{
Require(ApparentAgeBand, "Apparent age band is required for characters.");
Require(Presentation, "Presentation is required for characters.");
}
else if (Category == IllustrationLibraryCategories.Location)
{
Require(LocationType, "Location type is required for locations.");
}
else if (Category == IllustrationLibraryCategories.Asset)
{
Require(ObjectType, "Object type is required for assets.");
}
return errors;
void Require(string? value, string message)
{
if (string.IsNullOrWhiteSpace(value))
{
errors.Add(message);
}
}
}
}
public sealed class IllustrationLibraryItem
{
public int IllustrationLibraryItemID { get; set; }
public string Category { get; set; } = string.Empty;
public string StableCode { get; set; } = string.Empty;
public string DisplayTitle { get; set; } = string.Empty;
public string? ShortDescription { get; set; }
public string? MetadataJson { get; set; }
public string? SpecificationJson { get; set; }
public string? FilePath { get; set; }
public string? ThumbnailPath { get; set; }
public int? Width { get; set; }
public int? Height { get; set; }
public long? FileSizeBytes { get; set; }
public string? MimeType { get; set; }
public string Status { get; set; } = IllustrationLibraryStatuses.Planned;
public bool IsActive { get; set; }
public bool IsApproved { get; set; }
public DateTime CreatedUtc { get; set; }
public DateTime UpdatedUtc { get; set; }
public DateTime? ApprovedUtc { get; set; }
public int? ApprovedByUserID { get; set; }
public string? Provider { get; set; }
public string? Model { get; set; }
public string? PromptText { get; set; }
public string? Prompt { get; set; }
public string? PromptTemplateVersion { get; set; }
public string? ProviderRequestID { get; set; }
public int AttemptCount { get; set; }
public int GenerationAttemptCount { get; set; }
public int? ParentItemID { get; set; }
public string? RejectionReason { get; set; }
public DateTime? RejectedUtc { get; set; }
public int? RejectedByUserID { get; set; }
public string? AdminNotes { get; set; }
public int UsageCount { get; set; }
public string? LastError { get; set; }
public string? ErrorMessage { get; set; }
public DateTime? QueuedUtc { get; set; }
public DateTime? StartedUtc { get; set; }
public DateTime? CompletedUtc { get; set; }
}
public sealed class IllustrationLibrarySummary
{
public int TotalCount { get; set; }
public int PlannedCount { get; set; }
public int QueuedCount { get; set; }
public int GeneratingCount { get; set; }
public int GeneratedCount { get; set; }
public int ApprovedCount { get; set; }
public int RejectedCount { get; set; }
public int FailedCount { get; set; }
public int SupersededCount { get; set; }
public int DisabledCount { get; set; }
}
public sealed class IllustrationLibraryFilter
{
public string? Category { get; init; }
public string? Status { get; init; }
public string? Search { get; init; }
}
public sealed record IllustrationPromptBuildResult(
string Prompt,
string TemplateVersion,
string SpecificationJson,
string MetadataJson);
public sealed record IllustrationImageGenerationRequest(
int IllustrationLibraryItemID,
string StableCode,
string Category,
string Prompt,
string TemplateVersion);
public sealed record IllustrationImageGenerationResult(
bool Succeeded,
byte[]? ImageBytes = null,
string? MimeType = null,
string? Provider = null,
string? Model = null,
string? ProviderRequestID = null,
string? ErrorMessage = null)
{
[JsonIgnore]
public bool HasImage => ImageBytes is { Length: > 0 };
}
public sealed record IllustrationStoredImage(
string FilePath,
string ThumbnailPath,
int Width,
int Height,
long FileSizeBytes,
string MimeType);
public sealed record IllustrationLibraryActionResult(bool Succeeded, string Message);
public sealed record IllustrationBulkRetryResult(
int QueuedCount,
int SkippedCount,
int RetryLimitReachedCount)
{
public string Message
=> $"{QueuedCount:N0} failed illustration(s) queued. {SkippedCount:N0} skipped. {RetryLimitReachedCount:N0} reached the retry limit.";
}
public sealed record IllustrationStarterGenerationPlan(
int CharactersToGenerate,
int LocationsToGenerate,
int AssetsToGenerate)
{
public int TotalToGenerate => CharactersToGenerate + LocationsToGenerate + AssetsToGenerate;
}