From 31ad99ade5e7f1f4b0ab44e4ae1fd6c9867ceee3 Mon Sep 17 00:00:00 2001 From: kozhilya Date: Thu, 24 Apr 2025 15:53:05 +0400 Subject: [PATCH] =?UTF-8?q?I18n=20=D1=87=D0=B5=D1=80=D0=B5=D0=B7=20jsonb-?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D1=8F?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Aonprd/DataBuilder/AonprdDataBuilder.cs | 3 +- .../{ => Processors}/AbstractProcessor.cs | 52 ++---- .../AonprdProcessorAttribute.cs | 2 +- .../AonprdProcessorCollection.cs | 2 +- .../Processors/{ => Types}/ActionProcessor.cs | 2 +- .../{ => Types}/AncestryProcessor.cs | 2 +- .../{ => Types}/ArchetypeProcessor.cs | 3 +- .../{ => Types}/BackgroundProcessor.cs | 2 +- .../Processors/{ => Types}/ClassProcessor.cs | 3 +- .../Processors/{ => Types}/FeatProcessor.cs | 10 +- .../{ => Types}/LanguageProcessor.cs | 2 +- .../Processors/{ => Types}/SourceProcessor.cs | 2 +- .../{ => Types}/TrainingProcessor.cs | 2 +- .../Processors/{ => Types}/TraitProcessor.cs | 2 +- .../DataBuilder/AbstractProcessor.cs | 14 +- Pf2eDataScrapper/Pf2eDataScrapper.csproj | 4 + .../Migrations/20250418062358_Function.cs | 82 +++++++--- ...signer.cs => 20250424112427_1.Designer.cs} | 46 +----- ...0250421123520_1.cs => 20250424112427_1.cs} | 26 +-- .../Migrations/Pf2eDbContextModelSnapshot.cs | 44 +----- Pf2eModel/Model/Base/Entity.cs | 6 +- Pf2eModel/Model/Base/EntityAlias.cs | 24 --- .../Model/Common/I18n/LocaleExtension.cs | 46 ++++++ Pf2eModel/Model/Common/I18n/LocaleItem.cs | 10 ++ Pf2eModel/Model/Common/I18n/LocaleString.cs | 148 ++++++++++++++++++ Pf2eModel/Model/Common/I18n/LocaleType.cs | 24 +++ Pf2eModel/Model/Enum/AliasType.cs | 21 --- Pf2eModel/Model/Structure/Ancestry.cs | 2 +- Pf2eModel/Pf2eDbContext.cs | 5 +- Pf2eModel/Pf2eDbContextModel.cs | 74 +++++++-- Pf2eModel/Pf2eModel.csproj | 1 + Pf2eViewer/EntityConverter.cs | 5 +- Pf2eViewer/MainViewForm.cs | 2 +- 33 files changed, 409 insertions(+), 264 deletions(-) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/{ => Processors}/AbstractProcessor.cs (84%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/{ => Processors}/AonprdProcessorAttribute.cs (96%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/{ => Processors}/AonprdProcessorCollection.cs (99%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/ActionProcessor.cs (98%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/AncestryProcessor.cs (98%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/ArchetypeProcessor.cs (97%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/BackgroundProcessor.cs (98%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/ClassProcessor.cs (97%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/FeatProcessor.cs (85%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/LanguageProcessor.cs (94%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/SourceProcessor.cs (97%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/TrainingProcessor.cs (97%) rename Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/{ => Types}/TraitProcessor.cs (97%) rename Pf2eModel/Migrations/{20250421123520_1.Designer.cs => 20250424112427_1.Designer.cs} (98%) rename Pf2eModel/Migrations/{20250421123520_1.cs => 20250424112427_1.cs} (97%) delete mode 100644 Pf2eModel/Model/Base/EntityAlias.cs create mode 100644 Pf2eModel/Model/Common/I18n/LocaleExtension.cs create mode 100644 Pf2eModel/Model/Common/I18n/LocaleItem.cs create mode 100644 Pf2eModel/Model/Common/I18n/LocaleString.cs create mode 100644 Pf2eModel/Model/Common/I18n/LocaleType.cs delete mode 100644 Pf2eModel/Model/Enum/AliasType.cs diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs index e8f7d41..bc3e998 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdDataBuilder.cs @@ -1,6 +1,7 @@ using Konsole; using Pf2eDataScrapper.Importers.Implementations.Aonprd.Compendium; -using static Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.AbstractProcessor; +using Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +using static Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.AbstractProcessor; namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AbstractProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs similarity index 84% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AbstractProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs index ebf95a2..4b9cc6d 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AbstractProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AbstractProcessor.cs @@ -7,13 +7,15 @@ 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; +using System.Collections.Generic; using System.Security.Cryptography; using System.Text; using System.Text.RegularExpressions; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; internal abstract partial class AbstractProcessor(AonprdProcessorInput input) { @@ -65,11 +67,11 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input) { Entity.Id = Helper.NextEntityId; Entity.Type = Type; - Entity.Name = Input.GetRequired("name"); + 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); + Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]); Entity.IsTranslated = false; Entity.IsChecked = false; @@ -81,15 +83,6 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input) protected virtual void AddAlias() { - EntityAlias alias = new() - { - Type = AliasType.English, - Alias = Entity.Name, - Entity = Entity - }; - - Context.EntityAliases.Add(alias); - JArray? legacyIdArray = Input.GetOptional("legacy_id"); if (legacyIdArray is null) @@ -101,17 +94,8 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input) if (LegacyAliases.TryGetValue(legacyId, out string? legacyName)) { - if (legacyName != Entity.Name) - { - EntityAlias legacyAlias = new() - { - Type = AliasType.LegacyEnglish, - Alias = legacyName, - Entity = Entity - }; - - Context.EntityAliases.Add(legacyAlias); - } + // if (legacyName != Entity.Name[LocaleType.English]) + Entity.Name[LocaleType.LegacyEnglish] = legacyName; LegacyAliases.Remove(legacyId); } @@ -187,20 +171,18 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input) IEnumerable names = traitsJson.ToArray().Select(jToken => jToken.ToString()); List traitEntities = Context.Traits - .SelectMany(t => t.Entity.Aliases) - .Where(a => names.Contains(a.Alias)) - .Select(a => a.Entity) + .Select(t => t.Entity) + .Where(e => Pf2eDbContext.LocaleSearchArray(e.Name, names)) .ToList(); List traits = Context.Traits .Include(t => t.Entity) - .ThenInclude(e => e.Aliases) .Where(t => traitEntities.Contains(t.Entity)) .ToList(); foreach (string name in names) { - if (!traits.Any(t => t.Entity.Aliases.Select(a => a.Alias).Contains(name))) + if (!traits.Any(t => t.Entity.Name.Contains(name))) { throw new Exception($"Trait '{name}' is not found!!!"); } @@ -216,18 +198,10 @@ internal abstract partial class AbstractProcessor(AonprdProcessorInput input) { if (RemasterWithoutLegacy.TryGetValue(id, out Entity? remastered)) { - if (remastered.Name == name) - { - return; - } + // if (remastered.ToString() != name) + remastered.Name[LocaleType.LegacyEnglish] = name; - EntityAlias alias = new() - { - Type = AliasType.LegacyEnglish, - Alias = name, - Entity = remastered! - }; - Context.EntityAliases.Add(alias); + RemasterWithoutLegacy.Remove(id); } else { diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorAttribute.cs similarity index 96% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorAttribute.cs index d797eb6..fd9af3d 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorAttribute.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorAttribute.cs @@ -1,4 +1,4 @@ -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; [AttributeUsage(AttributeTargets.Class)] public class AonprdProcessorAttribute(string name, string type, string[] dependencies) : Attribute diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorCollection.cs similarity index 99% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorCollection.cs index 7654688..35c009d 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/AonprdProcessorCollection.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AonprdProcessorCollection.cs @@ -1,6 +1,6 @@ using System.Reflection; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; internal class AonprdProcessorCollection { diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ActionProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs similarity index 98% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ActionProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs index 41b0471..7315223 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ActionProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ActionProcessor.cs @@ -2,7 +2,7 @@ using Pf2eModel.Model; using Pf2eModel.Model.Enum; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("action")] internal class ActionProcessor(AonprdProcessorInput input) : AbstractProcessor(input) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AncestryProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs similarity index 98% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AncestryProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs index 006f6ba..554fd0d 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/AncestryProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/AncestryProcessor.cs @@ -6,7 +6,7 @@ using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure.AncestryRelation; using System.Text.RegularExpressions; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("ancestry", ["trait", "language"])] internal class AncestryProcessor(AonprdProcessorInput input) : AbstractProcessor(input) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ArchetypeProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs similarity index 97% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ArchetypeProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs index 745c899..9a4c09f 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ArchetypeProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ArchetypeProcessor.cs @@ -6,7 +6,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("archetype")] internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcessor(input) @@ -23,7 +23,6 @@ internal class ArchetypeProcessor(AonprdProcessorInput input) : AbstractProcesso Object.Level = Input.GetOptional("level"); - } protected override string GetDescription() diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/BackgroundProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs similarity index 98% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/BackgroundProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs index 830df34..63640ee 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/BackgroundProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/BackgroundProcessor.cs @@ -9,7 +9,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("background")] internal class BackgroundProcessor(AonprdProcessorInput input) : AbstractProcessor(input) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ClassProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs similarity index 97% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ClassProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs index f00a414..d271e3b 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/ClassProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/ClassProcessor.cs @@ -14,7 +14,7 @@ using System.Text; using System.Text.RegularExpressions; using System.Threading.Tasks; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("class", ["trait", "source", "skill"])] internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(input) @@ -89,6 +89,7 @@ internal class ClassProcessor(AonprdProcessorInput input) : AbstractProcessor(in .ToArray(); Training[] skills = Context.Trainings.Where(t => skillNames.Contains(t.Name)).ToArray(); + // Training[] skills = Context.Trainings.Where(t => Pf2eDbContext.LocaleSearchArray(t.Name, skillNames)).ToArray(); return skills.Select(skill => new ClassTraining() { diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/FeatProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs similarity index 85% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/FeatProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs index ea0b586..91310af 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/FeatProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/FeatProcessor.cs @@ -1,8 +1,9 @@ using Newtonsoft.Json.Linq; +using Pf2eModel; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("feat", ["trait", "source", "archetype"])] internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(input) @@ -42,7 +43,10 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp string[] archetypeStrings = Input.GetOptional("archetype")?.Select(t => t.ToString()).ToArray() ?? []; - var archetypes = Context.Archetypes.Where(a => archetypeStrings.Contains(a.Entity.Name)).ToArray(); + var archetypes = Context.Archetypes + .Where(a => Pf2eDbContext.LocaleSearchArray(a.Entity.Name, archetypeStrings)) + // .Where(a => archetypeStrings.Contains(a.Entity.Name.ToString())) + .ToArray(); if (archetypes.Length != 0) { @@ -53,7 +57,7 @@ internal class FeatProcessor(AonprdProcessorInput input) : AbstractProcessor(inp { Object.Archetypes.Add(archetype); - if (Entity.Name.Contains("Dedication")) + if (Entity.Name.Any(l => l.Value.Contains("Dedication"))) { archetype.DedicationFeat = Object; } diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/LanguageProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs similarity index 94% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/LanguageProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs index db40f4f..7786213 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/LanguageProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/LanguageProcessor.cs @@ -1,7 +1,7 @@ using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("language")] internal class LanguageProcessor(AonprdProcessorInput input) : AbstractProcessor(input) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/SourceProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SourceProcessor.cs similarity index 97% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/SourceProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SourceProcessor.cs index ba7df75..9493f16 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/SourceProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/SourceProcessor.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("source", new string[] { })] internal class SourceProcessor(AonprdProcessorInput input) : AbstractProcessor(input) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/TrainingProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TrainingProcessor.cs similarity index 97% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/TrainingProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TrainingProcessor.cs index 0bb204c..fae7d7b 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/TrainingProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TrainingProcessor.cs @@ -8,7 +8,7 @@ using System.Linq; using System.Text; using System.Threading.Tasks; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("skill", new string[] { })] internal class TrainingProcessor(AonprdProcessorInput input) : AbstractProcessor(input) diff --git a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/TraitProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs similarity index 97% rename from Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/TraitProcessor.cs rename to Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs index 251e713..b35da45 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/TraitProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Aonprd/DataBuilder/Processors/Types/TraitProcessor.cs @@ -1,7 +1,7 @@ using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; -namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors; +namespace Pf2eDataScrapper.Importers.Implementations.Aonprd.DataBuilder.Processors.Types; [AonprdProcessor("trait", new string[] { })] internal class TraitProcessor(AonprdProcessorInput input) : AbstractProcessor(input) diff --git a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/AbstractProcessor.cs b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/AbstractProcessor.cs index d222cd0..3225744 100644 --- a/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/AbstractProcessor.cs +++ b/Pf2eDataScrapper/Importers/Implementations/Pf2eFoundry/DataBuilder/AbstractProcessor.cs @@ -4,6 +4,7 @@ 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; using System.Security.Cryptography; @@ -30,14 +31,14 @@ internal abstract class AbstractProcessor(ProcessorInput input) Entity.Id = Helper.NextEntityId; Entity.Type = Type; - Entity.Name = Input.GetRequired("name"); + Entity.Name[LocaleType.English] = Input.GetRequired("name"); Entity.Img = Input.GetRequired("img"); Entity.Description = Input.GetRequired("system.description.value"); Entity.IsTranslated = false; Entity.IsChecked = false; - Entity.Slug = Helper.GenerateSlug(Entity.Name); + Entity.Slug = Helper.GenerateSlug(Entity.Name[LocaleType.English]); Entity.Rarity = Input.EnumOptional("system.traits.rarity", EntityRarity.Common); Object.Id = Entity.Id; @@ -45,15 +46,16 @@ internal abstract class AbstractProcessor(ProcessorInput input) Context.Entities.Add(Entity); // Alias + /* EntityAlias alias = new() { - Type = AliasType.English, + Type = LocaleType.English, Alias = Entity.Name, EntityId = Entity.Id, Entity = Entity }; Context.EntityAliases.Add(alias); - + */ // Source EntitySource source = new() { @@ -138,11 +140,11 @@ internal abstract class AbstractProcessor(ProcessorInput input) protected IEnumerable GetTraits(IEnumerable names) { - List result = Context.Traits.Where(t => names.Contains(t.Entity.Name)).ToList(); + List result = Context.Traits.Where(t => names.Contains(t.Entity.ToString())).ToList(); foreach (string name in names) { - if (result.Any(t => t.Entity.Name == name)) + if (result.Any(t => t.Entity.Name.Contains(name))) { continue; } diff --git a/Pf2eDataScrapper/Pf2eDataScrapper.csproj b/Pf2eDataScrapper/Pf2eDataScrapper.csproj index 5393688..fb0c769 100644 --- a/Pf2eDataScrapper/Pf2eDataScrapper.csproj +++ b/Pf2eDataScrapper/Pf2eDataScrapper.csproj @@ -37,4 +37,8 @@ + + + + diff --git a/Pf2eModel/Migrations/20250418062358_Function.cs b/Pf2eModel/Migrations/20250418062358_Function.cs index b1ca97a..f5a7f8c 100644 --- a/Pf2eModel/Migrations/20250418062358_Function.cs +++ b/Pf2eModel/Migrations/20250418062358_Function.cs @@ -1,4 +1,8 @@ using Microsoft.EntityFrameworkCore.Migrations; +using Pf2eModel.Model.Structure; +using static LinqToDB.Common.Configuration; +using static System.Runtime.InteropServices.JavaScript.JSType; +using System.Buffers; #nullable disable @@ -22,46 +26,76 @@ BEGIN || ' CASCADE;' FROM pg_tables WHERE schemaname = 'public' - AND tablename != '__EFMigrationsHistory' + AND tablename != '__EFMigrationsHistory' ); END $func$; """); migrationBuilder.Sql(""" CREATE OR REPLACE FUNCTION f_drop_tables() - RETURNS void - LANGUAGE plpgsql AS + RETURNS void + LANGUAGE plpgsql AS $func$ BEGIN - EXECUTE - (SELECT 'DROP TABLE ' - || string_agg(format('%I.%I', schemaname, tablename), ', ') - || ' CASCADE;' - FROM pg_tables - WHERE schemaname = 'public' - ); + EXECUTE + (SELECT 'DROP TABLE ' + || string_agg(format('%I.%I', schemaname, tablename), ', ') + || ' CASCADE;' + FROM pg_tables + WHERE schemaname = 'public' + ); END $func$; """); migrationBuilder.Sql(""" CREATE OR REPLACE FUNCTION f_permission_tables(_username text) - RETURNS void - LANGUAGE plpgsql AS -$func$ + RETURNS void + LANGUAGE plpgsql AS +$func$ DECLARE - _tbl text; - _sch text; + _tbl text; + _sch text; BEGIN - FOR _sch, _tbl IN - SELECT schemaname, tablename - FROM pg_tables - WHERE schemaname = 'public' - LOOP - EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', _sch, _tbl, _username); - END LOOP; + FOR _sch, _tbl IN + SELECT schemaname, tablename + FROM pg_tables + WHERE schemaname = 'public' + LOOP + EXECUTE format('ALTER TABLE %I.%I OWNER TO %I', _sch, _tbl, _username); + END LOOP; END $func$; -"""); +""" + ); + + migrationBuilder.Sql(""" +CREATE OR REPLACE FUNCTION locale_search(data jsonb, search_value text) + RETURNS boolean + LANGUAGE plpgsql AS +$func$ +BEGIN + RETURN jsonb_path_exists(data, '$.** ? (@ == $val)', jsonb_build_object('val', search_value)); +END; +$func$; +""" + ); + + migrationBuilder.Sql(""" +CREATE OR REPLACE FUNCTION locale_search_array(data jsonb, search_values text[]) + RETURNS boolean + LANGUAGE plpgsql AS +$func$ +BEGIN + RETURN EXISTS ( + SELECT 1 + FROM unnest(search_values) AS val + WHERE jsonb_path_exists(data, '$.** ? (@ == $val)', jsonb_build_object('val', val)) + ); +END; +$func$; +""" + ); + } /// @@ -70,5 +104,7 @@ $func$; migrationBuilder.Sql("DROP FUNCTION f_truncate_tables;"); migrationBuilder.Sql("DROP FUNCTION f_drop_tables;"); migrationBuilder.Sql("DROP FUNCTION f_permission_tables;"); + migrationBuilder.Sql("DROP FUNCTION locale_search;"); + migrationBuilder.Sql("DROP FUNCTION locale_search_array;"); } } diff --git a/Pf2eModel/Migrations/20250421123520_1.Designer.cs b/Pf2eModel/Migrations/20250424112427_1.Designer.cs similarity index 98% rename from Pf2eModel/Migrations/20250421123520_1.Designer.cs rename to Pf2eModel/Migrations/20250424112427_1.Designer.cs index 54e8cfb..2afff95 100644 --- a/Pf2eModel/Migrations/20250421123520_1.Designer.cs +++ b/Pf2eModel/Migrations/20250424112427_1.Designer.cs @@ -13,7 +13,7 @@ using Pf2eModel.Model.Enum; namespace Pf2eModel.Migrations { [DbContext(typeof(Pf2eDbContext))] - [Migration("20250421123520_1")] + [Migration("20250424112427_1")] partial class _1 { /// @@ -25,7 +25,6 @@ namespace Pf2eModel.Migrations .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "action_category", new[] { "unknown", "archetype", "class-feature", "class-feat", "offensive", "defensive", "interaction", "familiar", "army_tactic", "army_war_action", "kingdom_activity", "kingdom_feat", "kingdom_feature" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "alias_type", new[] { "legacy", "english", "legacy_english", "hobby", "other" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ancestry_vision", new[] { "normal", "low-light-vision", "dark-vision" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "attribute_values", new[] { "free", "str", "dex", "con", "int", "wis", "cha" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "class_progression_type", new[] { "entity", "ancestry_feat", "class_feat", "general_feat", "skill_feat", "skill_increase" }); @@ -37,6 +36,7 @@ namespace Pf2eModel.Migrations NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_type", new[] { "unknown", "trait", "action", "ancestry", "archetype", "background", "backpack", "campaign_feature", "class", "condition", "deity", "feat", "heritage", "spell", "language" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_action_type", new[] { "passive", "action", "reaction", "free" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_category", new[] { "uncategorized", "ancestry-feature", "class-feature", "calling", "class", "skill", "general", "ancestry", "bonus", "deityboon", "curse", "pfsboon", "campaign-feature" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "locale_type", new[] { "english", "legacy_english", "russian", "legacy", "hobby", "other" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "size", new[] { "tiny", "small", "medium", "large" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "source_category", new[] { "rulebook", "adventure-path", "lost-omens", "adventures", "comics", "blog-posts", "society" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder" }); @@ -142,8 +142,7 @@ namespace Pf2eModel.Migrations b.Property("Name") .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") + .HasColumnType("jsonb") .HasColumnName("name") .HasColumnOrder(2) .HasComment("Отображаемое имя"); @@ -173,31 +172,6 @@ namespace Pf2eModel.Migrations b.ToTable("entities", (string)null); }); - modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b => - { - b.Property("EntityId") - .HasColumnType("integer") - .HasColumnName("entity_id") - .HasColumnOrder(1); - - b.Property("Type") - .HasColumnType("alias_type") - .HasColumnName("type") - .HasColumnOrder(2); - - b.Property("Alias") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("alias") - .HasColumnOrder(3); - - b.HasKey("EntityId", "Type") - .HasName("pk_entity_aliases"); - - b.ToTable("entity_aliases", (string)null); - }); - modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.Keyword", b => { b.Property("Id") @@ -1536,18 +1510,6 @@ namespace Pf2eModel.Migrations .HasConstraintName("fk_entity_trait_traits_traits_id"); }); - modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b => - { - b.HasOne("Pf2eModel.Model.Base.Entity", "Entity") - .WithMany("Aliases") - .HasForeignKey("EntityId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entity_aliases_entities_entity_id"); - - b.Navigation("Entity"); - }); - modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.KeywordAlias", b => { b.HasOne("Pf2eModel.Model.Base.Keywords.Keyword", "Keyword") @@ -2056,8 +2018,6 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Base.Entity", b => { - b.Navigation("Aliases"); - b.Navigation("Externals"); b.Navigation("Sources"); diff --git a/Pf2eModel/Migrations/20250421123520_1.cs b/Pf2eModel/Migrations/20250424112427_1.cs similarity index 97% rename from Pf2eModel/Migrations/20250421123520_1.cs rename to Pf2eModel/Migrations/20250424112427_1.cs index 878bb35..9ace4b0 100644 --- a/Pf2eModel/Migrations/20250421123520_1.cs +++ b/Pf2eModel/Migrations/20250424112427_1.cs @@ -15,7 +15,6 @@ namespace Pf2eModel.Migrations { migrationBuilder.AlterDatabase() .Annotation("Npgsql:Enum:action_category", "unknown,archetype,class-feature,class-feat,offensive,defensive,interaction,familiar,army_tactic,army_war_action,kingdom_activity,kingdom_feat,kingdom_feature") - .Annotation("Npgsql:Enum:alias_type", "legacy,english,legacy_english,hobby,other") .Annotation("Npgsql:Enum:ancestry_vision", "normal,low-light-vision,dark-vision") .Annotation("Npgsql:Enum:attribute_values", "free,str,dex,con,int,wis,cha") .Annotation("Npgsql:Enum:class_progression_type", "entity,ancestry_feat,class_feat,general_feat,skill_feat,skill_increase") @@ -27,6 +26,7 @@ namespace Pf2eModel.Migrations .Annotation("Npgsql:Enum:entity_type", "unknown,trait,action,ancestry,archetype,background,backpack,campaign_feature,class,condition,deity,feat,heritage,spell,language") .Annotation("Npgsql:Enum:feat_action_type", "passive,action,reaction,free") .Annotation("Npgsql:Enum:feat_category", "uncategorized,ancestry-feature,class-feature,calling,class,skill,general,ancestry,bonus,deityboon,curse,pfsboon,campaign-feature") + .Annotation("Npgsql:Enum:locale_type", "english,legacy_english,russian,legacy,hobby,other") .Annotation("Npgsql:Enum:size", "tiny,small,medium,large") .Annotation("Npgsql:Enum:source_category", "rulebook,adventure-path,lost-omens,adventures,comics,blog-posts,society") .Annotation("Npgsql:Enum:spell_area_type", "burst,emanation,cone,line,square,cube,cylinder") @@ -41,7 +41,7 @@ namespace Pf2eModel.Migrations { id = table.Column(type: "integer", nullable: false) .Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn), - name = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Отображаемое имя"), + name = table.Column(type: "jsonb", nullable: false, comment: "Отображаемое имя"), type = table.Column(type: "entity_type", nullable: false, comment: "Тип объекта"), slug = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Часть имени в URL"), img = table.Column(type: "character varying(255)", maxLength: 255, nullable: false, comment: "Изображение объекта"), @@ -250,25 +250,6 @@ namespace Pf2eModel.Migrations onDelete: ReferentialAction.Cascade); }); - migrationBuilder.CreateTable( - name: "entity_aliases", - columns: table => new - { - entity_id = table.Column(type: "integer", nullable: false), - type = table.Column(type: "alias_type", nullable: false), - alias = table.Column(type: "character varying(255)", maxLength: 255, nullable: false) - }, - constraints: table => - { - table.PrimaryKey("pk_entity_aliases", x => new { x.entity_id, x.type }); - table.ForeignKey( - name: "fk_entity_aliases_entities_entity_id", - column: x => x.entity_id, - principalTable: "entities", - principalColumn: "id", - onDelete: ReferentialAction.Cascade); - }); - migrationBuilder.CreateTable( name: "feats", columns: table => new @@ -1189,9 +1170,6 @@ namespace Pf2eModel.Migrations migrationBuilder.DropTable( name: "deity_weapons"); - migrationBuilder.DropTable( - name: "entity_aliases"); - migrationBuilder.DropTable( name: "entity_externals"); diff --git a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs index 5cc4f9e..e41ede2 100644 --- a/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs +++ b/Pf2eModel/Migrations/Pf2eDbContextModelSnapshot.cs @@ -22,7 +22,6 @@ namespace Pf2eModel.Migrations .HasAnnotation("Relational:MaxIdentifierLength", 63); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "action_category", new[] { "unknown", "archetype", "class-feature", "class-feat", "offensive", "defensive", "interaction", "familiar", "army_tactic", "army_war_action", "kingdom_activity", "kingdom_feat", "kingdom_feature" }); - NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "alias_type", new[] { "legacy", "english", "legacy_english", "hobby", "other" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "ancestry_vision", new[] { "normal", "low-light-vision", "dark-vision" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "attribute_values", new[] { "free", "str", "dex", "con", "int", "wis", "cha" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "class_progression_type", new[] { "entity", "ancestry_feat", "class_feat", "general_feat", "skill_feat", "skill_increase" }); @@ -34,6 +33,7 @@ namespace Pf2eModel.Migrations NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "entity_type", new[] { "unknown", "trait", "action", "ancestry", "archetype", "background", "backpack", "campaign_feature", "class", "condition", "deity", "feat", "heritage", "spell", "language" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_action_type", new[] { "passive", "action", "reaction", "free" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "feat_category", new[] { "uncategorized", "ancestry-feature", "class-feature", "calling", "class", "skill", "general", "ancestry", "bonus", "deityboon", "curse", "pfsboon", "campaign-feature" }); + NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "locale_type", new[] { "english", "legacy_english", "russian", "legacy", "hobby", "other" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "size", new[] { "tiny", "small", "medium", "large" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "source_category", new[] { "rulebook", "adventure-path", "lost-omens", "adventures", "comics", "blog-posts", "society" }); NpgsqlModelBuilderExtensions.HasPostgresEnum(modelBuilder, "spell_area_type", new[] { "burst", "emanation", "cone", "line", "square", "cube", "cylinder" }); @@ -139,8 +139,7 @@ namespace Pf2eModel.Migrations b.Property("Name") .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") + .HasColumnType("jsonb") .HasColumnName("name") .HasColumnOrder(2) .HasComment("Отображаемое имя"); @@ -170,31 +169,6 @@ namespace Pf2eModel.Migrations b.ToTable("entities", (string)null); }); - modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b => - { - b.Property("EntityId") - .HasColumnType("integer") - .HasColumnName("entity_id") - .HasColumnOrder(1); - - b.Property("Type") - .HasColumnType("alias_type") - .HasColumnName("type") - .HasColumnOrder(2); - - b.Property("Alias") - .IsRequired() - .HasMaxLength(255) - .HasColumnType("character varying(255)") - .HasColumnName("alias") - .HasColumnOrder(3); - - b.HasKey("EntityId", "Type") - .HasName("pk_entity_aliases"); - - b.ToTable("entity_aliases", (string)null); - }); - modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.Keyword", b => { b.Property("Id") @@ -1533,18 +1507,6 @@ namespace Pf2eModel.Migrations .HasConstraintName("fk_entity_trait_traits_traits_id"); }); - modelBuilder.Entity("Pf2eModel.Model.Base.EntityAlias", b => - { - b.HasOne("Pf2eModel.Model.Base.Entity", "Entity") - .WithMany("Aliases") - .HasForeignKey("EntityId") - .OnDelete(DeleteBehavior.Cascade) - .IsRequired() - .HasConstraintName("fk_entity_aliases_entities_entity_id"); - - b.Navigation("Entity"); - }); - modelBuilder.Entity("Pf2eModel.Model.Base.Keywords.KeywordAlias", b => { b.HasOne("Pf2eModel.Model.Base.Keywords.Keyword", "Keyword") @@ -2053,8 +2015,6 @@ namespace Pf2eModel.Migrations modelBuilder.Entity("Pf2eModel.Model.Base.Entity", b => { - b.Navigation("Aliases"); - b.Navigation("Externals"); b.Navigation("Sources"); diff --git a/Pf2eModel/Model/Base/Entity.cs b/Pf2eModel/Model/Base/Entity.cs index 96980b0..5715fad 100644 --- a/Pf2eModel/Model/Base/Entity.cs +++ b/Pf2eModel/Model/Base/Entity.cs @@ -2,6 +2,7 @@ using Pf2eModel.Model.Base.Keywords; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; +using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; using System.ComponentModel.DataAnnotations; @@ -16,9 +17,8 @@ public class Entity public int Id { get; set; } [Column(Order = 2)] - [MaxLength(Pf2eDbContext.NameLength)] [Comment("Отображаемое имя")] - public string Name { get; set; } = null!; + public LocaleString Name { get; set; } = new(); [Column(Order = 3)] [Comment("Тип объекта")] @@ -53,8 +53,6 @@ public class Entity public virtual ICollection Keywords { get; set; } = []; - public virtual ICollection Aliases { get; set; } = []; - public virtual ICollection Sources { get; set; } = []; public virtual ICollection Externals { get; set; } = []; diff --git a/Pf2eModel/Model/Base/EntityAlias.cs b/Pf2eModel/Model/Base/EntityAlias.cs deleted file mode 100644 index 5af3a05..0000000 --- a/Pf2eModel/Model/Base/EntityAlias.cs +++ /dev/null @@ -1,24 +0,0 @@ -using Microsoft.EntityFrameworkCore; -using Newtonsoft.Json; -using Pf2eModel.Model.Enum; -using System.ComponentModel.DataAnnotations; -using System.ComponentModel.DataAnnotations.Schema; - -namespace Pf2eModel.Model.Base; - -[PrimaryKey(nameof(EntityId), nameof(Type))] -public partial class EntityAlias -{ - [Column(Order = 1)] - public int EntityId { get; set; } - - [Column(Order = 2)] - public AliasType Type { get; set; } = AliasType.Other; - - [Column(Order = 3)] - [MaxLength(Pf2eDbContext.NameLength)] - public string Alias { get; set; } = null!; - - [JsonIgnore] - public virtual Entity Entity { get; set; } = null!; -} diff --git a/Pf2eModel/Model/Common/I18n/LocaleExtension.cs b/Pf2eModel/Model/Common/I18n/LocaleExtension.cs new file mode 100644 index 0000000..d3bc1a5 --- /dev/null +++ b/Pf2eModel/Model/Common/I18n/LocaleExtension.cs @@ -0,0 +1,46 @@ +using Microsoft.EntityFrameworkCore; +using Pf2eModel.Model.Base; +using System.ComponentModel; +using System.Linq.Expressions; +using System.Reflection; + +namespace Pf2eModel.Model.Common.I18n; +public static class LocaleExtension +{ + public static string GetTypeString(this LocaleType localeType) + { + FieldInfo fi = localeType.GetType().GetField(localeType.ToString())!; + + DescriptionAttribute[] attributes = (DescriptionAttribute[]) fi.GetCustomAttributes(typeof(DescriptionAttribute), false); + + return (attributes.Length != 0 ? attributes.FirstOrDefault()?.Description : null) ?? localeType.ToString(); + } + + public static LocaleType GetTypeByString(this string localeType) + { + foreach (System.Reflection.FieldInfo field in typeof(LocaleType).GetFields()) + { + if (Attribute.GetCustomAttribute(field, typeof(DescriptionAttribute)) is DescriptionAttribute descriptionAttribute) + { + if (descriptionAttribute.Description == localeType) + { + return (LocaleType) field.GetValue(null)!; + } + } + } + + throw new InvalidEnumArgumentException(); + } + /* + public static IQueryable WhereLocale( + this IQueryable query, Expression> expression, string searchTerm) where T : class + { + return query.Where(e => DbContext.LocaleSearch(e.Name.ToJson(), searchTerm)); + } + + public static IQueryable WhereLocale( + this IQueryable query, ICollection searchTerms) + { + return query.Where(e => EF.Functions.LocaleSearchArray(e.Name.ToJson(), searchTerms)); + }*/ +} diff --git a/Pf2eModel/Model/Common/I18n/LocaleItem.cs b/Pf2eModel/Model/Common/I18n/LocaleItem.cs new file mode 100644 index 0000000..1ba7ea1 --- /dev/null +++ b/Pf2eModel/Model/Common/I18n/LocaleItem.cs @@ -0,0 +1,10 @@ +namespace Pf2eModel.Model.Common.I18n; + +public class LocaleItem(LocaleType type, string value) +{ + public LocaleType Locale { get; set; } = type; + + public string Value { get; set; } = value; + + public LocaleItem(KeyValuePair item) : this(LocaleExtension.GetTypeByString(item.Key), item.Value) { } +} diff --git a/Pf2eModel/Model/Common/I18n/LocaleString.cs b/Pf2eModel/Model/Common/I18n/LocaleString.cs new file mode 100644 index 0000000..1e7a169 --- /dev/null +++ b/Pf2eModel/Model/Common/I18n/LocaleString.cs @@ -0,0 +1,148 @@ +using Newtonsoft.Json.Linq; +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; +using System.Runtime.Serialization.Json; +using System.Text; +using System.Threading.Tasks; + +namespace Pf2eModel.Model.Common.I18n; + +public class LocaleString : ICollection +{ + private readonly List _items = []; + private void AssureDefault() + { + if (_items.Count == 0) + { + _items.Add(new LocaleItem(LocaleType.English, string.Empty)); + } + } + + public int Count => _items.Count; + + public bool IsReadOnly => false; + + public ICollection Types => _items.Select(c => c.Locale).ToList(); + + public LocaleString() + { + AssureDefault(); + } + + private LocaleType _E(string localeString) => LocaleExtension.GetTypeByString(localeString); + + private LocaleType RealLocale(LocaleType locale) => Contains(locale) ? locale : LocaleType.English; + private LocaleType RealLocale(string localeString) => RealLocale(_E(localeString)); + + public LocaleItem GetItem(LocaleType locale) => this.First(c => c.Locale == RealLocale(locale)); + public LocaleItem GetItem(string locale) => this.First(c => c.Locale == RealLocale(locale)); + + public string this[LocaleType locale] + { + get => GetItem(locale).Value; + set + { + if (Types.Contains(locale)) + { + GetItem(locale).Value = value; + } + else + { + _items.Add(new LocaleItem(locale, value)); + } + } + } + public string this[string localeString] + { + get => this[_E(localeString)]; + set => this[_E(localeString)] = value; + } + + public void Add(LocaleItem item) + { + this[item.Locale] = item.Value; + } + public void Add(LocaleType locale, string value) + { + this[locale] = value; + } + + public void Clear() + { + _items.Clear(); + AssureDefault(); + } + + public bool Contains(LocaleItem item) + { + return _items.Contains(item); + } + public bool Contains(LocaleType locale) + { + return Types.Contains(locale); + } + public bool Contains(string value) + { + return this.Any(item => item.Value == value); + } + + public void CopyTo(LocaleItem[] array, int arrayIndex) + { + throw new NotImplementedException(); + } + + public bool Remove(LocaleItem item) + { + bool result = _items.Remove(item); + + if (Count == 0) + { + AssureDefault(); + return false; + } + + return result; + } + + public IEnumerator GetEnumerator() + { + return _items.GetEnumerator(); + } + IEnumerator IEnumerable.GetEnumerator() + { + return GetEnumerator(); + } + + public override string ToString() + { + return this[LocaleType.English]; + } + + public string Serialize() + { + JObject json = []; + + foreach (LocaleItem item in _items) + { + json.Add(item.Locale.GetTypeString(), item.Value); + } + + return json.ToString(); + } + public static LocaleString Deserialize(string input) + { + JObject json = JObject.Parse(input); + LocaleString result = new(); + + foreach (var item in json) + { + result[item.Key] = item.ToString(); + } + + return result; + } + +} diff --git a/Pf2eModel/Model/Common/I18n/LocaleType.cs b/Pf2eModel/Model/Common/I18n/LocaleType.cs new file mode 100644 index 0000000..5621b4b --- /dev/null +++ b/Pf2eModel/Model/Common/I18n/LocaleType.cs @@ -0,0 +1,24 @@ +using System.ComponentModel; + +namespace Pf2eModel.Model.Common.I18n; + +public enum LocaleType +{ + [Description("en")] + English, + + [Description("en.legacy")] + LegacyEnglish, + + [Description("ru")] + Russian, + + [Description("ru.legacy")] + Legacy, + + [Description("ru.hobby")] + Hobby, + + [Description("other")] + Other, +} diff --git a/Pf2eModel/Model/Enum/AliasType.cs b/Pf2eModel/Model/Enum/AliasType.cs deleted file mode 100644 index b947c77..0000000 --- a/Pf2eModel/Model/Enum/AliasType.cs +++ /dev/null @@ -1,21 +0,0 @@ -using NpgsqlTypes; - -namespace Pf2eModel.Model.Enum; - -public enum AliasType -{ - [PgName("legacy")] - Legacy, - - [PgName("english")] - English, - - [PgName("legacy_english")] - LegacyEnglish, - - [PgName("hobby")] - Hobby, - - [PgName("other")] - Other, -} diff --git a/Pf2eModel/Model/Structure/Ancestry.cs b/Pf2eModel/Model/Structure/Ancestry.cs index 76d9d06..310e079 100644 --- a/Pf2eModel/Model/Structure/Ancestry.cs +++ b/Pf2eModel/Model/Structure/Ancestry.cs @@ -16,7 +16,7 @@ public partial class Ancestry : IEntity public Entity Entity { get; set; } = null!; [Column(Order = 2)] - public Size[] Size { get; set; } + public Size[] Size { get; set; } = []; [Column(Order = 3)] public int Hp { get; set; } diff --git a/Pf2eModel/Pf2eDbContext.cs b/Pf2eModel/Pf2eDbContext.cs index 94e0bd5..fdf5ff4 100644 --- a/Pf2eModel/Pf2eDbContext.cs +++ b/Pf2eModel/Pf2eDbContext.cs @@ -6,6 +6,7 @@ using Pf2eModel.Model.Base.Keywords; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common; +using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure.AncestryRelation; @@ -72,8 +73,6 @@ public partial class Pf2eDbContext : DbContext public virtual DbSet Entities { get; set; } - public virtual DbSet EntityAliases { get; set; } - public virtual DbSet EntityExternals { get; set; } public virtual DbSet EntitySources { get; set; } @@ -125,7 +124,7 @@ public partial class Pf2eDbContext : DbContext builder .MapEnum() .MapEnum() - .MapEnum() + .MapEnum() .MapEnum() .MapEnum() .MapEnum() diff --git a/Pf2eModel/Pf2eDbContextModel.cs b/Pf2eModel/Pf2eDbContextModel.cs index af0329b..7ac5965 100644 --- a/Pf2eModel/Pf2eDbContextModel.cs +++ b/Pf2eModel/Pf2eDbContextModel.cs @@ -1,11 +1,13 @@ using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Query.SqlExpressions; using Pf2eModel.Model; using Pf2eModel.Model.Base; using Pf2eModel.Model.Base.Keywords; using Pf2eModel.Model.Base.Source; using Pf2eModel.Model.Base.Targets; using Pf2eModel.Model.Common; +using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Enum; using Pf2eModel.Model.Structure; using Pf2eModel.Model.Structure.AncestryRelation; @@ -15,6 +17,8 @@ using Pf2eModel.Model.Structure.ClassRelation; using Pf2eModel.Model.Structure.DeityRelation; using Pf2eModel.Model.Structure.FeatRelation; using Pf2eModel.Model.Structure.SpellRelation; +using System.Linq.Expressions; +using System.Reflection; namespace Pf2eModel; @@ -30,6 +34,32 @@ public partial class Pf2eDbContext DefineBase(modelBuilder); DefineCommon(modelBuilder); DefineStructure(modelBuilder); + DefineMethods(modelBuilder); + } + + private void BindEntity(EntityTypeBuilder entity) where T : class, IEntity + { + entity.HasOne(entity => entity.Entity) + .WithOne() + .HasForeignKey(entity => entity.Id) + .IsRequired(); + } + + private void BindLocaleString(EntityTypeBuilder entity, Expression> func) where T : class + { + /* + entity.OwnsMany(func, d => + { + d.ToJson(); + }); + */ + + entity.Property(func) + .HasConversion( + v => v.Serialize(), + v => LocaleString.Deserialize(v) + ) + .HasColumnType("jsonb"); } private void DefineEnums(ModelBuilder modelBuilder) @@ -37,7 +67,7 @@ public partial class Pf2eDbContext modelBuilder .HasPostgresEnum() .HasPostgresEnum() - .HasPostgresEnum() + .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() .HasPostgresEnum() @@ -62,15 +92,12 @@ public partial class Pf2eDbContext { modelBuilder.Entity(entity => { + BindLocaleString(entity, e => e.Name); + entity.HasMany(e => e.Traits).WithMany(t => t.Entities); entity.HasMany(e => e.Keywords).WithMany(t => t.Entities); - // EntityAlias - entity.HasMany(e => e.Aliases).WithOne(e => e.Entity) - .HasForeignKey(e => e.EntityId) - .OnDelete(DeleteBehavior.Cascade); - // EntitySource entity.HasMany(e => e.Sources).WithOne(e => e.Entity) .HasForeignKey(e => e.EntityId) @@ -81,7 +108,6 @@ public partial class Pf2eDbContext .HasForeignKey(e => e.EntityId) .OnDelete(DeleteBehavior.Cascade); }); - modelBuilder.Entity(entity => { }); // Source modelBuilder.Entity(entity => @@ -124,14 +150,6 @@ public partial class Pf2eDbContext modelBuilder.Entity(entity => { }); } - private void BindEntity(EntityTypeBuilder entity) where T : class, IEntity - { - entity.HasOne(entity => entity.Entity) - .WithOne() - .HasForeignKey(entity => entity.Id) - .IsRequired(); - } - private void DefineStructure(ModelBuilder modelBuilder) { // Action @@ -341,4 +359,30 @@ public partial class Pf2eDbContext BindEntity(entity); }); } + + private void DefineMethods(ModelBuilder modelBuilder) + { + MethodInfo localeSearch = typeof(Pf2eDbContext).GetMethod(nameof(LocaleSearch), BindingFlags.Static | BindingFlags.Public, [typeof(LocaleString), typeof(string)])!; + modelBuilder.HasDbFunction(localeSearch, builder => + { + builder.HasName("locale_search"); + builder.HasParameter("data").HasStoreType("jsonb"); + builder.HasParameter("search_value"); + }); + + MethodInfo localeSearchArray = typeof(Pf2eDbContext).GetMethod(nameof(LocaleSearchArray), BindingFlags.Static | BindingFlags.Public, [typeof(LocaleString), typeof(IEnumerable)])!; + modelBuilder.HasDbFunction(localeSearchArray, builder => + { + builder.HasName("locale_search_array"); + builder.HasParameter("data").HasStoreType("jsonb"); + builder.HasParameter("search_values"); + }); + } + + public static bool LocaleSearch(LocaleString data, string search_value) + => throw new InvalidOperationException("Only for LINQ translation"); + + public static bool LocaleSearchArray(LocaleString data, IEnumerable search_values) + => throw new InvalidOperationException("Only for LINQ translation"); + } diff --git a/Pf2eModel/Pf2eModel.csproj b/Pf2eModel/Pf2eModel.csproj index 034bf66..2411111 100644 --- a/Pf2eModel/Pf2eModel.csproj +++ b/Pf2eModel/Pf2eModel.csproj @@ -22,6 +22,7 @@ runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/Pf2eViewer/EntityConverter.cs b/Pf2eViewer/EntityConverter.cs index 397dc0f..3618477 100644 --- a/Pf2eViewer/EntityConverter.cs +++ b/Pf2eViewer/EntityConverter.cs @@ -5,6 +5,7 @@ using Pf2eModel; using Pf2eModel.Model; using Pf2eModel.Model.Base; using Pf2eModel.Model.Base.Source; +using Pf2eModel.Model.Common.I18n; using Pf2eModel.Model.Structure; using System.ComponentModel; @@ -70,11 +71,11 @@ internal class EntityConverter : JsonConverter private void ProcessTrait(Dictionary dictionary, Trait trait) { - string name = trait.Entity?.Name ?? _context.Entities.Where(e => e.Id == trait.Id).Select(e => e.Name).FirstOrDefault()!; + LocaleString name = trait.Entity?.Name ?? _context.Entities.Where(e => e.Id == trait.Id).Select(e => e.Name).FirstOrDefault()!; dictionary.Remove("Entity"); dictionary.Remove("Entities"); - dictionary.Add("Name", name); + dictionary.Add("Name", name.ToString()); } private void ProcessEntity(Dictionary dictionary, Entity entity) diff --git a/Pf2eViewer/MainViewForm.cs b/Pf2eViewer/MainViewForm.cs index ae10144..e500cb3 100644 --- a/Pf2eViewer/MainViewForm.cs +++ b/Pf2eViewer/MainViewForm.cs @@ -296,7 +296,7 @@ public partial class MainViewForm : Form private class EntityGridRow(Entity e) { public int Id { get; set; } = e.Id; - public string Name { get; set; } = e.Name; + public string Name { get; set; } = e.Name.ToString(); public EntityRarity Rarity { get; set; } = e.Rarity; private readonly Entity _e = e;