diff --git a/.editorconfig b/.editorconfig index b075827..6283c94 100644 --- a/.editorconfig +++ b/.editorconfig @@ -4,6 +4,11 @@ root = true # Файлы C# [*.cs] +#### Resharper #### + +resharper_member_can_be_private_global_highlighting = none + + #### Основные параметры EditorConfig #### # Отступы и интервалы @@ -264,4 +269,5 @@ dotnet_style_namespace_match_folder = true:suggestion dotnet_code_quality_unused_parameters = all:suggestion dotnet_style_operator_placement_when_wrapping = beginning_of_line tab_width = 4 -indent_size = 4 \ No newline at end of file +indent_size = 4 +dotnet_diagnostic.CA1822.severity = none \ No newline at end of file diff --git a/Pf2eDataScrapper.sln b/Pf2eDataScrapper.sln index bd0d4fb..c19e6f3 100644 --- a/Pf2eDataScrapper.sln +++ b/Pf2eDataScrapper.sln @@ -10,6 +10,7 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Pf2eDataScrapper", "Pf2eDat EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Элементы решения", "Элементы решения", "{754FC069-D67B-A9D7-50A1-8D1CA196D8F1}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig README.md = README.md EndProjectSection EndProject diff --git a/Pf2eDataScrapper/Importers/DatabaseHelper.cs b/Pf2eDataScrapper/Importers/DatabaseHelper.cs index d36220f..14f5366 100644 --- a/Pf2eDataScrapper/Importers/DatabaseHelper.cs +++ b/Pf2eDataScrapper/Importers/DatabaseHelper.cs @@ -5,21 +5,17 @@ namespace Pf2eDataScrapper.Importers; public class DatabaseHelper { - public Pf2eDbContext Context = new(); + public readonly Pf2eDbContext Context = new(); - public SlugHelper Slugger = new(); + public readonly SlugHelper Slugger = new(); - private int _id = 0; + private int _id; - public int NextEntityId - { - get => ++_id; - set => _id = value; - } + public int NextEntityId => ++_id; private DatabaseHelper() { - NextEntityId = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0; + _id = Context.Entities.Any() ? Context.Entities.Max(e => e.Id) : 0; } public string GenerateSlug(string name) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdLoader.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdLoader.cs index 3c70291..6813181 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdLoader.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/AonprdLoader.cs @@ -40,7 +40,7 @@ internal class AonprdLoader(AonprdImporter importer) public IEnumerable Entries(string[] types) { - foreach (string? file in Compendium.Files(types)) + foreach (string file in Compendium.Files(types)) { using StreamReader fileReader = new(Path.Combine(Importer.Dir, $"{file}.json")); JArray array = JArray.Parse(fileReader.ReadToEnd()); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs index eb5e402..70699af 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs @@ -1,13 +1,13 @@ using Konsole; +using Newtonsoft.Json.Linq; using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium; using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; -using static Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.AbstractProcessor; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; internal class AonprdDataBuilder(AonprdLoader loader) { - public Pf2eDatabase Database = Pf2eDatabase.Instance; + public readonly Pf2eDatabase Database = Pf2eDatabase.Instance; /// /// Загрузчик данных. @@ -19,7 +19,7 @@ internal class AonprdDataBuilder(AonprdLoader loader) /// public AonprdCompendium Compendium => Loader.Compendium; - public AonprdProcessorCollection Processors = new(); + public readonly AonprdProcessorCollection Processors = new(); public void TruncateTables() { @@ -30,64 +30,79 @@ internal class AonprdDataBuilder(AonprdLoader loader) Console.WriteLine("Truncation completed!"); } - public void Build() + protected ConcurrentWriter Window = null!; + + protected Window Output = null!; + + protected ProgressBar ProcessProgressBar = null!; + + protected ProgressBar SubProcessProgressBar = null!; + + protected void ConfigureWindow() { - Console.WriteLine("Building data..."); + Window = new Window(0, 0, Console.WindowWidth, Console.WindowHeight).Concurrent(); - ConcurrentWriter window = new Window( - 0, - 0, - Console.WindowWidth, - Console.WindowHeight - ).Concurrent(); - - IConsole bars = window.OpenBox( + IConsole bars = Window.OpenBox( "Progress", Console.WindowWidth, 4, - new BoxStyle { Body = Colors.WhiteOnBlack } + new() { Body = Colors.WhiteOnBlack } ); - Window output = new(0, 4, Console.WindowWidth, Console.WindowHeight - 4, window); + Output = new(0, 4, Console.WindowWidth, Console.WindowHeight - 4, Window); - ProgressBar bar1 = new(bars, Processors.Items.Count, 15); - ProgressBar bar2 = new(bars, 1, 15); - int i = 0; + ProcessProgressBar = new(bars, Processors.Items.Count, 15); + + SubProcessProgressBar = new(bars, 1, 15); + } + + protected void RunProcessor(AonprdProcessorCollection.ProcessorItem item, JObject json) + { + AonprdProcessorInput input = new(json); + AbstractProcessor processor = (AbstractProcessor)item.Constructor.Invoke([input]); + + try + { + processor.Process(); + processor.Save(); + } + catch (LegacyItemException ex) + { + Output.WriteLine(ex.Message); + } + catch (SkipItemException ex) + { + Output.WriteLine(ex.Message); + } + } + + public void Build() + { + ConfigureWindow(); foreach (AonprdProcessorCollection.ProcessorItem item in Processors.Iterate()) { - bar1.Refresh(i++, $"{item.Attribute.Name}"); + ProcessProgressBar.Next(item.Attribute.Name); - bar2.Max = Compendium.Count([item.Attribute.Type]); - int j = 0; + if (item.Attribute.Type is null) + { + RunProcessor(item, []); + continue; + } + + SubProcessProgressBar.Max = Compendium.Count([item.Attribute.Type]); foreach (AonprdLoader.LoaderEntry entry in Loader.Entries(item.Attribute.Type)) { - bar2.Refresh(j++, $"{entry.Id}"); - - AonprdProcessorInput input = new(entry.Json); - AbstractProcessor processor = (AbstractProcessor)item.Constructor.Invoke([input]); - - try - { - processor.Process(); - processor.Save(); - } - catch (LegacyItemException ex) - { - output.WriteLine(ex.Message); - } - catch (SkipItemException ex) - { - output.WriteLine(ex.Message); - } + SubProcessProgressBar.Next($"{entry.Id}"); + RunProcessor(item, entry.Json); } - bar2.Refresh(j, "Processor completed"); + SubProcessProgressBar.Next("Ready"); } - bar1.Refresh(i, "Done!"); + ProcessProgressBar.Next("Done!"); - window.Clear(); + Window.Clear(); } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorAttribute.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs similarity index 77% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorAttribute.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs index 4b2c973..594ff4d 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorAttribute.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs @@ -1,11 +1,11 @@ -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; [AttributeUsage(AttributeTargets.Class)] -public class AonprdProcessorAttribute(string name, string type, string[] dependencies) : Attribute +public class AonprdProcessorAttribute(string name, string? type, string[] dependencies) : Attribute { public readonly string Name = name; - public readonly string Type = type; + public readonly string? Type = type; public readonly IEnumerable Dependencies = dependencies; diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorCollection.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs similarity index 92% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorCollection.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs index d50c117..c10f7d1 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorCollection.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs @@ -1,6 +1,7 @@ using System.Reflection; +using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; internal class AonprdProcessorCollection { @@ -41,13 +42,13 @@ internal class AonprdProcessorCollection ); if (collision is not null) { - throw new Exception( + throw new( $"Multiply attributes 'AonprdProcessor' have same name '{attribute.Name}'." + $" They are added to classes: '{type.FullName}' and '{collision.Constructor.DeclaringType!.FullName}'" ); } - Items.Add(new ProcessorItem(attribute, constructor)); + Items.Add(new(attribute, constructor)); } } @@ -65,7 +66,7 @@ internal class AonprdProcessorCollection } IEnumerable dependencyPriorities = item.Attribute.Dependencies.Select(d => - priorities.TryGetValue(d, out int value) ? value : -1 + priorities.GetValueOrDefault(d, -1) ); if (dependencyPriorities.Any(p => p == -1)) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorInput.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorInput.cs index 2e5e3e2..67e9b3f 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorInput.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorInput.cs @@ -1,4 +1,5 @@ using Newtonsoft.Json.Linq; +using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; @@ -84,10 +85,10 @@ internal class AonprdProcessorInput(JObject input) : ProcessorInput(input) public class ActionStruct { - public int Actions = 0; - public int MaxActions = 0; - public bool IsPassive = false; - public bool IsReaction = false; - public bool IsFreeAction = false; + public int Actions; + public int MaxActions; + public bool IsPassive; + public bool IsReaction; + public bool IsFreeAction; } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdBuilderGlobals.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdBuilderGlobals.cs new file mode 100644 index 0000000..d5b35a9 --- /dev/null +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdBuilderGlobals.cs @@ -0,0 +1,12 @@ +using Pf2eModel.Model.Base.Targets; + +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; + +internal class AonprdBuilderGlobals +{ + public ExternalTarget AonprdTarget = null!; + + private AonprdBuilderGlobals() { } + + public static AonprdBuilderGlobals Instance = new AonprdBuilderGlobals(); +} diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdEnumResolver.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdEnumResolver.cs similarity index 99% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdEnumResolver.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdEnumResolver.cs index a8cf14d..8b3af91 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdEnumResolver.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/AonprdEnumResolver.cs @@ -1,6 +1,6 @@ using Pf2eModel.Model.Enum; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; public static class AonprdEnumResolver { diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/StringHelper.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/StringHelper.cs similarity index 55% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/StringHelper.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/StringHelper.cs index 5457558..9968b36 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/StringHelper.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Helpers/StringHelper.cs @@ -1,16 +1,17 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; +using System.Text.RegularExpressions; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; internal partial class StringHelper { private StringHelper() { } + public string ResolveSource(string input) + { + Match match = SourceSuffixRegex().Match(input); + return match.Captures.Count > 0 ? match.Captures[0].Value : ""; + } + public static StringHelper Instance { get; } = new(); [GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")] diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractEntityProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractEntityProcessor.cs new file mode 100644 index 0000000..6677144 --- /dev/null +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractEntityProcessor.cs @@ -0,0 +1,224 @@ +using System.Security.Cryptography; +using System.Text; +using Microsoft.EntityFrameworkCore; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using Pf2eModel; +using Pf2eModel.Model; +using Pf2eModel.Model.Base; +using Pf2eModel.Model.Base.Source; +using Pf2eModel.Model.Base.Targets; +using Pf2eModel.Model.Common.I18n; +using Pf2eModel.Model.Enum; +using Pf2eModel.Model.Structure; + +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; + +internal abstract class AbstractEntityProcessor(AonprdProcessorInput input) + : AbstractProcessor(input) +{ + public Entity Entity { get; } = new(); + + public abstract EntityType Type { get; } + + public abstract IEntity Object { get; } + + public abstract void Add(); + + public override void Process() + { + if (Input.GetOptional("remaster_id") is not null) + { + string id = Input.GetRequired("id"); + string name = Input.GetRequired("name"); + + ProcessLegacy(id, name); + throw new LegacyItemException(name); + } + + AddEntity(); + AddAlias(); + AddSource(); + AddExternal(); + AddTraits(); + } + + public override void Save() + { + Add(); + Context.SaveChanges(); + } + + protected string AddedDescription(string path) => + $"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional(path) ?? ""}"; + + protected string AddedDescription(string[] paths) => + string.Join("", paths.Select(AddedDescription)); + + protected virtual string GetDescription() => Input.GetRequired("markdown"); + + protected virtual void AddEntity() + { + Entity.Id = Helper.NextEntityId; + Entity.Type = Type; + Entity.Name[LocaleType.English] = Input.GetRequired("name"); + Entity.Img = ""; + Entity.Description = GetDescription(); + Entity.Rarity = Input.EnumOptional("rarity", EntityRarity.Common); + Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]); + Entity.IsTranslated = false; + Entity.IsChecked = false; + + Object.Entity = Entity; + Object.Id = Entity.Id; + + Context.Entities.Add(Entity); + } + + protected virtual void AddAlias() + { + JArray? legacyIdArray = Input.GetOptional("legacy_id"); + + if (legacyIdArray is null) + { + return; + } + + string legacyId = legacyIdArray[0].ToString(); + + if (LegacyAliases.TryGetValue(legacyId, out string? legacyName)) + { + // if (legacyName != Entity.Name[LocaleType.English]) + Entity.Name[LocaleType.LegacyEnglish] = legacyName; + + LegacyAliases.Remove(legacyId); + } + else + { + RemasterWithoutLegacy.Add(legacyId, Entity); + } + } + + protected virtual void AddSource() + { + string name = Input.GetRequired("source[0]"); + + Source? source = Context.Sources.FirstOrDefault(s => s.Name == name); + + if (source == null) + { + source = new() + { + Name = name, + Category = Input.EnumRequired("primary_source_category"), + ReleaseDate = DateOnly.Parse(Input.GetRequired("release_date")), + }; + + Context.Sources.Add(source); + } + + EntitySource entitySource = new() + { + Entity = Entity, + Source = source, + Suffix = StringHelper.ResolveSource(Input.GetRequired("source_markdown")), + IsRemaster = Input.GetOptional("legacy_id") is not null, + }; + + Context.EntitySources.Add(entitySource); + } + + protected virtual void AddExternal() + { + EntityExternal external = new() + { + Entity = Entity, + Target = Globals.AonprdTarget, + Suffix = Input.GetRequired("url"), + Md5 = Md5(Input.Input.ToString()), + }; + + Context.EntityExternals.Add(external); + } + + protected virtual void AddTraits() + { + JToken? traitsJson = Input.Input.SelectToken("trait"); + + if (traitsJson?.Type != JTokenType.Array) + { + return; + } + + IEnumerable names = traitsJson.ToArray().Select(jToken => jToken.ToString()); + List traitEntities = Context + .Traits.Select(t => t.Entity) + // ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall + .Where(e => Pf2eDbContext.LocaleSearchArray(e.Name, names)) + .ToList(); + + List traits = Context + .Traits.Include(t => t.Entity) + .ThenInclude(entity => entity.Name) + .Where(t => traitEntities.Contains(t.Entity)) + .ToList(); + + foreach (string name in names) + { + if (!traits.Any(t => t.Entity.Name.Contains(name))) + { + throw new Exception($"Trait '{name}' is not found!!!"); + } + } + + foreach (Trait trait in traits) + { + Entity.Traits.Add(trait); + } + } + + protected void ProcessLegacy(string id, string name) + { + if (RemasterWithoutLegacy.TryGetValue(id, out Entity? remastered)) + { + // if (remastered.ToString() != name) + remastered.Name[LocaleType.LegacyEnglish] = name; + + RemasterWithoutLegacy.Remove(id); + } + else + { + LegacyAliases.Add(id, name); + } + } + + public string GenerateExtra(params string[] args) + { + Dictionary data = []; + + foreach (string key in args) + { + data.Add(key, Input.Input.SelectToken(key)); + } + + return JsonConvert.SerializeObject(data, new JsonSerializerSettings()); + } + + public static string Md5(string input, bool isLowercase = false) + { + using MD5 md5 = MD5.Create(); + byte[] byteHash = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); + string hash = BitConverter.ToString(byteHash).Replace("-", ""); + return isLowercase ? hash.ToLower() : hash; + } + + protected static readonly Dictionary LegacyAliases = []; + + protected static readonly Dictionary RemasterWithoutLegacy = []; +} + +public class LegacyItemException(string name) + : Exception($"Item '{name}' is linked with newer version, skipping.") { } + +public class SkipItemException(string name, string reason) + : Exception($"Item '{name}' is skipped, reason: {reason}.") { } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs index 0d62902..1f35a70 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs @@ -1,250 +1,21 @@ -using System.Security.Cryptography; -using System.Text; -using System.Text.RegularExpressions; -using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; +using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Helpers; using Pf2eModel; -using Pf2eModel.Model; -using Pf2eModel.Model.Base; -using Pf2eModel.Model.Base.Source; -using Pf2eModel.Model.Base.Targets; -using Pf2eModel.Model.Common.I18n; -using Pf2eModel.Model.Enum; -using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; -internal abstract partial class AbstractProcessor(AonprdProcessorInput input) +internal abstract class AbstractProcessor(AonprdProcessorInput input) { - protected DatabaseHelper Helper = DatabaseHelper.Instance; + protected readonly DatabaseHelper Helper = DatabaseHelper.Instance; - protected StringHelper StringHelper = StringHelper.Instance; + protected readonly StringHelper StringHelper = StringHelper.Instance; + + protected readonly AonprdBuilderGlobals Globals = AonprdBuilderGlobals.Instance; public readonly Pf2eDbContext Context = DatabaseHelper.DbContext; public readonly AonprdProcessorInput Input = input; - public Entity Entity { get; set; } = new(); + public abstract void Process(); - public abstract EntityType Type { get; } - - public abstract IEntity Object { get; } - - protected abstract void Add(); - - public virtual void Process() - { - if (Input.GetOptional("remaster_id") is not null) - { - string id = Input.GetRequired("id"); - string name = Input.GetRequired("name"); - - ProcessLegacy(id, name); - throw new LegacyItemException(name); - } - - AddEntity(); - AddAlias(); - AddSource(); - AddExternal(); - AddTraits(); - } - - public void Save() - { - Add(); - Context.SaveChanges(); - } - - protected string AddedDescription(string path) => - $"\r\n\r\n========== [{path}] ==========\r\n\r\n{Input.GetOptional(path) ?? ""}"; - - protected string AddedDescription(string[] paths) => - string.Join("", paths.Select(AddedDescription)); - - protected virtual string GetDescription() => Input.GetRequired("markdown"); - - protected virtual void AddEntity() - { - Entity.Id = Helper.NextEntityId; - Entity.Type = Type; - Entity.Name[LocaleType.English] = Input.GetRequired("name"); - Entity.Img = ""; - Entity.Description = GetDescription(); - Entity.Rarity = Input.EnumOptional("rarity", EntityRarity.Common); - Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]); - Entity.IsTranslated = false; - Entity.IsChecked = false; - - Object.Entity = Entity; - Object.Id = Entity.Id; - - Context.Entities.Add(Entity); - } - - protected virtual void AddAlias() - { - JArray? legacyIdArray = Input.GetOptional("legacy_id"); - - if (legacyIdArray is null) - { - return; - } - - string legacyId = legacyIdArray[0].ToString(); - - if (LegacyAliases.TryGetValue(legacyId, out string? legacyName)) - { - // if (legacyName != Entity.Name[LocaleType.English]) - Entity.Name[LocaleType.LegacyEnglish] = legacyName; - - LegacyAliases.Remove(legacyId); - } - else - { - RemasterWithoutLegacy.Add(legacyId, Entity); - } - } - - protected virtual void AddSource() - { - string name = Input.GetRequired("source[0]"); - - Source? source = Context.Sources.FirstOrDefault(s => s.Name == name); - - if (source == null) - { - source = new() - { - Name = name, - Category = Input.EnumRequired("primary_source_category"), - ReleaseDate = DateOnly.Parse(Input.GetRequired("release_date")), - }; - - Context.Sources.Add(source); - } - string sourceMarkdown = Input.GetRequired("source_markdown"); - Match match = SourceSuffixRegex().Match(sourceMarkdown); - - EntitySource entitySource = new() - { - Entity = Entity, - Source = source, - Suffix = ((match?.Captures.Count > 0) ? match?.Captures[0]?.Value : "") ?? "", - IsRemaster = Input.GetOptional("legacy_id") is not null, - }; - - Context.EntitySources.Add(entitySource); - } - - protected virtual void AddExternal() - { - if (AonprdTarget is null) - { - AonprdTarget = Context.ExternalTargets.FirstOrDefault(t => - t.Name == AonprdImporter.SourceName - ); - - AonprdTarget ??= new() - { - Name = AonprdImporter.SourceName, - Icon = "http://www.google.com/s2/favicons?domain=2e.aonprd.com", - Prefix = "https://2e.aonprd.com", - }; - } - - EntityExternal external = new() - { - Entity = Entity, - Target = AonprdTarget, - Suffix = Input.GetRequired("url"), - Md5 = Md5(Input.Input.ToString()), - }; - Context.EntityExternals.Add(external); - } - - protected virtual void AddTraits() - { - JToken? traitsJson = Input.Input.SelectToken("trait"); - - if (traitsJson?.Type != JTokenType.Array) - { - return; - } - - IEnumerable names = traitsJson.ToArray().Select(jToken => jToken.ToString()); - List traitEntities = Context - .Traits.Select(t => t.Entity) - .Where(e => Pf2eDbContext.LocaleSearchArray(e.Name, names)) - .ToList(); - - List traits = Context - .Traits.Include(t => t.Entity) - .Where(t => traitEntities.Contains(t.Entity)) - .ToList(); - - foreach (string name in names) - { - if (!traits.Any(t => t.Entity.Name.Contains(name))) - { - throw new Exception($"Trait '{name}' is not found!!!"); - } - } - - foreach (Trait trait in traits) - { - Entity.Traits.Add(trait); - } - } - - protected void ProcessLegacy(string id, string name) - { - if (RemasterWithoutLegacy.TryGetValue(id, out Entity? remastered)) - { - // if (remastered.ToString() != name) - remastered.Name[LocaleType.LegacyEnglish] = name; - - RemasterWithoutLegacy.Remove(id); - } - else - { - LegacyAliases.Add(id, name); - } - } - - public string GenerateExtra(params string[] args) - { - Dictionary data = []; - - foreach (string key in args) - { - data.Add(key, Input.Input.SelectToken(key)); - } - - return JsonConvert.SerializeObject(data, new JsonSerializerSettings() { }); - } - - public static string Md5(string input, bool isLowercase = false) - { - using MD5 md5 = MD5.Create(); - byte[] byteHash = md5.ComputeHash(Encoding.UTF8.GetBytes(input)); - string hash = BitConverter.ToString(byteHash).Replace("-", ""); - return isLowercase ? hash.ToLower() : hash; - } - - protected static ExternalTarget? AonprdTarget = null; - - protected static Dictionary LegacyAliases = []; - - protected static Dictionary RemasterWithoutLegacy = []; - - [GeneratedRegex(@"^\[.*?\]\(.*?\)\s+(.*)$")] - private static partial Regex SourceSuffixRegex(); - - public class LegacyItemException(string name) - : Exception($"Item '{name}' is linked with newer version, skipping.") { } - - public class SkipItemException(string name, string reason) - : Exception($"Item '{name}' is skipped, reason: {reason}.") { } + public abstract void Save(); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractSubclassProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractSubclassProcessor.cs index e9d34db..11f52fd 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractSubclassProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractSubclassProcessor.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; -using Pf2eModel; -using Pf2eModel.Model; +using Pf2eModel; using Pf2eModel.Model.Base; using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; @@ -13,7 +7,7 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input) - : AbstractProcessor(input) + : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Subclass; @@ -23,7 +17,7 @@ internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input) public abstract string GroupName { get; } - protected override void Add() => Context.Subclasses.Add(Object); + public override void Add() => Context.Subclasses.Add(Object); public override void Process() { @@ -37,51 +31,40 @@ internal abstract class AbstractSubclassProcessor(AonprdProcessorInput input) public SubclassGroup GetGroup() { SubclassGroup? group = Context.SubclassGroups.FirstOrDefault(g => + // ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall Pf2eDbContext.LocaleSearch(g.Entity.Name, GroupName) ); - if (group is null) + if (group is not null) { - Class? cl = - (ClassName is not null) - ? Context.Classes.FirstOrDefault(c => - Pf2eDbContext.LocaleSearch(c.Entity.Name, GroupName) - ) - : null; - - Entity groupEntity = new() - { - Id = Helper.NextEntityId, - Type = EntityType.SubclassGroup, - Img = "", - Description = "", - Rarity = EntityRarity.Common, - IsTranslated = false, - IsChecked = false, - }; - - groupEntity.Name[LocaleType.English] = GroupName; - groupEntity.Slug = Helper.GenerateSlug(GroupName); - Context.Entities.Add(groupEntity); - - /* - - Entity.Id = Helper.NextEntityId; - Entity.Type = Type; - Entity.Name[LocaleType.English] = Input.GetRequired("name"); - Entity.Img = ""; - Entity.Description = GetDescription(); - Entity.Rarity = Input.EnumOptional("rarity", EntityRarity.Common); - Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]); - Entity.IsTranslated = false; - Entity.IsChecked = false; - - */ - - group = new SubclassGroup() { Entity = groupEntity, Class = cl }; - Context.SubclassGroups.Add(group); + return group; } + Class? cl = ClassName is not null + ? Context.Classes.FirstOrDefault(c => + // ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall + Pf2eDbContext.LocaleSearch(c.Entity.Name, GroupName) + ) + : null; + + Entity groupEntity = new() + { + Id = Helper.NextEntityId, + Type = EntityType.SubclassGroup, + Img = "", + Description = "", + Rarity = EntityRarity.Common, + IsTranslated = false, + IsChecked = false, + Name = { [LocaleType.English] = GroupName }, + Slug = Helper.GenerateSlug(GroupName), + }; + + Context.Entities.Add(groupEntity); + + group = new() { Entity = groupEntity, Class = cl }; + Context.SubclassGroups.Add(group); + return group; } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/ExternalProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/ExternalProcessor.cs new file mode 100644 index 0000000..d9a4ad8 --- /dev/null +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/ExternalProcessor.cs @@ -0,0 +1,26 @@ +using Pf2eModel.Model.Base.Targets; + +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Common; + +[AonprdProcessor("external", null, [])] +internal class ExternalProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +{ + public ExternalTarget AonprdTarget = null!; + + public override void Process() + { + AonprdTarget = new() + { + Name = AonprdImporter.SourceName, + Icon = "http://www.google.com/s2/favicons?domain=2e.aonprd.com", + Prefix = "https://2e.aonprd.com", + }; + } + + public override void Save() + { + Context.ExternalTargets.Add(AonprdTarget); + + Globals.AonprdTarget = AonprdTarget; + } +} diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SourceProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SourceProcessor.cs similarity index 56% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SourceProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SourceProcessor.cs index 8c16078..88a7224 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SourceProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/SourceProcessor.cs @@ -1,29 +1,25 @@ -using Pf2eModel.Model; -using Pf2eModel.Model.Base.Source; +using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Enum; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Common; -[AonprdProcessor("source", new string[] { })] +[AonprdProcessor("source", [])] internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(input) { - public override EntityType Type => EntityType.Unknown; - - public override IEntity? Object => null; - - protected override void Add() { } + protected Source Source = null!; public override void Process() { - // base.Process(); - - Source source = new() + Source = new() { Name = Input.GetRequired("name"), Category = Input.EnumRequired("primary_source_category"), ReleaseDate = DateOnly.Parse(Input.GetRequired("release_date")), }; + } - Context.Sources.Add(source); + public override void Save() + { + Context.Sources.Add(Source); } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/TrainingProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/TrainingProcessor.cs new file mode 100644 index 0000000..80342ce --- /dev/null +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Common/TrainingProcessor.cs @@ -0,0 +1,20 @@ +using Pf2eModel.Model.Common; +using Pf2eModel.Model.Enum; + +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Common; + +[AonprdProcessor("skill", [])] +internal class TrainingProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +{ + public Training Training = null!; + + public override void Process() + { + Training = new() { Name = Input.GetRequired("name"), Type = TrainingType.Skill }; + } + + public override void Save() + { + Context.Trainings.Add(Training); + } +} diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs index 84f824c..e3e01cb 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Subclasses/ApparitionProcessor.cs @@ -7,7 +7,7 @@ internal class ApparitionProcessor(AonprdProcessorInput input) : AbstractSubclas { public override Subclass Object { get; } = new(); - public override string? ClassName => "Animist"; + public override string ClassName => "Animist"; public override string GroupName => "Apparition"; diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs index 1b3cabe..9466b42 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs @@ -3,13 +3,13 @@ namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("action")] -internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +internal class ActionProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Action; public override Pf2eModel.Model.Structure.Action Object { get; } = new(); - protected override void Add() => Context.Actions.Add(Object); + public override void Add() => Context.Actions.Add(Object); public override void Process() { @@ -24,7 +24,7 @@ internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(i Object.IsFreeAction = action.IsFreeAction; Object.IsPassive = action.IsPassive; Object.IsReaction = action.IsReaction; - ; + Object.Extra = GenerateExtra("school"); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs index 01651ff..4571eef 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs @@ -1,19 +1,17 @@ -using LinqToDB; -using Newtonsoft.Json.Linq; +using Newtonsoft.Json.Linq; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; -using Pf2eModel.Model.Structure.AncestryRelation; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("ancestry", ["trait", "language"])] -internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +internal class AncestryProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Ancestry; public override Ancestry Object { get; } = new(); - protected override void Add() => Context.Ancestries.Add(Object); + public override void Add() => Context.Ancestries.Add(Object); public override void Process() { @@ -36,7 +34,7 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor Object.Speed = Convert.ToInt32(Input.GetOptional("speed.land")); - Object.Vision = Input.EnumOptional("vision", AncestryVision.Normal); + Object.Vision = Input.EnumOptional("vision", AncestryVision.Normal); AddBoosts("attribute", true); AddBoosts("attribute_flaw", false); @@ -59,14 +57,14 @@ internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor foreach (string boost in boosts) { - AncestryBoost ancestryBoost = new() - { - Ancestry = Object, - Boost = isBoost, - Value = Input.Enum(boost), - }; - - Context.AncestryBoosts.Add(ancestryBoost); + Context.AncestryBoosts.Add( + new() + { + Ancestry = Object, + Boost = isBoost, + Value = Input.Enum(boost), + } + ); } } } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs index 8166620..a514d97 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs @@ -4,13 +4,13 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("archetype")] -internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Archetype; public override Archetype Object { get; } = new(); - protected override void Add() => Context.Archetypes.Add(Object); + public override void Add() => Context.Archetypes.Add(Object); public override void Process() { diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs index b14b78c..7bf29a4 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs @@ -6,13 +6,13 @@ using Pf2eModel.Model.Structure.BackgroundRelation; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("background")] -internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Background; public override Background Object { get; } = new(); - protected override void Add() => Context.Backgrounds.Add(Object); + public override void Add() => Context.Backgrounds.Add(Object); public override void Process() { diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs index 2b20449..10ae2e4 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs @@ -1,6 +1,4 @@ using System.Text.RegularExpressions; -using LinqToDB; -using Microsoft.EntityFrameworkCore; using Newtonsoft.Json.Linq; using Pf2eModel.Model.Common; using Pf2eModel.Model.Enum; @@ -10,13 +8,13 @@ using Pf2eModel.Model.Structure.ClassRelation; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("class", ["trait", "source", "skill"])] -internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +internal class ClassProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Class; public override Class Object { get; } = new(); - protected override void Add() => Context.Classes.Add(Object); + public override void Add() => Context.Classes.Add(Object); public override void Process() { @@ -156,7 +154,6 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in .Where(s => s != "") .Select(s => string.Concat(s[0].ToString().ToUpper(), s.AsSpan(1))) .ToArray(); - ; } protected ICollection GetSaveTrainings() diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs index c40264c..993a957 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs @@ -6,13 +6,13 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; // [AonprdProcessor("feat", ["trait", "source", "archetype"])] -internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +internal class FeatProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Feat; public override Feat Object { get; } = new(); - protected override void Add() => Context.Feats.Add(Object); + public override void Add() => Context.Feats.Add(Object); public override void Process() { @@ -47,6 +47,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp Input.GetOptional("archetype")?.Select(t => t.ToString()).ToArray() ?? []; Archetype[] archetypes = Context + // ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall .Archetypes.Where(a => Pf2eDbContext.LocaleSearchArray(a.Entity.Name, archetypeStrings)) // .Where(a => archetypeStrings.Contains(a.Entity.Name.ToString())) .ToArray(); @@ -56,7 +57,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp Object.Category = ActionCategory.Archetype; } - foreach (Archetype? archetype in archetypes) + foreach (Archetype archetype in archetypes) { Object.Archetypes.Add(archetype); diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs index 0538f97..10599e7 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs @@ -4,16 +4,11 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("language")] -internal class LanguageProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +internal class LanguageProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Language; public override Language Object { get; } = new(); - protected override void Add() => Context.Languages.Add(Object); - - public override void Process() - { - base.Process(); - } + public override void Add() => Context.Languages.Add(Object); } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TrainingProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TrainingProcessor.cs deleted file mode 100644 index eca7c59..0000000 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TrainingProcessor.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Pf2eModel.Model; -using Pf2eModel.Model.Common; -using Pf2eModel.Model.Enum; - -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; - -[AonprdProcessor("skill", new string[] { })] -internal class TrainingProcessor(AonprdProcessorInput input) : AbstractProcessor(input) -{ - public override EntityType Type => EntityType.Unknown; - - public override IEntity? Object => null; - - protected override void Add() { } - - public override void Process() - { - // base.Process(); - - Training training = new() - { - Name = Input.GetRequired("name"), - Type = TrainingType.Skill, - }; - - Context.Trainings.Add(training); - } -} diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs index e4b2b06..6bbf758 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs @@ -3,14 +3,14 @@ using Pf2eModel.Model.Structure; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; -[AonprdProcessor("trait", new string[] { })] -internal class TraitProcessor(AonprdProcessorInput input) : AbstractProcessor(input) +[AonprdProcessor("trait", ["external"])] +internal class TraitProcessor(AonprdProcessorInput input) : AbstractEntityProcessor(input) { public override EntityType Type => EntityType.Trait; public override Trait Object { get; } = new(); - protected override void Add() => Context.Traits.Add(Object); + public override void Add() => Context.Traits.Add(Object); public override void Process() { diff --git a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/Compendium/CompendiumFolder.cs b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/Compendium/CompendiumFolder.cs index 4cf26bc..07557b5 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/Compendium/CompendiumFolder.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/Compendium/CompendiumFolder.cs @@ -46,7 +46,7 @@ internal class CompendiumFolder if (json.TryGetValue("_id", out JToken? id)) { // Вложенная папка через _folders.json - Id = id!.ToString(); + Id = id.ToString(); Name = json["name"]!.ToString(); Path = null; IsContained = true; diff --git a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/CompendiumLoader.cs b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/CompendiumLoader.cs index ae02121..7010191 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/CompendiumLoader.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/CompendiumLoader.cs @@ -22,12 +22,12 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer) /// /// Счётчик папок /// - private int _folderCount = 0; + private int _folderCount; /// /// Общее количество элементов в компендиуме /// - public int ItemCount { get; protected set; } = 0; + public int ItemCount { get; protected set; } /// /// Обработка папок компендиума в поле `packFolders`. @@ -48,7 +48,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer) if (obj.TryGetValue("packs", out JToken? packs)) { - foreach (JToken jToken in packs!) + foreach (JToken jToken in packs) { string packId = jToken.ToString(); CompendiumFolder? child = Collection.GetById(packId); @@ -62,7 +62,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer) if (obj.TryGetValue("folders", out JToken? innerFolders)) { - ProcessPackFolders((JArray)innerFolders!, folder); + ProcessPackFolders((JArray)innerFolders, folder); } } @@ -90,7 +90,7 @@ internal class CompendiumLoader(Pf2EFoundryImporter importer) } // Поиск _folders.json - foreach (CompendiumFolder? parent in Collection.ToList()) + foreach (CompendiumFolder parent in Collection.ToList()) { if (parent.Path is null) { diff --git a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/AbstractProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/AbstractProcessor.cs index 2b74479..8ff9468 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/AbstractProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/AbstractProcessor.cs @@ -1,5 +1,6 @@ using System.Security.Cryptography; using System.Text; +using Microsoft.EntityFrameworkCore; using Newtonsoft.Json.Linq; using Pf2eModel; using Pf2eModel.Model; @@ -39,10 +40,7 @@ internal abstract class AbstractProcessor(ProcessorInput input) Entity.IsChecked = false; Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]); - Entity.Rarity = Input.EnumOptional( - "system.traits.rarity", - EntityRarity.Common - ); + Entity.Rarity = Input.EnumOptional("system.traits.rarity", EntityRarity.Common); Object.Id = Entity.Id; @@ -140,16 +138,19 @@ internal abstract class AbstractProcessor(ProcessorInput input) protected IEnumerable GetTraits(IEnumerable names) { List result = Context - .Traits.Where(t => names.Contains(t.Entity.ToString())) + .Traits.Include(t => t.Entity) + .ThenInclude(e => e.Name) + // ReSharper disable once EntityFramework.UnsupportedServerSideFunctionCall + .Where(t => Pf2eDbContext.LocaleSearchArray(t.Entity.Name, names)) .ToList(); + /* foreach (string name in names) { if (result.Any(t => t.Entity.Name.Contains(name))) { continue; } - /* Trait trait = new() { Name = name, @@ -159,8 +160,8 @@ internal abstract class AbstractProcessor(ProcessorInput input) }; Context.Traits.Add(trait); result.Add(trait); - */ } + */ return result; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/Processors/ConditionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/Processors/ConditionProcessor.cs index ab74cd4..f549256 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/Processors/ConditionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/Processors/ConditionProcessor.cs @@ -24,7 +24,7 @@ internal class ConditionProcessor(ProcessorInput input) : AbstractProcessor(inpu }; Object.Extra = extra.ToString(Newtonsoft.Json.Formatting.None); - Object.Group = Input.EnumOptional("system.group", ConditionGroup.Unknown); + Object.Group = Input.EnumOptional("system.group", ConditionGroup.Unknown); Context.Conditions.Add(Object); } diff --git a/Pf2eDataScrapper/Importers/Repositories/GitRepository.cs b/Pf2eDataScrapper/Importers/Repositories/GitRepository.cs index 39ae89b..c9c609d 100644 --- a/Pf2eDataScrapper/Importers/Repositories/GitRepository.cs +++ b/Pf2eDataScrapper/Importers/Repositories/GitRepository.cs @@ -2,10 +2,6 @@ namespace Pf2eDataScrapper.Importers.Repositories; -/// -/// Загрузчик данных из git-репозиториев -/// -/// internal class GitRepository(AbstractImporter importer, string url) : AbstractImporterRepository(importer) { diff --git a/Pf2eDataScrapper/SchemaBuilder/SchemaNode.cs b/Pf2eDataScrapper/SchemaBuilder/SchemaNode.cs index 25856a9..454f681 100644 --- a/Pf2eDataScrapper/SchemaBuilder/SchemaNode.cs +++ b/Pf2eDataScrapper/SchemaBuilder/SchemaNode.cs @@ -11,7 +11,7 @@ internal class SchemaNode /// /// Отображаемое имя элемента. /// - public string Name = ""; + public readonly string Name = ""; /// /// Путь к элементу. @@ -21,12 +21,12 @@ internal class SchemaNode /// /// Родительский элемент. /// - public SchemaNode? Parent = null; + public readonly SchemaNode? Parent; /// /// Указание, является ли элемент корневым. /// - public bool Root = false; + public readonly bool Root; /// /// Корневой элемент схемы данных. @@ -36,12 +36,12 @@ internal class SchemaNode /// /// Типы данных, использующиеся в элементе. /// - public List Types = []; + public readonly List Types = []; /// /// Указание, может ли этот элемент отсутсвовать в структуре данных. /// - public bool IsNullable = false; + public bool IsNullable; /// /// Объект для определения типа поля в схеме данных. @@ -51,7 +51,7 @@ internal class SchemaNode /// /// Количество значений, добавленных в тип. /// - public int Values { get; protected set; } = 0; + public int Values { get; protected set; } /// /// Указание, является ли тип элемента простым. @@ -93,13 +93,10 @@ internal class SchemaNode Values++; - foreach (AbstractSchemaType type in Types) + foreach (AbstractSchemaType type in Types.Where(type => type.IsCompatibleValue(value))) { - if (type.IsCompatibleValue(value)) - { - type.AddValue(value); - return; - } + type.AddValue(value); + return; } AbstractSchemaType newType = Resolver.Resolve(this, value); @@ -161,7 +158,7 @@ internal class SchemaNode foreach (AbstractSchemaType type in Types) { - types.Add(Indent(type?.ToString() ?? "")); + types.Add(Indent(type.ToString())); } return FormatYaml(result, new() { ["types"] = "- " + string.Join("\r\n- ", types) }); @@ -223,19 +220,9 @@ internal class SchemaNode { Dictionary result = []; - foreach (Dictionary item in input) + foreach (KeyValuePair kvp in input.SelectMany(item => item)) { - foreach (KeyValuePair kvp in item) - { - if (result.ContainsKey(kvp.Key)) - { - result[kvp.Key] = kvp.Value; - } - else - { - result.Add(kvp.Key, kvp.Value); - } - } + result[kvp.Key] = kvp.Value; } return result; diff --git a/Pf2eModel/Migrations/20250425164659_1.Designer.cs b/Pf2eModel/Migrations/20250426125550_1.Designer.cs similarity index 99% rename from Pf2eModel/Migrations/20250425164659_1.Designer.cs rename to Pf2eModel/Migrations/20250426125550_1.Designer.cs index 503611e..7e50928 100644 --- a/Pf2eModel/Migrations/20250425164659_1.Designer.cs +++ b/Pf2eModel/Migrations/20250426125550_1.Designer.cs @@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Migrations { [DbContext(typeof(Pf2eDbContext))] - [Migration("20250425164659_1")] + [Migration("20250426125550_1")] partial class _1 { /// @@ -182,11 +182,18 @@ namespace Pf2eModel.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Name") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("name") + .HasColumnOrder(2); + b.Property("Slug") .IsRequired() - .HasColumnType("text") + .HasMaxLength(255) + .HasColumnType("character varying(255)") .HasColumnName("slug") - .HasColumnOrder(2); + .HasColumnOrder(3); b.HasKey("Id") .HasName("pk_keywords"); @@ -1457,7 +1464,8 @@ namespace Pf2eModel.Migrations b.Property("Extra") .IsRequired() .HasColumnType("text") - .HasColumnName("extra"); + .HasColumnName("extra") + .HasColumnOrder(4); b.Property("GroupId") .HasColumnType("integer") diff --git a/Pf2eModel/Migrations/20250425164659_1.cs b/Pf2eModel/Migrations/20250426125550_1.cs similarity index 99% rename from Pf2eModel/Migrations/20250425164659_1.cs rename to Pf2eModel/Migrations/20250426125550_1.cs index c58fe03..750c5af 100644 --- a/Pf2eModel/Migrations/20250425164659_1.cs +++ b/Pf2eModel/Migrations/20250426125550_1.cs @@ -91,7 +91,8 @@ namespace Pf2eModel.Migrations { id = table.Column(type: "integer", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - slug = table.Column(type: "text", nullable: false) + name = table.Column(type: "jsonb", nullable: false), + slug = table.Column(type: "character varying(255)", maxLength: 255, nullable: false) }, constraints: table => { diff --git a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs index 230f89e..15fd196 100644 --- a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs +++ b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs @@ -179,11 +179,18 @@ namespace Pf2eModel.Migrations NpgsqlPropertyBuilderExtensions.UseIdentityByDefaultColumn(b.Property("Id")); + b.Property("Name") + .IsRequired() + .HasColumnType("jsonb") + .HasColumnName("name") + .HasColumnOrder(2); + b.Property("Slug") .IsRequired() - .HasColumnType("text") + .HasMaxLength(255) + .HasColumnType("character varying(255)") .HasColumnName("slug") - .HasColumnOrder(2); + .HasColumnOrder(3); b.HasKey("Id") .HasName("pk_keywords"); @@ -1454,7 +1461,8 @@ namespace Pf2eModel.Migrations b.Property("Extra") .IsRequired() .HasColumnType("text") - .HasColumnName("extra"); + .HasColumnName("extra") + .HasColumnOrder(4); b.Property("GroupId") .HasColumnType("integer") diff --git a/Pf2eModel/Model/Base/Entity.cs b/Pf2eModel/Model/Base/Entity.cs index 693b96e..a9d981e 100644 --- a/Pf2eModel/Model/Base/Entity.cs +++ b/Pf2eModel/Model/Base/Entity.cs @@ -12,13 +12,13 @@ namespace Pf2eModel.Model.Base; public class Entity { - [System.ComponentModel.DataAnnotations.Key] + [Key] [Column(Order = 1)] public int Id { get; set; } [Column(Order = 2)] [Comment("Отображаемое имя")] - public LocaleString Name { get; set; } = new(); + public LocaleString Name { get; set; } = []; [Column(Order = 3)] [Comment("Тип объекта")] @@ -36,6 +36,7 @@ public class Entity [Column(Order = 6)] [Comment("Текстовое описание объекта")] + // ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength public string Description { get; set; } = null!; [Column(Order = 7)] diff --git a/Pf2eModel/Model/Base/Keywords/Keyword.cs b/Pf2eModel/Model/Base/Keywords/Keyword.cs index d6a909d..0edd73e 100644 --- a/Pf2eModel/Model/Base/Keywords/Keyword.cs +++ b/Pf2eModel/Model/Base/Keywords/Keyword.cs @@ -1,9 +1,10 @@ using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; +using Pf2eModel.Model.Common.I18n; namespace Pf2eModel.Model.Base.Keywords; -public partial class Keyword +public class Keyword { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -11,10 +12,14 @@ public partial class Keyword public int Id { get; set; } [Column(Order = 2)] + public LocaleString Name = []; + + [Column(Order = 3)] + [MaxLength(Pf2eDbContext.NameLength)] public string Slug { get; set; } = null!; [Newtonsoft.Json.JsonIgnore] - public virtual ICollection Entities { get; set; } = []; + public ICollection Entities { get; set; } = []; - public virtual ICollection Aliases { get; set; } = []; + public ICollection Aliases { get; set; } = []; } diff --git a/Pf2eModel/Model/Base/Keywords/KeywordAlias.cs b/Pf2eModel/Model/Base/Keywords/KeywordAlias.cs index a7815d7..cd2574c 100644 --- a/Pf2eModel/Model/Base/Keywords/KeywordAlias.cs +++ b/Pf2eModel/Model/Base/Keywords/KeywordAlias.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Base.Keywords; -public partial class KeywordAlias +public class KeywordAlias { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -18,5 +18,5 @@ public partial class KeywordAlias public string Value { get; set; } = null!; [Newtonsoft.Json.JsonIgnore] - public virtual Keyword Keyword { get; set; } = null!; + public Keyword Keyword { get; set; } = null!; } diff --git a/Pf2eModel/Model/Base/Source/EntitySource.cs b/Pf2eModel/Model/Base/Source/EntitySource.cs index a0cb13f..6a6143f 100644 --- a/Pf2eModel/Model/Base/Source/EntitySource.cs +++ b/Pf2eModel/Model/Base/Source/EntitySource.cs @@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore; namespace Pf2eModel.Model.Base.Source; [PrimaryKey(nameof(EntityId), nameof(SourceId))] -public partial class EntitySource +public class EntitySource { [Column(Order = 1)] public int EntityId { get; set; } @@ -23,7 +23,7 @@ public partial class EntitySource public bool IsRemaster { get; set; } [Newtonsoft.Json.JsonIgnore] - public virtual Entity Entity { get; set; } = null!; + public Entity Entity { get; set; } = null!; - public virtual Source Source { get; set; } = null!; + public Source Source { get; set; } = null!; } diff --git a/Pf2eModel/Model/Base/Source/Source.cs b/Pf2eModel/Model/Base/Source/Source.cs index 4483873..e7035d1 100644 --- a/Pf2eModel/Model/Base/Source/Source.cs +++ b/Pf2eModel/Model/Base/Source/Source.cs @@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Base.Source; -public partial class Source +public class Source { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Base/Targets/EntityExternal.cs b/Pf2eModel/Model/Base/Targets/EntityExternal.cs index 75abe92..2081ef2 100644 --- a/Pf2eModel/Model/Base/Targets/EntityExternal.cs +++ b/Pf2eModel/Model/Base/Targets/EntityExternal.cs @@ -5,7 +5,7 @@ using Microsoft.EntityFrameworkCore; namespace Pf2eModel.Model.Base.Targets; [PrimaryKey(nameof(EntityId), nameof(TargetId))] -public partial class EntityExternal +public class EntityExternal { [Column(Order = 0)] public int EntityId { get; set; } diff --git a/Pf2eModel/Model/Base/Targets/ExternalTarget.cs b/Pf2eModel/Model/Base/Targets/ExternalTarget.cs index ca9c55d..9fb6db2 100644 --- a/Pf2eModel/Model/Base/Targets/ExternalTarget.cs +++ b/Pf2eModel/Model/Base/Targets/ExternalTarget.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Base.Targets; -public partial class ExternalTarget +public class ExternalTarget { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -24,5 +24,5 @@ public partial class ExternalTarget [Column(Order = 4)] [Newtonsoft.Json.JsonIgnore] - public virtual ICollection EntityExternals { get; set; } = []; + public ICollection EntityExternals { get; set; } = []; } diff --git a/Pf2eModel/Model/Common/Frequency.cs b/Pf2eModel/Model/Common/Frequency.cs index 876f7e9..82ecb97 100644 --- a/Pf2eModel/Model/Common/Frequency.cs +++ b/Pf2eModel/Model/Common/Frequency.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Common; -public partial class Frequency +public class Frequency { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Common/I18n/LocaleExtension.cs b/Pf2eModel/Model/Common/I18n/LocaleExtension.cs index b2c5453..ee4d8b5 100644 --- a/Pf2eModel/Model/Common/I18n/LocaleExtension.cs +++ b/Pf2eModel/Model/Common/I18n/LocaleExtension.cs @@ -18,17 +18,19 @@ public static class LocaleExtension public static LocaleType GetTypeByString(this string localeType) { - foreach (System.Reflection.FieldInfo field in typeof(LocaleType).GetFields()) + foreach (FieldInfo field in typeof(LocaleType).GetFields()) { if ( Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) - is DescriptionAttribute descriptionAttribute + is not DescriptionAttribute descriptionAttribute ) { - if (descriptionAttribute.Description == localeType) - { - return (LocaleType)field.GetValue(null)!; - } + continue; + } + + if (descriptionAttribute.Description == localeType) + { + return (LocaleType)field.GetValue(null)!; } } diff --git a/Pf2eModel/Model/Common/Training.cs b/Pf2eModel/Model/Common/Training.cs index a6118fd..6bb92da 100644 --- a/Pf2eModel/Model/Common/Training.cs +++ b/Pf2eModel/Model/Common/Training.cs @@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Common; -public partial class Training +public class Training { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Structure/Action.cs b/Pf2eModel/Model/Structure/Action.cs index 29e0888..ff0bbbb 100644 --- a/Pf2eModel/Model/Structure/Action.cs +++ b/Pf2eModel/Model/Structure/Action.cs @@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Action)] -public partial class Action : IEntity +public class Action : IEntity { [System.ComponentModel.DataAnnotations.Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -28,6 +28,7 @@ public partial class Action : IEntity public bool IsPassive { get; set; } = false; [Column(Order = 6)] + // ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength public string? Extra { get; set; } public Entity Entity { get; set; } = null!; diff --git a/Pf2eModel/Model/Structure/Ancestry.cs b/Pf2eModel/Model/Structure/Ancestry.cs index 2920c53..f26f398 100644 --- a/Pf2eModel/Model/Structure/Ancestry.cs +++ b/Pf2eModel/Model/Structure/Ancestry.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Structure.AncestryRelation; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Ancestry)] -public partial class Ancestry : IEntity +public class Ancestry : IEntity { [System.ComponentModel.DataAnnotations.Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -27,11 +27,11 @@ public partial class Ancestry : IEntity [Column(Order = 5)] public AncestryVision Vision { get; set; } = AncestryVision.Normal; - public virtual ICollection AncestryBoosts { get; set; } = []; + public ICollection AncestryBoosts { get; set; } = []; - public virtual ICollection AncestryItems { get; set; } = []; + public ICollection AncestryItems { get; set; } = []; - public virtual ICollection AncestryLanguages { get; set; } = []; + public ICollection AncestryLanguages { get; set; } = []; - public virtual ICollection Heritages { get; set; } = []; + public ICollection Heritages { get; set; } = []; } diff --git a/Pf2eModel/Model/Structure/AncestryRelation/AncestryBoost.cs b/Pf2eModel/Model/Structure/AncestryRelation/AncestryBoost.cs index e053305..63f2aa4 100644 --- a/Pf2eModel/Model/Structure/AncestryRelation/AncestryBoost.cs +++ b/Pf2eModel/Model/Structure/AncestryRelation/AncestryBoost.cs @@ -4,7 +4,7 @@ using Newtonsoft.Json; namespace Pf2eModel.Model.Structure.AncestryRelation; -public partial class AncestryBoost +public class AncestryBoost { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -21,5 +21,5 @@ public partial class AncestryBoost public Enum.AttributeValues Value { get; set; } [JsonIgnore] - public virtual Ancestry Ancestry { get; set; } = null!; + public Ancestry Ancestry { get; set; } = null!; } diff --git a/Pf2eModel/Model/Structure/AncestryRelation/AncestryItem.cs b/Pf2eModel/Model/Structure/AncestryRelation/AncestryItem.cs index 0272b5c..2136e3a 100644 --- a/Pf2eModel/Model/Structure/AncestryRelation/AncestryItem.cs +++ b/Pf2eModel/Model/Structure/AncestryRelation/AncestryItem.cs @@ -5,7 +5,7 @@ using Pf2eModel.Model.Base; namespace Pf2eModel.Model.Structure.AncestryRelation; [PrimaryKey(nameof(AncestryId), nameof(ItemId))] -public partial class AncestryItem +public class AncestryItem { [Column(Order = 1)] public int AncestryId { get; set; } @@ -16,7 +16,7 @@ public partial class AncestryItem [Column(Order = 3)] public int Level { get; set; } - public virtual Ancestry Ancestry { get; set; } = null!; + public Ancestry Ancestry { get; set; } = null!; - public virtual Entity Item { get; set; } = null!; + public Entity Item { get; set; } = null!; } diff --git a/Pf2eModel/Model/Structure/AncestryRelation/AncestryLanguage.cs b/Pf2eModel/Model/Structure/AncestryRelation/AncestryLanguage.cs index 17c6c9c..92f00f3 100644 --- a/Pf2eModel/Model/Structure/AncestryRelation/AncestryLanguage.cs +++ b/Pf2eModel/Model/Structure/AncestryRelation/AncestryLanguage.cs @@ -4,7 +4,7 @@ using Microsoft.EntityFrameworkCore; namespace Pf2eModel.Model.Structure.AncestryRelation; [PrimaryKey(nameof(AncestryId), nameof(LanguageId))] -public partial class AncestryLanguage +public class AncestryLanguage { [Column(Order = 1)] public int AncestryId { get; set; } @@ -15,7 +15,7 @@ public partial class AncestryLanguage [Column(Order = 3)] public bool IsAdditional { get; set; } - public virtual Ancestry Ancestry { get; set; } = null!; + public Ancestry Ancestry { get; set; } = null!; - public virtual Language Language { get; set; } = null!; + public Language Language { get; set; } = null!; } diff --git a/Pf2eModel/Model/Structure/Archetype.cs b/Pf2eModel/Model/Structure/Archetype.cs index 5fa510d..bc65a80 100644 --- a/Pf2eModel/Model/Structure/Archetype.cs +++ b/Pf2eModel/Model/Structure/Archetype.cs @@ -21,9 +21,10 @@ public class Archetype : IEntity public int? DedicationFeatId { get; set; } = null; [Column(Order = 4)] + // ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength public string? Extra { get; set; } public Feat DedicationFeat { get; set; } = null!; - public virtual ICollection Feats { get; set; } = []; + public ICollection Feats { get; set; } = []; } diff --git a/Pf2eModel/Model/Structure/Background.cs b/Pf2eModel/Model/Structure/Background.cs index ff88912..5ed8b3b 100644 --- a/Pf2eModel/Model/Structure/Background.cs +++ b/Pf2eModel/Model/Structure/Background.cs @@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.BackgroundRelation; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Background)] -public partial class Background : IEntity +public class Background : IEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -16,9 +16,9 @@ public partial class Background : IEntity public Entity Entity { get; set; } = null!; - public virtual ICollection BackgroundBoosts { get; set; } = []; + public ICollection BackgroundBoosts { get; set; } = []; - public virtual ICollection BackgroundItems { get; set; } = []; + public ICollection BackgroundItems { get; set; } = []; - public virtual ICollection BackgroundSkills { get; set; } = []; + public ICollection BackgroundSkills { get; set; } = []; } diff --git a/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundBoost.cs b/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundBoost.cs index 6f5c90b..d5b8d2b 100644 --- a/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundBoost.cs +++ b/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundBoost.cs @@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.BackgroundRelation; -public partial class BackgroundBoost +public class BackgroundBoost { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -19,5 +19,5 @@ public partial class BackgroundBoost public AttributeValues Attribute { get; set; } [JsonIgnore] - public virtual Background Background { get; set; } = null!; + public Background Background { get; set; } = null!; } diff --git a/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundItem.cs b/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundItem.cs index e4ec5db..5642f35 100644 --- a/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundItem.cs +++ b/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundItem.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Base; namespace Pf2eModel.Model.Structure.BackgroundRelation; [PrimaryKey(nameof(BackgroundId), nameof(EntityId))] -public partial class BackgroundItem +public class BackgroundItem { [Column(Order = 1)] public int BackgroundId { get; set; } @@ -18,7 +18,7 @@ public partial class BackgroundItem public int? Level { get; set; } [JsonIgnore] - public virtual Background Background { get; set; } = null!; + public Background Background { get; set; } = null!; - public virtual Entity Entity { get; set; } = null!; + public Entity Entity { get; set; } = null!; } diff --git a/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundTraining.cs b/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundTraining.cs index 1983676..d4d6e24 100644 --- a/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundTraining.cs +++ b/Pf2eModel/Model/Structure/BackgroundRelation/BackgroundTraining.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.BackgroundRelation; -public partial class BackgroundTraining +public class BackgroundTraining { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -23,7 +23,7 @@ public partial class BackgroundTraining public TrainingStage Level { get; set; } [JsonIgnore] - public virtual Background Background { get; set; } = null!; + public Background Background { get; set; } = null!; - public virtual Training Training { get; set; } = null!; + public Training Training { get; set; } = null!; } diff --git a/Pf2eModel/Model/Structure/ClassRelation/ClassKeyAbility.cs b/Pf2eModel/Model/Structure/ClassRelation/ClassKeyAbility.cs index a2920dc..d64bc49 100644 --- a/Pf2eModel/Model/Structure/ClassRelation/ClassKeyAbility.cs +++ b/Pf2eModel/Model/Structure/ClassRelation/ClassKeyAbility.cs @@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.ClassRelation; -public partial class ClassKeyAbility +public class ClassKeyAbility { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -19,5 +19,5 @@ public partial class ClassKeyAbility public AttributeValues Value { get; set; } [JsonIgnore] - public virtual Class Class { get; set; } = null!; + public Class Class { get; set; } = null!; } diff --git a/Pf2eModel/Model/Structure/ClassRelation/ClassProgression.cs b/Pf2eModel/Model/Structure/ClassRelation/ClassProgression.cs index 6c8f26e..e8b1467 100644 --- a/Pf2eModel/Model/Structure/ClassRelation/ClassProgression.cs +++ b/Pf2eModel/Model/Structure/ClassRelation/ClassProgression.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.ClassRelation; -public partial class ClassProgression +public class ClassProgression { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -26,7 +26,7 @@ public partial class ClassProgression public int? EntityId { get; set; } [JsonIgnore] - public virtual Class Class { get; set; } = null!; + public Class Class { get; set; } = null!; - public virtual Entity? Entity { get; set; } + public Entity? Entity { get; set; } } diff --git a/Pf2eModel/Model/Structure/ClassRelation/ClassTraining.cs b/Pf2eModel/Model/Structure/ClassRelation/ClassTraining.cs index 83f23c4..0cbde59 100644 --- a/Pf2eModel/Model/Structure/ClassRelation/ClassTraining.cs +++ b/Pf2eModel/Model/Structure/ClassRelation/ClassTraining.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.ClassRelation; -public partial class ClassTraining +public class ClassTraining { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] @@ -27,7 +27,7 @@ public partial class ClassTraining public TrainingStage Stage { get; set; } [JsonIgnore] - public virtual Class Class { get; set; } = null!; + public Class Class { get; set; } = null!; - public virtual Training? Training { get; set; } + public Training? Training { get; set; } } diff --git a/Pf2eModel/Model/Structure/Condition.cs b/Pf2eModel/Model/Structure/Condition.cs index 934c4d0..9fbe564 100644 --- a/Pf2eModel/Model/Structure/Condition.cs +++ b/Pf2eModel/Model/Structure/Condition.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Condition)] -public partial class Condition : IEntity +public class Condition : IEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -19,5 +19,6 @@ public partial class Condition : IEntity public ConditionGroup? Group { get; set; } [Column(Order = 3)] + // ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength public string? Extra { get; set; } } diff --git a/Pf2eModel/Model/Structure/Deity.cs b/Pf2eModel/Model/Structure/Deity.cs index f87f653..0e91fdc 100644 --- a/Pf2eModel/Model/Structure/Deity.cs +++ b/Pf2eModel/Model/Structure/Deity.cs @@ -8,7 +8,7 @@ using Pf2eModel.Model.Structure.DeityRelation; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Deity)] -public partial class Deity : IEntity +public class Deity : IEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -29,14 +29,13 @@ public partial class Deity : IEntity [Column(Order = 5)] public bool SanctificationMust { get; set; } - public virtual ICollection DeityAttributes { get; set; } = - new List(); + public ICollection DeityAttributes { get; set; } = new List(); - public virtual ICollection DeityDomains { get; set; } = new List(); + public ICollection DeityDomains { get; set; } = new List(); - public virtual ICollection DeitySpells { get; set; } = new List(); + public ICollection DeitySpells { get; set; } = new List(); - public virtual ICollection DeityWeapons { get; set; } = new List(); + public ICollection DeityWeapons { get; set; } = new List(); - public virtual ICollection Skills { get; set; } = new List(); + public ICollection Skills { get; set; } = new List(); } diff --git a/Pf2eModel/Model/Structure/DeityRelation/DeityAttribute.cs b/Pf2eModel/Model/Structure/DeityRelation/DeityAttribute.cs index f8d4420..9b186b6 100644 --- a/Pf2eModel/Model/Structure/DeityRelation/DeityAttribute.cs +++ b/Pf2eModel/Model/Structure/DeityRelation/DeityAttribute.cs @@ -5,7 +5,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.DeityRelation; [PrimaryKey(nameof(DeityId), nameof(Attribute))] -public partial class DeityAttribute +public class DeityAttribute { [Column(Order = 1)] public int DeityId { get; set; } @@ -13,5 +13,5 @@ public partial class DeityAttribute [Column(Order = 2)] public AttributeValues Attribute { get; set; } - public virtual Deity Deity { get; set; } = null!; + public Deity Deity { get; set; } = null!; } diff --git a/Pf2eModel/Model/Structure/DeityRelation/DeityDomain.cs b/Pf2eModel/Model/Structure/DeityRelation/DeityDomain.cs index e112c2d..7e0e1dc 100644 --- a/Pf2eModel/Model/Structure/DeityRelation/DeityDomain.cs +++ b/Pf2eModel/Model/Structure/DeityRelation/DeityDomain.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure.DeityRelation; -public partial class DeityDomain +public class DeityDomain { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Structure/DeityRelation/DeitySpell.cs b/Pf2eModel/Model/Structure/DeityRelation/DeitySpell.cs index 97be457..0f448a8 100644 --- a/Pf2eModel/Model/Structure/DeityRelation/DeitySpell.cs +++ b/Pf2eModel/Model/Structure/DeityRelation/DeitySpell.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure.DeityRelation; -public partial class DeitySpell +public class DeitySpell { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Structure/DeityRelation/DeityWeapon.cs b/Pf2eModel/Model/Structure/DeityRelation/DeityWeapon.cs index daf7241..73a2bca 100644 --- a/Pf2eModel/Model/Structure/DeityRelation/DeityWeapon.cs +++ b/Pf2eModel/Model/Structure/DeityRelation/DeityWeapon.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure.DeityRelation; -public partial class DeityWeapon +public class DeityWeapon { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Structure/Feat.cs b/Pf2eModel/Model/Structure/Feat.cs index 3e952ce..3762df0 100644 --- a/Pf2eModel/Model/Structure/Feat.cs +++ b/Pf2eModel/Model/Structure/Feat.cs @@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.FeatRelation; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Feat)] -public partial class Feat : IEntity +public class Feat : IEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] @@ -38,9 +38,10 @@ public partial class Feat : IEntity public bool IsPassive { get; set; } = false; [Column(Order = 10)] + // ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength public string? Extra { get; set; } - public virtual ICollection Archetypes { get; set; } = []; + public ICollection Archetypes { get; set; } = []; - public virtual ICollection FeatPrerequisites { get; set; } = []; + public ICollection FeatPrerequisites { get; set; } = []; } diff --git a/Pf2eModel/Model/Structure/FeatRelation/FeatPrerequisite.cs b/Pf2eModel/Model/Structure/FeatRelation/FeatPrerequisite.cs index 41c820e..bff9311 100644 --- a/Pf2eModel/Model/Structure/FeatRelation/FeatPrerequisite.cs +++ b/Pf2eModel/Model/Structure/FeatRelation/FeatPrerequisite.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure.FeatRelation; -public partial class FeatPrerequisite +public class FeatPrerequisite { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Structure/Heritage.cs b/Pf2eModel/Model/Structure/Heritage.cs index b7e367d..c9d04c9 100644 --- a/Pf2eModel/Model/Structure/Heritage.cs +++ b/Pf2eModel/Model/Structure/Heritage.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Heritage)] -public partial class Heritage : IEntity +public class Heritage : IEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] diff --git a/Pf2eModel/Model/Structure/Language.cs b/Pf2eModel/Model/Structure/Language.cs index 5f652b3..c9072d7 100644 --- a/Pf2eModel/Model/Structure/Language.cs +++ b/Pf2eModel/Model/Structure/Language.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Language)] -public partial class Language : IEntity +public class Language : IEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Structure/Spell.cs b/Pf2eModel/Model/Structure/Spell.cs index 37a8dea..b0c6637 100644 --- a/Pf2eModel/Model/Structure/Spell.cs +++ b/Pf2eModel/Model/Structure/Spell.cs @@ -7,7 +7,7 @@ using Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Spell)] -public partial class Spell : IEntity +public class Spell : IEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] diff --git a/Pf2eModel/Model/Structure/SpellRelation/SpellArea.cs b/Pf2eModel/Model/Structure/SpellRelation/SpellArea.cs index 098b22b..ed9dae6 100644 --- a/Pf2eModel/Model/Structure/SpellRelation/SpellArea.cs +++ b/Pf2eModel/Model/Structure/SpellRelation/SpellArea.cs @@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.SpellRelation; -public partial class SpellArea +public class SpellArea { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] diff --git a/Pf2eModel/Model/Structure/SpellRelation/SpellDamage.cs b/Pf2eModel/Model/Structure/SpellRelation/SpellDamage.cs index 177fa83..139883e 100644 --- a/Pf2eModel/Model/Structure/SpellRelation/SpellDamage.cs +++ b/Pf2eModel/Model/Structure/SpellRelation/SpellDamage.cs @@ -4,7 +4,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure.SpellRelation; -public partial class SpellDamage +public class SpellDamage { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Structure/SpellRelation/SpellDefense.cs b/Pf2eModel/Model/Structure/SpellRelation/SpellDefense.cs index b4313ba..c841a43 100644 --- a/Pf2eModel/Model/Structure/SpellRelation/SpellDefense.cs +++ b/Pf2eModel/Model/Structure/SpellRelation/SpellDefense.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure.SpellRelation; -public partial class SpellDefense +public class SpellDefense { [Key] [DatabaseGenerated(DatabaseGeneratedOption.Identity)] diff --git a/Pf2eModel/Model/Structure/SpellRelation/SpellRitual.cs b/Pf2eModel/Model/Structure/SpellRelation/SpellRitual.cs index fd96c08..133ff9c 100644 --- a/Pf2eModel/Model/Structure/SpellRelation/SpellRitual.cs +++ b/Pf2eModel/Model/Structure/SpellRelation/SpellRitual.cs @@ -3,7 +3,7 @@ using System.ComponentModel.DataAnnotations.Schema; namespace Pf2eModel.Model.Structure.SpellRelation; -public partial class SpellRitual +public class SpellRitual { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] diff --git a/Pf2eModel/Model/Structure/Subclass.cs b/Pf2eModel/Model/Structure/Subclass.cs index 9deb561..6e418ca 100644 --- a/Pf2eModel/Model/Structure/Subclass.cs +++ b/Pf2eModel/Model/Structure/Subclass.cs @@ -21,7 +21,9 @@ public class Subclass : IEntity public SubclassGroup Group { get; set; } = null!; [Column(Order = 3)] - public LocaleString Additional { get; set; } = new(); + public LocaleString Additional { get; set; } = []; + [Column(Order = 4)] + // ReSharper disable once EntityFramework.ModelValidation.UnlimitedStringLength public string Extra { get; set; } = "{}"; } diff --git a/Pf2eModel/Model/Structure/SubclassGroup.cs b/Pf2eModel/Model/Structure/SubclassGroup.cs index 5411cb9..017d586 100644 --- a/Pf2eModel/Model/Structure/SubclassGroup.cs +++ b/Pf2eModel/Model/Structure/SubclassGroup.cs @@ -1,13 +1,7 @@ -using System; -using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations; using System.ComponentModel.DataAnnotations.Schema; -using System.Linq; -using System.Text; -using System.Threading.Tasks; using Pf2eModel.Model.Base; using Pf2eModel.Model.Enum; -using Pf2eModel.Model.Structure.ClassRelation; namespace Pf2eModel.Model.Structure; @@ -25,5 +19,5 @@ public class SubclassGroup : IEntity public Class? Class { get; set; } = null!; - public virtual ICollection Subclasses { get; set; } = []; + public ICollection Subclasses { get; set; } = []; } diff --git a/Pf2eModel/Model/Structure/Trait.cs b/Pf2eModel/Model/Structure/Trait.cs index 830f664..ac6db44 100644 --- a/Pf2eModel/Model/Structure/Trait.cs +++ b/Pf2eModel/Model/Structure/Trait.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Model.Structure; [EntityOfType(EntityType.Trait)] -public partial class Trait : IEntity +public class Trait : IEntity { [Key] [DatabaseGenerated(DatabaseGeneratedOption.None)] diff --git a/Pf2eModel/Pf2eDbContextModel.cs b/Pf2eModel/Pf2eDbContextModel.cs index b850e1a..bffb505 100644 --- a/Pf2eModel/Pf2eDbContextModel.cs +++ b/Pf2eModel/Pf2eDbContextModel.cs @@ -1,4 +1,5 @@ -using System.Linq.Expressions; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; using System.Reflection; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; @@ -21,6 +22,7 @@ using Pf2eModel.Model.Structure.SpellRelation; namespace Pf2eModel; +[SuppressMessage("ReSharper", "UnusedParameter.Local")] public partial class Pf2eDbContext { public const int NameLength = 255; @@ -39,11 +41,7 @@ public partial class Pf2eDbContext private void BindEntity(EntityTypeBuilder entity) where T : class, IEntity { - entity - .HasOne(entity => entity.Entity) - .WithOne() - .HasForeignKey(entity => entity.Id) - .IsRequired(); + entity.HasOne(e => e.Entity).WithOne().HasForeignKey(e => e.Id).IsRequired(); } private void BindLocaleString( @@ -144,6 +142,8 @@ public partial class Pf2eDbContext // Keywords modelBuilder.Entity(entity => { + BindLocaleString(entity, k => k.Name); + entity .HasMany(e => e.Aliases) .WithOne(e => e.Keyword) @@ -483,9 +483,11 @@ public partial class Pf2eDbContext ); } + // ReSharper disable once InconsistentNaming public static bool LocaleSearch(LocaleString data, string search_value) => throw new InvalidOperationException("Only for LINQ translation"); + // ReSharper disable once InconsistentNaming public static bool LocaleSearchArray(LocaleString data, IEnumerable search_values) => throw new InvalidOperationException("Only for LINQ translation"); } diff --git a/Pf2eViewer/Converters/EntityConverter.cs b/Pf2eViewer/Converters/EntityConverter.cs index 9d12848..4423ccb 100644 --- a/Pf2eViewer/Converters/EntityConverter.cs +++ b/Pf2eViewer/Converters/EntityConverter.cs @@ -1,7 +1,5 @@ -using System.Collections.Generic; -using System.ComponentModel; +using System.ComponentModel; using Newtonsoft.Json; -using Newtonsoft.Json.Linq; using Pf2eModel; using Pf2eModel.Model; using Pf2eModel.Model.Base; @@ -39,64 +37,86 @@ internal class EntityConverter : JsonConverter public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { - Dictionary dictionary = DictionarySerialization(value!); + if (value == null) + { + serializer.Serialize(writer, null); + return; + } + + Dictionary dictionary = DictionarySerialization(value); if (value.GetType() != typeof(Entity)) { dictionary.Remove("Entity"); + + Processor? processor = _processors.FirstOrDefault(p => p.Type == value.GetType()); + + processor?.Process(dictionary, (IEntity)value, this); } - - switch (value) + else { - case Entity entity: - ProcessEntity(dictionary, entity); - break; - - case Trait trait: - ProcessTrait(dictionary, trait); - break; + dictionary.Remove("Description"); } serializer.Serialize(writer, dictionary); + } - /* - writer.WriteStartObject(); + private abstract class Processor + { + public abstract Type Type { get; } - foreach (KeyValuePair keyValuePair in dictionary) + public abstract void Process( + Dictionary dictionary, + IEntity entity, + EntityConverter converter + ); + } + + private class Processor(Action, T, EntityConverter> method) + : Processor + where T : IEntity + { + public override Type Type { get; } = typeof(T); + + public override void Process( + Dictionary dictionary, + IEntity entity, + EntityConverter converter + ) { - writer.WritePropertyName(keyValuePair.Key, true); - - serializer.Serialize(writer, keyValuePair.Value); + method.Invoke(dictionary, (T)entity, converter); } - - writer.WriteEndObject(); - */ } - private void ProcessTrait(Dictionary dictionary, Trait trait) - { - LocaleString name = - trait.Entity?.Name - ?? _context.Entities.Where(e => e.Id == trait.Id).Select(e => e.Name).FirstOrDefault()!; + private readonly ICollection _processors = + [ + new Processor( + (dictionary, trait, converter) => + { + LocaleString name = + // ReSharper disable once ConditionalAccessQualifierIsNonNullableAccordingToAPIContract + trait.Entity?.Name + ?? converter + ._context.Entities.Where(e => e.Id == trait.Id) + .Select(e => e.Name) + .FirstOrDefault()!; - dictionary.Remove("Entity"); - dictionary.Remove("Entities"); - dictionary.Add("Name", name.ToString()); - } - - private void ProcessEntity(Dictionary dictionary, Entity entity) - { - dictionary.Remove("Description"); - } - - private void ProcessSubclass(Dictionary dictionary, Subclass entity) - { - dictionary.Remove("Group"); - } + dictionary.Remove("Entity"); + dictionary.Remove("Entities"); + dictionary.Add("Name", name.ToString()); + } + ), + new Processor( + (dictionary, _, _) => + { + dictionary.Remove("Group"); + } + ), + ]; public override bool CanRead => false; - public override object? ReadJson( + public override object ReadJson( JsonReader reader, Type objectType, object? existingValue, diff --git a/Pf2eViewer/Converters/LocaleStringConverter.cs b/Pf2eViewer/Converters/LocaleStringConverter.cs index 7594e01..432a37c 100644 --- a/Pf2eViewer/Converters/LocaleStringConverter.cs +++ b/Pf2eViewer/Converters/LocaleStringConverter.cs @@ -27,7 +27,7 @@ public class LocaleStringConverter : JsonConverter public override bool CanRead => false; - public override object? ReadJson( + public override object ReadJson( JsonReader reader, Type objectType, object? existingValue, diff --git a/Pf2eViewer/DarkModeCS.cs b/Pf2eViewer/DarkModeCS.cs index 99fe0f7..9fca4f9 100644 --- a/Pf2eViewer/DarkModeCS.cs +++ b/Pf2eViewer/DarkModeCS.cs @@ -1,15 +1,22 @@ -using System; -using System.ComponentModel; -using System.Diagnostics; -using System.Drawing; +using System.ComponentModel; using System.Drawing.Drawing2D; -using System.Linq; using System.Reflection; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; -using System.Windows.Forms; -using System.Xml.Linq; using Microsoft.Win32; +#pragma warning disable CS8603 // Possible null reference return. +#pragma warning disable CS0184 // 'is' expression's given expression is never of the provided type +#pragma warning disable CA1806 +#pragma warning disable CS8605 // Unboxing a possibly null value. +#pragma warning disable CS8622 // Nullability of reference types in type of parameter doesn't match the target delegate (possibly because of nullability attributes). +#pragma warning disable CS8604 // Possible null reference argument. +#pragma warning disable CS8600 // Converting null literal or possible null value to non-nullable type. +#pragma warning disable CS8602 // Dereference of a possibly null reference. +#pragma warning disable CS8625 // Cannot convert null literal to non-nullable reference type. +#pragma warning disable CS0169 // Field is never used +#pragma warning disable CS8618 // Non-nullable field must contain a non-null value when exiting constructor. Consider adding the 'required' modifier or declaring as nullable. + +// ReSharper disable All namespace Pf2eViewer; @@ -1088,8 +1095,9 @@ public class DarkModeCS /// Returns Windows's System Colors for UI components following Google Material Design concepts. /// [OPTIONAL] Applies DarkMode (if set) to this Window Title and Background. + /// O: DarkMode, 1: LightMode /// List of Colors: Background, OnBackground, Surface, OnSurface, Primary, OnPrimary, Secondary, OnSecondary - public static OSThemeColors GetSystemColors(Form Window = null, int ColorMode = 0) //<- O: DarkMode, 1: LightMode + public static OSThemeColors GetSystemColors(Form Window = null, int ColorMode = 0) { OSThemeColors _ret = new OSThemeColors(); @@ -1287,6 +1295,7 @@ public class DarkModeCS /// Attemps to apply Window's Dark Style to the Control and all its childs. /// + /// private static void ApplySystemDarkTheme(Control control = null, bool IsDarkMode = true) { /* diff --git a/Pf2eViewer/DatabaseViewer.cs b/Pf2eViewer/DatabaseViewer.cs new file mode 100644 index 0000000..1430544 --- /dev/null +++ b/Pf2eViewer/DatabaseViewer.cs @@ -0,0 +1,171 @@ +using LinqToDB.SqlQuery; +using LinqToDB.Tools; +using Microsoft.EntityFrameworkCore; +using Pf2eModel; +using Pf2eModel.Model; +using Pf2eModel.Model.Base; +using Pf2eModel.Model.Enum; +using Pf2eModel.Model.Structure; + +namespace Pf2eViewer; + +public class DatabaseViewer +{ + public readonly Pf2eDbContext Context = new(); + + private int _perPage = 30; + + private int _page = 1; + + private EntityType _entityType; + + public int PerPage + { + get => _perPage; + set + { + _perPage = value; + UpdateView(); + } + } + + public int Page + { + get => _page; + set + { + _page = value; + UpdateView(); + } + } + + public EntityType EntityType + { + get => _entityType; + set + { + _entityType = value; + UpdateView(); + } + } + + public int Count { get; private set; } + + public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage)); + + public ICollection Entities { get; private set; } = []; + + public delegate void ViewUpdateHandler(); + public event ViewUpdateHandler? ViewUpdateStart; + public event ViewUpdateHandler? ViewUpdate; + + public Entity? LastEntity; + public IEntity? LastSubEntity; + + public delegate void ViewSelectHandler(Entity? entity, IEntity? subEntity); + public event ViewSelectHandler? ViewSelect; + + public void UpdateView() + { + ViewUpdateStart?.Invoke(); + + Count = Context.Entities.Count(e => e.Type == EntityType); + + if (_page < 1 || _page > MaxPage) + { + _page = Math.Min(MaxPage, Math.Max(1, _page)); + } + + Entities = Context + .Entities.Where(e => e.Type == EntityType) + .OrderBy(e => e.Id) + .Skip((Page - 1) * PerPage) + .Take(PerPage) + .ToList(); + + ViewUpdate?.Invoke(); + } + + public void Select(Entity entity) + { + Getter? getter = _getters.FirstOrDefault(g => g.Type == entity.Type); + + Entity? fullEntity = Context + .Entities.Where(e => e.Id == entity.Id && e.Type == entity.Type) + .Include(e => e.Sources) + .ThenInclude(s => s.Source) + .Include(e => e.Keywords) + .Include(e => e.Externals) + .ThenInclude(x => x.Target) + .Include(e => e.Traits) + .ThenInclude(t => t.Entity) + .FirstOrDefault(); + + IEntity? subEntity = + (getter != null) + ? getter.Get(entity.Id, Context) + : (IEntity?)Context.Find(EntityOfTypeAttribute.Resolve(entity), entity.Id); + + ViewSelect?.Invoke(fullEntity, subEntity); + + LastEntity = fullEntity; + LastSubEntity = subEntity; + } + + private readonly ICollection _getters = + [ + new Getter( + EntityType.Ancestry, + context => context.Ancestries.Include(a => a.AncestryBoosts) + ), + new Getter( + EntityType.Archetype, + context => + context + .Archetypes.Include(a => a.DedicationFeat) + .ThenInclude(f => f.Entity) + .Include(a => a.Feats) + .ThenInclude(f => f.Entity) + ), + new Getter( + EntityType.Background, + context => + context + .Backgrounds.Include(b => b.BackgroundBoosts) + .Include(b => b.BackgroundSkills) + .ThenInclude(t => t.Training) + .Include(b => b.BackgroundItems) + ), + new Getter( + EntityType.Class, + context => + context + .Classes.Include(c => c.ClassKeyAbilities) + .Include(c => c.ClassTrainings) + .ThenInclude(t => t.Training) + .Include(c => c.ClassProgressions) + ), + new Getter( + EntityType.Feat, + context => + context.Feats.Include(f => f.Archetypes).ThenInclude(a => a.Entity).AsQueryable() + ), + ]; + + private abstract class Getter(EntityType type) + { + public readonly EntityType Type = type; + + public abstract IEntity? Get(int id, Pf2eDbContext context); + } + + private class Getter(EntityType type, Func> method) + : Getter(type) + where T : IEntity + { + public override IEntity? Get(int id, Pf2eDbContext context) + { + return method.Invoke(context).FirstOrDefault(c => c.Id == id); + } + } +} diff --git a/Pf2eViewer/MainViewForm.cs b/Pf2eViewer/MainViewForm.cs index 8f72a51..136183b 100644 --- a/Pf2eViewer/MainViewForm.cs +++ b/Pf2eViewer/MainViewForm.cs @@ -1,7 +1,7 @@ -using Microsoft.EntityFrameworkCore; +using System.Windows.Forms; +using Microsoft.EntityFrameworkCore; using Newtonsoft.Json; using Newtonsoft.Json.Converters; -using Newtonsoft.Json.Linq; using Newtonsoft.Json.Serialization; using Pf2eModel; using Pf2eModel.Model; @@ -9,6 +9,7 @@ using Pf2eModel.Model.Base; using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Pf2eViewer.Converters; +using static System.Windows.Forms.VisualStyles.VisualStyleElement; namespace Pf2eViewer; @@ -16,15 +17,15 @@ public partial class MainViewForm : Form { public Pf2eDbContext Context = new(); - public string JsonViewerHtml = string.Empty; + public readonly string JsonViewerHtml; - private DarkModeCS dm = null; + public readonly DatabaseViewer Viewer = new(); public MainViewForm() { InitializeComponent(); - dm = new DarkModeCS(this) { ColorMode = DarkModeCS.DisplayMode.SystemDefault }; + _ = new DarkModeCS(this) { ColorMode = DarkModeCS.DisplayMode.SystemDefault }; Reset(); @@ -32,6 +33,10 @@ public partial class MainViewForm : Form using StreamReader reader = new("Resources/json-viewer.html"); JsonViewerHtml = reader.ReadToEnd(); + + Viewer.ViewUpdateStart += Viewer_ViewUpdateStart; + Viewer.ViewUpdate += Viewer_ViewUpdate; + Viewer.ViewSelect += Viewer_ViewSelect; } public void Reset() @@ -43,53 +48,63 @@ public partial class MainViewForm : Form TypesListBox.SelectedIndex = 0; } - public const int PerPage = 30; - - public int Count; - - public int Page + private void ToggleFreeze(bool state) { - get => Convert.ToInt32(PageTextBox.Text); - set - { - PageTextBox.Text = value.ToString(); - UpdateTreeView(); - } + EntityGridView.Visible = state; + JsonWebView.Visible = state; + + FirstPageButton.Enabled = state; + PrevPageButton.Enabled = state; + NextPageButton.Enabled = state; + LastPageButton.Enabled = state; + + PageTextBox.Enabled = state; } - public int MaxPage => Convert.ToInt32(Math.Ceiling(1.0 * Count / PerPage)); + // Viewer events - private void UpdateTreeView() + private void Viewer_ViewUpdateStart() { - // EntityTreeView.Nodes.Clear(); - EntityType type = (EntityType)TypesListBox.SelectedItem!; - - Count = Context.Entities.Count(e => e.Type == type); - - if (Page < 1 || Page > MaxPage) - { - Page = Math.Min(MaxPage, Math.Max(1, Page)); - return; - } - - EntityGridView.DataSource = Context - .Entities.Where(e => e.Type == (EntityType)TypesListBox.SelectedItem) - .OrderBy(e => e.Id) - .Select(e => new EntityGridRow(e)) - .Skip((Page - 1) * PerPage) - .Take(PerPage) - .ToList(); - - EntityGridView.Rows[0].Selected = true; - - PaginationLabel.Text = $"Всего: {Count} объектов, {MaxPage} страниц"; - - FirstPageButton.Enabled = Page > 1; - PrevPageButton.Enabled = Page > 1; - NextPageButton.Enabled = Page < MaxPage; - LastPageButton.Enabled = Page < MaxPage; + ToggleFreeze(false); } + private void Viewer_ViewUpdate() + { + EntityGridView.DataSource = Viewer.Entities.Select(e => new EntityGridRow(e)); + + PaginationLabel.Text = $@"Всего: {Viewer.Count} объектов, {Viewer.MaxPage} страниц"; + + FirstPageButton.Enabled = Viewer.Page > 1; + PrevPageButton.Enabled = Viewer.Page > 1; + NextPageButton.Enabled = Viewer.Page < Viewer.MaxPage; + LastPageButton.Enabled = Viewer.Page < Viewer.MaxPage; + + ToggleFreeze(true); + } + + private void Viewer_ViewSelect(Entity? entity, IEntity? subEntity) + { + JsonSerializerSettings settings = new() + { + Converters = + [ + new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }, + new EntityConverter(), + new LocaleStringConverter(), + ], + ReferenceLoopHandling = ReferenceLoopHandling.Ignore, + }; + + string result = JsonConvert.SerializeObject(entity, settings); + string subResult = JsonConvert.SerializeObject(subEntity, settings); + + string json = result[..^1] + $",\"SubEntity\":{subResult}}}"; + + RenderJson(json); + } + + // Form events + private void TypesListBox_SelectedIndexChanged(object sender, EventArgs _) { if (TypesListBox.SelectedItem is null) @@ -97,27 +112,27 @@ public partial class MainViewForm : Form return; } - Page = 1; + Viewer.Page = 1; } private void FirstPageButton_Click(object sender, EventArgs e) { - Page = 1; + Viewer.Page = 1; } private void PrevPageButton_Click(object sender, EventArgs e) { - Page--; + Viewer.Page--; } private void NextPageButton_Click(object sender, EventArgs e) { - Page++; + Viewer.Page++; } private void LastPageButton_Click(object sender, EventArgs e) { - Page = MaxPage; + Viewer.Page = Viewer.MaxPage; } private void PageTextBox_KeyPress(object sender, KeyPressEventArgs e) @@ -129,7 +144,7 @@ public partial class MainViewForm : Form if (e.KeyChar == (char)Keys.Return) { - UpdateTreeView(); + Viewer.Page = Convert.ToInt32(PageTextBox.Text); } } @@ -148,37 +163,7 @@ public partial class MainViewForm : Form EntityGridRow data = (EntityGridRow)row.DataBoundItem; - ViewEntity(data.Id); - } - - public void ViewEntity(int id) - { - // EntityTreeView.Nodes.Clear(); - - Entity? entity = Context - .Entities.Where(e => e.Id == id && e.Type == (EntityType)TypesListBox.SelectedItem!) - .Include(e => e.Sources) - .ThenInclude(s => s.Source) - .Include(e => e.Keywords) - .Include(e => e.Externals) - .ThenInclude(x => x.Target) - .Include(e => e.Traits) - .ThenInclude(t => t.Entity) - .FirstOrDefault(); - - if (entity == null) - { - DescriptionTextBox.Text = "Not found"; - return; - } - - string json = SerializeEntity(entity); - - // JsonToTreeview(EntityTreeView, json, "Entity"); - - RenderJson(json); - - DescriptionTextBox.Text = entity.Description.Replace("\\r", "").Replace("\\n", "\\r\\n"); + Viewer.Select(data.GetEntity()); } public void RenderJson(string json) @@ -199,149 +184,17 @@ public partial class MainViewForm : Form ) { JsonWebView.Visible = true; - DataGridViewRow row = EntityGridView.SelectedRows[0]; - - EntityGridRow data = (EntityGridRow)row.DataBoundItem; - - ViewEntity(data.Id); - } - - public string SerializeEntity(Entity entity) - { - JsonSerializerSettings settings = new() - { - Converters = - [ - new StringEnumConverter { NamingStrategy = new CamelCaseNamingStrategy() }, - new EntityConverter(), - new LocaleStringConverter(), - ], - ReferenceLoopHandling = ReferenceLoopHandling.Ignore, - }; - - string result = JsonConvert.SerializeObject(entity, settings); - - Type type = EntityOfTypeAttribute.Resolve(entity); - string subResult = JsonConvert.SerializeObject(ResolveSubEntity(entity), settings); - - return result[..^1] + $",\"SubEntity\":{subResult}}}"; - } - - public IEntity? ResolveSubEntity(Entity entity) - { - switch (entity.Type) - { - case EntityType.Ancestry: - return Context - .Ancestries.Include(a => a.AncestryBoosts) - .FirstOrDefault(e => e.Id == entity.Id); - - case EntityType.Archetype: - return Context - .Archetypes.Include(a => a.DedicationFeat) - .ThenInclude(f => f.Entity) - .Include(a => a.Feats) - .ThenInclude(f => f.Entity) - .FirstOrDefault(a => a.Id == entity.Id); - - case EntityType.Background: - return Context - .Backgrounds.Include(b => b.BackgroundBoosts) - .Include(b => b.BackgroundSkills) - .ThenInclude(t => t.Training) - .Include(b => b.BackgroundItems) - .FirstOrDefault(e => e.Id == entity.Id); - - case EntityType.Class: - return Context - .Classes.Include(c => c.ClassKeyAbilities) - .Include(c => c.ClassTrainings) - .ThenInclude(t => t.Training) - .Include(c => c.ClassProgressions) - .FirstOrDefault(c => c.Id == entity.Id); - - case EntityType.Feat: - return Context - .Feats.Include(f => f.Archetypes) - .ThenInclude(a => a.Entity) - .FirstOrDefault(f => f.Id == entity.Id); - - default: - Type type = EntityOfTypeAttribute.Resolve(entity); - return (IEntity?)Context.Find(type, entity.Id); - } - } - - public static void JsonToTreeview(TreeView treeView, string jsonString, string rootName) - { - object json = jsonString.StartsWith('[') - ? JArray.Parse(jsonString) - : JObject.Parse(jsonString); - - treeView.Nodes.Clear(); - foreach (TreeNode node in Element2TreeNode(json, rootName).Nodes) - { - treeView.Nodes.Add(node); - } - treeView.ExpandAll(); - } - - private static TreeNode Element2TreeNode(object element, string text = "") - { - List subNodes = []; - - switch (element.GetType().Name) - { - case "JObject": - JObject jObject = (JObject)element; - foreach (KeyValuePair token in jObject) - { - subNodes.Add(Element2TreeNode(token.Value, token.Key)); - } - break; - - case "JArray": - JArray jArray = (JArray)element; - int i = 0; - subNodes.AddRange(jArray.Select(token => Element2TreeNode(token, $"[{i++}]"))); - - if (i == 0) - { - text += ": []"; - } - break; - - case "JValue": - JValue jValue = (JValue)element; - text += $": ({jValue.Type}) {jValue}"; - break; - - default: - throw new Exception( - "clsJSON2Treeview can't interpret object:" + element.GetType().Name - ); - } - - TreeNode treeNode = new(text); - - if (subNodes.Count > 0) - { - treeNode.Nodes.AddRange( - subNodes.OrderBy(node => node.Nodes.Count > 0 ? 1 : 0).ToArray() - ); - } - - return treeNode; - } - - private class EntityGridRow(Entity e) - { - public int Id { get; set; } = e.Id; - public string Name { get; set; } = e.Name[LocaleType.English]; - public EntityRarity Rarity { get; set; } = e.Rarity; - - private readonly Entity _e = e; - - public Entity GetEntity() => _e; + Viewer_ViewSelect(Viewer.LastEntity, Viewer.LastSubEntity); } } + +internal class EntityGridRow(Entity e) +{ + public int Id { get; set; } = e.Id; + public string Name { get; set; } = e.Name[LocaleType.English]; + public EntityRarity Rarity { get; set; } = e.Rarity; + + private readonly Entity _e = e; + + public Entity GetEntity() => _e; +}